diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-25 16:18:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-25 16:18:27 -0700 |
commit | 2518f226c60d8e04d18ba4295500a5b0b8ac7659 (patch) | |
tree | e74de5ca0db01398cbb0c34376f74a81d7583c75 /drivers/gpu/drm/i915/display | |
parent | 86c87bea6b42100c67418af690919c44de6ede6e (diff) | |
parent | c4955d9cd2fc56c43e78c908dad4e2cac7cc9073 (diff) | |
download | linux-2518f226c60d8e04d18ba4295500a5b0b8ac7659.tar.bz2 |
Merge tag 'drm-next-2022-05-25' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"Intel have enabled DG2 on certain SKUs for laptops, AMD has started
some new GPU support, msm has user allocated VA controls
dma-buf:
- add dma_resv_replace_fences
- add dma_resv_get_singleton
- make dma_excl_fence private
core:
- EDID parser refactorings
- switch drivers to drm_mode_copy/duplicate
- DRM managed mutex initialization
display-helper:
- put HDMI, SCDC, HDCP, DSC and DP into new module
gem:
- rework fence handling
ttm:
- rework bulk move handling
- add common debugfs for resource managers
- convert to kvcalloc
format helpers:
- support monochrome formats
- RGB888, RGB565 to XRGB8888 conversions
fbdev:
- cfb/sys_imageblit fixes
- pagelist corruption fix
- create offb platform device
- deferred io improvements
sysfb:
- Kconfig rework
- support for VESA mode selection
bridge:
- conversions to devm_drm_of_get_bridge
- conversions to panel_bridge
- analogix_dp - autosuspend support
- it66121 - audio support
- tc358767 - DSI to DPI support
- icn6211 - PLL/I2C fixes, DT property
- adv7611 - enable DRM_BRIDGE_OP_HPD
- anx7625 - fill ELD if no monitor
- dw_hdmi - add audio support
- lontium LT9211 support, i.MXMP LDB
- it6505: Kconfig fix, DPCD set power fix
- adv7511 - CEC support for ADV7535
panel:
- ltk035c5444t, B133UAN01, NV3052C panel support
- DataImage FG040346DSSWBG04 support
- st7735r - DT bindings fix
- ssd130x - fixes
i915:
- DG2 laptop PCI-IDs ("motherboard down")
- Initial RPL-P PCI IDs
- compute engine ABI
- DG2 Tile4 support
- DG2 CCS clear color compression support
- DG2 render/media compression formats support
- ATS-M platform info
- RPL-S PCI IDs added
- Bump ADL-P DMC version to v2.16
- Support static DRRS
- Support multiple eDP/LVDS native mode refresh rates
- DP HDR support for HSW+
- Lots of display refactoring + fixes
- GuC hwconfig support and query
- sysfs support for multi-tile
- fdinfo per-client gpu utilisation
- add geometry subslices query
- fix prime mmap with LMEM
- fix vm open count and remove vma refcounts
- contiguous allocation fixes
- steered register write support
- small PCI BAR enablement
- GuC error capture support
- sunset igpu legacy mmap support for newer devices
- GuC version 70.1.1 support
amdgpu:
- Initial SoC21 support
- SMU 13.x enablement
- SMU 13.0.4 support
- ttm_eu cleanups
- USB-C, GPUVM updates
- TMZ fixes for RV
- RAS support for VCN
- PM sysfs code cleanup
- DC FP rework
- extend CG/PG flags to 64-bit
- SI dpm lockdep fix
- runtime PM fixes
amdkfd:
- RAS/SVM fixes
- TLB flush fixes
- CRIU GWS support
- ignore bogus MEC signals more efficiently
msm:
- Fourcc modifier for tiled but not compressed layouts
- Support for userspace allocated IOVA (GPU virtual address)
- DPU: DSC (Display Stream Compression) support
- DP: eDP support
- DP: conversion to use drm_bridge and drm_bridge_connector
- Merge DPU1 and MDP5 MDSS driver
- DPU: writeback support
nouveau:
- make some structures static
- make some variables static
- switch to drm_gem_plane_helper_prepare_fb
radeon:
- misc fixes/cleanups
mxsfb:
- rework crtc mode setting
- LCDIF CRC support
etnaviv:
- fencing improvements
- fix address space collisions
- cleanup MMU reference handling
gma500:
- GEM/GTT improvements
- connector handling fixes
komeda:
- switch to plane reset helper
mediatek:
- MIPI DSI improvements
omapdrm:
- GEM improvements
qxl:
- aarch64 support
vc4:
- add a CL submission tracepoint
- HDMI YUV support
- HDMI/clock improvements
- drop is_hdmi caching
virtio:
- remove restriction of non-zero blob types
vmwgfx:
- support for cursormob and cursorbypass 4
- fence improvements
tidss:
- reset DISPC on startup
solomon:
- SPI support
- DT improvements
sun4i:
- allwinner D1 support
- drop is_hdmi caching
imx:
- use swap() instead of open-coding
- use devm_platform_ioremap_resource
- remove redunant initializations
ast:
- Displayport support
rockchip:
- Refactor IOMMU initialisation
- make some structures static
- replace drm_detect_hdmi_monitor with drm_display_info.is_hdmi
- support swapped YUV formats,
- clock improvements
- rk3568 support
- VOP2 support
mediatek:
- MT8186 support
tegra:
- debugabillity improvements"
* tag 'drm-next-2022-05-25' of git://anongit.freedesktop.org/drm/drm: (1740 commits)
drm/i915/dsi: fix VBT send packet port selection for ICL+
drm/i915/uc: Fix undefined behavior due to shift overflowing the constant
drm/i915/reg: fix undefined behavior due to shift overflowing the constant
drm/i915/gt: Fix use of static in macro mismatch
drm/i915/audio: fix audio code enable/disable pipe logging
drm/i915: Fix CFI violation with show_dynamic_id()
drm/i915: Fix 'mixing different enum types' warnings in intel_display_power.c
drm/i915/gt: Fix build error without CONFIG_PM
drm/msm/dpu: handle pm_runtime_get_sync() errors in bind path
drm/msm/dpu: add DRM_MODE_ROTATE_180 back to supported rotations
drm/msm: don't free the IRQ if it was not requested
drm/msm/dpu: limit writeback modes according to max_linewidth
drm/amd: Don't reset dGPUs if the system is going to s2idle
drm/amdgpu: Unmap legacy queue when MES is enabled
drm: msm: fix possible memory leak in mdp5_crtc_cursor_set()
drm/msm: Fix fb plane offset calculation
drm/msm/a6xx: Fix refcount leak in a6xx_gpu_init
drm/msm/dsi: don't powerup at modeset time for parade-ps8640
drm/rockchip: Change register space names in vop2
dt-bindings: display: rockchip: make reg-names mandatory for VOP2
...
Diffstat (limited to 'drivers/gpu/drm/i915/display')
86 files changed, 8051 insertions, 7544 deletions
diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index f67bbaaad8e0..5a957acebfd6 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -5,12 +5,15 @@ * DisplayPort support for G4x,ILK,SNB,IVB,VLV,CHV (HSW+ handled by the DDI code). */ +#include <linux/string_helpers.h> + #include "g4x_dp.h" #include "intel_audio.h" #include "intel_backlight.h" #include "intel_connector.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display_power.h" #include "intel_display_types.h" #include "intel_dp.h" #include "intel_dp_link_training.h" @@ -22,58 +25,37 @@ #include "intel_pps.h" #include "vlv_sideband.h" -struct dp_link_dpll { - int clock; - struct dpll dpll; +static const struct dpll g4x_dpll[] = { + { .dot = 162000, .p1 = 2, .p2 = 10, .n = 2, .m1 = 23, .m2 = 8, }, + { .dot = 270000, .p1 = 1, .p2 = 10, .n = 1, .m1 = 14, .m2 = 2, }, }; -static const struct dp_link_dpll g4x_dpll[] = { - { 162000, - { .p1 = 2, .p2 = 10, .n = 2, .m1 = 23, .m2 = 8 } }, - { 270000, - { .p1 = 1, .p2 = 10, .n = 1, .m1 = 14, .m2 = 2 } } +static const struct dpll pch_dpll[] = { + { .dot = 162000, .p1 = 2, .p2 = 10, .n = 1, .m1 = 12, .m2 = 9, }, + { .dot = 270000, .p1 = 1, .p2 = 10, .n = 2, .m1 = 14, .m2 = 8, }, }; -static const struct dp_link_dpll pch_dpll[] = { - { 162000, - { .p1 = 2, .p2 = 10, .n = 1, .m1 = 12, .m2 = 9 } }, - { 270000, - { .p1 = 1, .p2 = 10, .n = 2, .m1 = 14, .m2 = 8 } } +static const struct dpll vlv_dpll[] = { + { .dot = 162000, .p1 = 3, .p2 = 2, .n = 5, .m1 = 3, .m2 = 81, }, + { .dot = 270000, .p1 = 2, .p2 = 2, .n = 1, .m1 = 2, .m2 = 27, }, }; -static const struct dp_link_dpll vlv_dpll[] = { - { 162000, - { .p1 = 3, .p2 = 2, .n = 5, .m1 = 3, .m2 = 81 } }, - { 270000, - { .p1 = 2, .p2 = 2, .n = 1, .m1 = 2, .m2 = 27 } } -}; - -/* - * CHV supports eDP 1.4 that have more link rates. - * Below only provides the fixed rate but exclude variable rate. - */ -static const struct dp_link_dpll chv_dpll[] = { - /* - * CHV requires to program fractional division for m2. - * m2 is stored in fixed point format using formula below - * (m2_int << 22) | m2_fraction - */ - { 162000, /* m2_int = 32, m2_fraction = 1677722 */ - { .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a } }, - { 270000, /* m2_int = 27, m2_fraction = 0 */ - { .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } }, +static const struct dpll chv_dpll[] = { + /* m2 is .22 binary fixed point */ + { .dot = 162000, .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a /* 32.4 */ }, + { .dot = 270000, .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 /* 27.0 */ }, }; const struct dpll *vlv_get_dpll(struct drm_i915_private *i915) { - return IS_CHERRYVIEW(i915) ? &chv_dpll[0].dpll : &vlv_dpll[0].dpll; + return IS_CHERRYVIEW(i915) ? &chv_dpll[0] : &vlv_dpll[0]; } void g4x_dp_set_clock(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - const struct dp_link_dpll *divisor = NULL; + const struct dpll *divisor = NULL; int i, count = 0; if (IS_G4X(dev_priv)) { @@ -92,8 +74,8 @@ void g4x_dp_set_clock(struct intel_encoder *encoder, if (divisor && count) { for (i = 0; i < count; i++) { - if (pipe_config->port_clock == divisor[i].clock) { - pipe_config->dpll = divisor[i].dpll; + if (pipe_config->port_clock == divisor[i].dot) { + pipe_config->dpll = divisor[i]; pipe_config->clock_set = true; break; } @@ -192,7 +174,7 @@ static void assert_dp_port(struct intel_dp *intel_dp, bool state) I915_STATE_WARN(cur_state != state, "[ENCODER:%d:%s] state assertion failure (expected %s, current %s)\n", dig_port->base.base.base.id, dig_port->base.base.name, - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } #define assert_dp_port_disabled(d) assert_dp_port((d), false) @@ -202,7 +184,7 @@ static void assert_edp_pll(struct drm_i915_private *dev_priv, bool state) I915_STATE_WARN(cur_state != state, "eDP PLL state assertion failure (expected %s, current %s)\n", - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } #define assert_edp_pll_enabled(d) assert_edp_pll((d), true) #define assert_edp_pll_disabled(d) assert_edp_pll((d), false) @@ -514,9 +496,7 @@ static void intel_disable_dp(struct intel_atomic_state *state, intel_dp->link_trained = false; - if (old_crtc_state->has_audio) - intel_audio_codec_disable(encoder, - old_crtc_state, old_conn_state); + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); /* * Make sure the panel is off before trying to change the mode. @@ -677,9 +657,7 @@ static void intel_enable_dp(struct intel_atomic_state *state, { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); u32 dp_reg = intel_de_read(dev_priv, intel_dp->output_reg); - enum pipe pipe = crtc->pipe; intel_wakeref_t wakeref; if (drm_WARN_ON(&dev_priv->drm, dp_reg & DP_PORT_EN)) @@ -713,11 +691,7 @@ static void intel_enable_dp(struct intel_atomic_state *state, intel_dp_start_link_train(intel_dp, pipe_config); intel_dp_stop_link_train(intel_dp, pipe_config); - if (pipe_config->has_audio) { - drm_dbg(&dev_priv->drm, "Enabling DP audio on pipe %c\n", - pipe_name(pipe)); - intel_audio_codec_enable(encoder, pipe_config, conn_state); - } + intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void g4x_enable_dp(struct intel_atomic_state *state, @@ -1402,7 +1376,7 @@ bool g4x_dp_init(struct drm_i915_private *dev_priv, dig_port->max_lanes = 4; intel_encoder->type = INTEL_OUTPUT_DP; - intel_encoder->power_domain = intel_port_to_power_domain(port); + intel_encoder->power_domain = intel_display_power_ddi_lanes_domain(dev_priv, port); if (IS_CHERRYVIEW(dev_priv)) { if (port == PORT_D) intel_encoder->pipe_mask = BIT(PIPE_C); diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index 06e00b1eaa7c..5fbd2ae95869 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -10,6 +10,7 @@ #include "intel_connector.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display_power.h" #include "intel_display_types.h" #include "intel_dpio_phy.h" #include "intel_fifo_underrun.h" @@ -143,19 +144,6 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder, &pipe_config->infoframes.hdmi); } -static void intel_enable_hdmi_audio(struct intel_encoder *encoder, - const struct intel_crtc_state *pipe_config, - const struct drm_connector_state *conn_state) -{ - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); - - drm_WARN_ON(&i915->drm, !pipe_config->has_hdmi_sink); - drm_dbg_kms(&i915->drm, "Enabling HDMI audio on pipe %c\n", - pipe_name(crtc->pipe)); - intel_audio_codec_enable(encoder, pipe_config, conn_state); -} - static void g4x_enable_hdmi(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *pipe_config, @@ -175,8 +163,9 @@ static void g4x_enable_hdmi(struct intel_atomic_state *state, intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); - if (pipe_config->has_audio) - intel_enable_hdmi_audio(encoder, pipe_config, conn_state); + drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && + !pipe_config->has_hdmi_sink); + intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void ibx_enable_hdmi(struct intel_atomic_state *state, @@ -227,8 +216,9 @@ static void ibx_enable_hdmi(struct intel_atomic_state *state, intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); } - if (pipe_config->has_audio) - intel_enable_hdmi_audio(encoder, pipe_config, conn_state); + drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && + !pipe_config->has_hdmi_sink); + intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void cpt_enable_hdmi(struct intel_atomic_state *state, @@ -281,8 +271,9 @@ static void cpt_enable_hdmi(struct intel_atomic_state *state, intel_de_read(dev_priv, TRANS_CHICKEN1(pipe)) & ~TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE); } - if (pipe_config->has_audio) - intel_enable_hdmi_audio(encoder, pipe_config, conn_state); + drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && + !pipe_config->has_hdmi_sink); + intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void vlv_enable_hdmi(struct intel_atomic_state *state, @@ -356,9 +347,7 @@ static void g4x_disable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - if (old_crtc_state->has_audio) - intel_audio_codec_disable(encoder, - old_crtc_state, old_conn_state); + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); intel_disable_hdmi(state, encoder, old_crtc_state, old_conn_state); } @@ -368,9 +357,7 @@ static void pch_disable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - if (old_crtc_state->has_audio) - intel_audio_codec_disable(encoder, - old_crtc_state, old_conn_state); + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); } static void pch_post_disable_hdmi(struct intel_atomic_state *state, @@ -588,7 +575,7 @@ void g4x_hdmi_init(struct drm_i915_private *dev_priv, intel_encoder->shutdown = intel_hdmi_encoder_shutdown; intel_encoder->type = INTEL_OUTPUT_HDMI; - intel_encoder->power_domain = intel_port_to_power_domain(port); + intel_encoder->power_domain = intel_display_power_ddi_lanes_domain(dev_priv, port); intel_encoder->port = port; if (IS_CHERRYVIEW(dev_priv)) { if (port == PORT_D) diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.c b/drivers/gpu/drm/i915/display/i9xx_plane.c index a87b65cd41fd..7fe1a4e57654 100644 --- a/drivers/gpu/drm/i915/display/i9xx_plane.c +++ b/drivers/gpu/drm/i915/display/i9xx_plane.c @@ -418,9 +418,6 @@ static void i9xx_plane_update_noarm(struct intel_plane *plane, { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, DSPSTRIDE(i9xx_plane), plane_state->view.color_plane[0].mapping_stride); @@ -441,8 +438,6 @@ static void i9xx_plane_update_noarm(struct intel_plane *plane, intel_de_write_fw(dev_priv, DSPSIZE(i9xx_plane), DISP_HEIGHT(crtc_h - 1) | DISP_WIDTH(crtc_w - 1)); } - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void i9xx_plane_update_arm(struct intel_plane *plane, @@ -454,7 +449,6 @@ static void i9xx_plane_update_arm(struct intel_plane *plane, int x = plane_state->view.color_plane[0].x; int y = plane_state->view.color_plane[0].y; u32 dspcntr, dspaddr_offset, linear_offset; - unsigned long irqflags; dspcntr = plane_state->ctl | i9xx_plane_ctl_crtc(crtc_state); @@ -465,8 +459,6 @@ static void i9xx_plane_update_arm(struct intel_plane *plane, else dspaddr_offset = linear_offset; - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - if (IS_CHERRYVIEW(dev_priv) && i9xx_plane == PLANE_B) { int crtc_x = plane_state->uapi.dst.x1; int crtc_y = plane_state->uapi.dst.y1; @@ -496,14 +488,13 @@ static void i9xx_plane_update_arm(struct intel_plane *plane, * the control register just before the surface register. */ intel_de_write_fw(dev_priv, DSPCNTR(i9xx_plane), dspcntr); + if (DISPLAY_VER(dev_priv) >= 4) intel_de_write_fw(dev_priv, DSPSURF(i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); else intel_de_write_fw(dev_priv, DSPADDR(i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void i830_plane_update_arm(struct intel_plane *plane, @@ -525,7 +516,6 @@ static void i9xx_plane_disable_arm(struct intel_plane *plane, { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; - unsigned long irqflags; u32 dspcntr; /* @@ -540,15 +530,12 @@ static void i9xx_plane_disable_arm(struct intel_plane *plane, */ dspcntr = i9xx_plane_ctl_crtc(crtc_state); - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - intel_de_write_fw(dev_priv, DSPCNTR(i9xx_plane), dspcntr); + if (DISPLAY_VER(dev_priv) >= 4) intel_de_write_fw(dev_priv, DSPSURF(i9xx_plane), 0); else intel_de_write_fw(dev_priv, DSPADDR(i9xx_plane), 0); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -561,16 +548,14 @@ g4x_primary_async_flip(struct intel_plane *plane, u32 dspcntr = plane_state->ctl | i9xx_plane_ctl_crtc(crtc_state); u32 dspaddr_offset = plane_state->view.color_plane[0].offset; enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; - unsigned long irqflags; if (async_flip) dspcntr |= DISP_ASYNC_FLIP; - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, DSPCNTR(i9xx_plane), dspcntr); + intel_de_write_fw(dev_priv, DSPSURF(i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -582,12 +567,9 @@ vlv_primary_async_flip(struct intel_plane *plane, struct drm_i915_private *dev_priv = to_i915(plane->base.dev); u32 dspaddr_offset = plane_state->view.color_plane[0].offset; enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; - unsigned long irqflags; - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, DSPADDR_VLV(i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index 13b07c6fd6be..19bf717fd4cb 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -25,6 +25,7 @@ * Jani Nikula <jani.nikula@intel.com> */ +#include <drm/display/drm_dsc_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_mipi_dsi.h> @@ -399,8 +400,8 @@ static void get_dsi_io_power_domains(struct drm_i915_private *dev_priv, intel_dsi->io_wakeref[port] = intel_display_power_get(dev_priv, port == PORT_A ? - POWER_DOMAIN_PORT_DDI_A_IO : - POWER_DOMAIN_PORT_DDI_B_IO); + POWER_DOMAIN_PORT_DDI_IO_A : + POWER_DOMAIN_PORT_DDI_IO_B); } } @@ -1425,8 +1426,8 @@ static void gen11_dsi_disable_io_power(struct intel_encoder *encoder) wakeref = fetch_and_zero(&intel_dsi->io_wakeref[port]); intel_display_power_put(dev_priv, port == PORT_A ? - POWER_DOMAIN_PORT_DDI_A_IO : - POWER_DOMAIN_PORT_DDI_B_IO, + POWER_DOMAIN_PORT_DDI_IO_A : + POWER_DOMAIN_PORT_DDI_IO_B, wakeref); } @@ -1967,6 +1968,8 @@ static void icl_dphy_param_init(struct intel_dsi *intel_dsi) static void icl_dsi_add_properties(struct intel_connector *connector) { + const struct drm_display_mode *fixed_mode = + intel_panel_preferred_fixed_mode(connector); u32 allowed_scalers; allowed_scalers = BIT(DRM_MODE_SCALE_ASPECT) | @@ -1979,9 +1982,9 @@ static void icl_dsi_add_properties(struct intel_connector *connector) connector->base.state->scaling_mode = DRM_MODE_SCALE_ASPECT; drm_connector_set_panel_orientation_with_quirk(&connector->base, - intel_dsi_get_panel_orientation(connector), - connector->panel.fixed_mode->hdisplay, - connector->panel.fixed_mode->vdisplay); + intel_dsi_get_panel_orientation(connector), + fixed_mode->hdisplay, + fixed_mode->vdisplay); } void icl_dsi_init(struct drm_i915_private *dev_priv) @@ -1991,7 +1994,6 @@ void icl_dsi_init(struct drm_i915_private *dev_priv) struct intel_encoder *encoder; struct intel_connector *intel_connector; struct drm_connector *connector; - struct drm_display_mode *fixed_mode; enum port port; if (!intel_bios_is_dsi_present(dev_priv, &port)) @@ -2048,15 +2050,16 @@ void icl_dsi_init(struct drm_i915_private *dev_priv) intel_connector_attach_encoder(intel_connector, encoder); mutex_lock(&dev->mode_config.mutex); - fixed_mode = intel_panel_vbt_fixed_mode(intel_connector); + intel_panel_add_vbt_lfp_fixed_mode(intel_connector); mutex_unlock(&dev->mode_config.mutex); - if (!fixed_mode) { + if (!intel_panel_preferred_fixed_mode(intel_connector)) { drm_err(&dev_priv->drm, "DSI fixed mode info missing\n"); goto err; } - intel_panel_init(&intel_connector->panel, fixed_mode, NULL); + intel_panel_init(intel_connector); + intel_backlight_setup(intel_connector, INVALID_PIPE); if (dev_priv->vbt.dsi.config->dual_link) diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c index 5712688232fb..efe8591619e3 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c @@ -181,29 +181,67 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, } unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state, - const struct intel_plane_state *plane_state) + const struct intel_plane_state *plane_state, + int color_plane) { const struct drm_framebuffer *fb = plane_state->hw.fb; - unsigned int cpp; - unsigned int pixel_rate; if (!plane_state->uapi.visible) return 0; - pixel_rate = intel_plane_pixel_rate(crtc_state, plane_state); + return intel_plane_pixel_rate(crtc_state, plane_state) * + fb->format->cpp[color_plane]; +} + +static bool +use_min_ddb(const struct intel_crtc_state *crtc_state, + struct intel_plane *plane) +{ + struct drm_i915_private *i915 = to_i915(plane->base.dev); + + return DISPLAY_VER(i915) >= 13 && + crtc_state->uapi.async_flip && + plane->async_flip; +} + +static unsigned int +intel_plane_relative_data_rate(const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state, + int color_plane) +{ + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + const struct drm_framebuffer *fb = plane_state->hw.fb; + int width, height; - cpp = fb->format->cpp[0]; + if (plane->id == PLANE_CURSOR) + return 0; + + if (!plane_state->uapi.visible) + return 0; /* - * Based on HSD#:1408715493 - * NV12 cpp == 4, P010 cpp == 8 - * - * FIXME what is the logic behind this? + * We calculate extra ddb based on ratio plane rate/total data rate + * in case, in some cases we should not allocate extra ddb for the plane, + * so do not count its data rate, if this is the case. */ - if (fb->format->is_yuv && fb->format->num_planes > 1) - cpp *= 4; + if (use_min_ddb(crtc_state, plane)) + return 0; + + /* + * Src coordinates are already rotated by 270 degrees for + * the 90/270 degree plane rotation cases (to match the + * GTT mapping), hence no need to account for rotation here. + */ + width = drm_rect_width(&plane_state->uapi.src) >> 16; + height = drm_rect_height(&plane_state->uapi.src) >> 16; + + /* UV plane does 1/2 pixel sub-sampling */ + if (color_plane == 1) { + width /= 2; + height /= 2; + } - return pixel_rate * cpp; + return width * height * fb->format->cpp[color_plane]; } int intel_plane_calc_min_cdclk(struct intel_atomic_state *state, @@ -326,6 +364,9 @@ void intel_plane_set_invisible(struct intel_crtc_state *crtc_state, crtc_state->nv12_planes &= ~BIT(plane->id); crtc_state->c8_planes &= ~BIT(plane->id); crtc_state->data_rate[plane->id] = 0; + crtc_state->data_rate_y[plane->id] = 0; + crtc_state->rel_data_rate[plane->id] = 0; + crtc_state->rel_data_rate_y[plane->id] = 0; crtc_state->min_cdclk[plane->id] = 0; plane_state->uapi.visible = false; @@ -551,8 +592,27 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_ if (new_plane_state->uapi.visible || old_plane_state->uapi.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); + if (new_plane_state->uapi.visible && + intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) { + new_crtc_state->data_rate_y[plane->id] = + intel_plane_data_rate(new_crtc_state, new_plane_state, 0); + new_crtc_state->data_rate[plane->id] = + intel_plane_data_rate(new_crtc_state, new_plane_state, 1); + + new_crtc_state->rel_data_rate_y[plane->id] = + intel_plane_relative_data_rate(new_crtc_state, + new_plane_state, 0); + new_crtc_state->rel_data_rate[plane->id] = + intel_plane_relative_data_rate(new_crtc_state, + new_plane_state, 1); + } else if (new_plane_state->uapi.visible) { + new_crtc_state->data_rate[plane->id] = + intel_plane_data_rate(new_crtc_state, new_plane_state, 0); + + new_crtc_state->rel_data_rate[plane->id] = + intel_plane_relative_data_rate(new_crtc_state, + new_plane_state, 0); + } return intel_plane_atomic_calc_changes(old_crtc_state, new_crtc_state, old_plane_state, new_plane_state); @@ -616,8 +676,8 @@ int intel_plane_atomic_check(struct intel_atomic_state *state, static struct intel_plane * skl_next_plane_to_commit(struct intel_atomic_state *state, struct intel_crtc *crtc, - struct skl_ddb_entry entries_y[I915_MAX_PLANES], - struct skl_ddb_entry entries_uv[I915_MAX_PLANES], + struct skl_ddb_entry ddb[I915_MAX_PLANES], + struct skl_ddb_entry ddb_y[I915_MAX_PLANES], unsigned int *update_mask) { struct intel_crtc_state *crtc_state = @@ -636,17 +696,15 @@ skl_next_plane_to_commit(struct intel_atomic_state *state, !(*update_mask & BIT(plane_id))) continue; - if (skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb_y[plane_id], - entries_y, - I915_MAX_PLANES, plane_id) || - skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb_uv[plane_id], - entries_uv, - I915_MAX_PLANES, plane_id)) + if (skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb[plane_id], + ddb, I915_MAX_PLANES, plane_id) || + skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb_y[plane_id], + ddb_y, I915_MAX_PLANES, plane_id)) continue; *update_mask &= ~BIT(plane_id); - entries_y[plane_id] = crtc_state->wm.skl.plane_ddb_y[plane_id]; - entries_uv[plane_id] = crtc_state->wm.skl.plane_ddb_uv[plane_id]; + ddb[plane_id] = crtc_state->wm.skl.plane_ddb[plane_id]; + ddb_y[plane_id] = crtc_state->wm.skl.plane_ddb_y[plane_id]; return plane; } @@ -728,19 +786,17 @@ static void skl_crtc_planes_update_arm(struct intel_atomic_state *state, intel_atomic_get_old_crtc_state(state, crtc); struct intel_crtc_state *new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc); - struct skl_ddb_entry entries_y[I915_MAX_PLANES]; - struct skl_ddb_entry entries_uv[I915_MAX_PLANES]; + struct skl_ddb_entry ddb[I915_MAX_PLANES]; + struct skl_ddb_entry ddb_y[I915_MAX_PLANES]; u32 update_mask = new_crtc_state->update_planes; struct intel_plane *plane; - memcpy(entries_y, old_crtc_state->wm.skl.plane_ddb_y, + memcpy(ddb, old_crtc_state->wm.skl.plane_ddb, + sizeof(old_crtc_state->wm.skl.plane_ddb)); + memcpy(ddb_y, old_crtc_state->wm.skl.plane_ddb_y, sizeof(old_crtc_state->wm.skl.plane_ddb_y)); - memcpy(entries_uv, old_crtc_state->wm.skl.plane_ddb_uv, - sizeof(old_crtc_state->wm.skl.plane_ddb_uv)); - while ((plane = skl_next_plane_to_commit(state, crtc, - entries_y, entries_uv, - &update_mask))) { + while ((plane = skl_next_plane_to_commit(state, crtc, ddb, ddb_y, &update_mask))) { struct intel_plane_state *new_plane_state = intel_atomic_get_new_plane_state(state, plane); @@ -802,8 +858,8 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state, struct drm_framebuffer *fb = plane_state->hw.fb; struct drm_rect *src = &plane_state->uapi.src; struct drm_rect *dst = &plane_state->uapi.dst; + const struct drm_rect *clip = &crtc_state->pipe_src; unsigned int rotation = plane_state->hw.rotation; - struct drm_rect clip = {}; int hscale, vscale; if (!fb) { @@ -823,31 +879,25 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state, return -ERANGE; } - if (crtc_state->hw.enable) { - clip.x2 = crtc_state->pipe_src_w; - clip.y2 = crtc_state->pipe_src_h; - } - - /* right side of the image is on the slave crtc, adjust dst to match */ - if (intel_crtc_is_bigjoiner_slave(crtc_state)) - drm_rect_translate(dst, -crtc_state->pipe_src_w, 0); - /* * FIXME: This might need further adjustment for seamless scaling * with phase information, for the 2p2 and 2p1 scenarios. */ - plane_state->uapi.visible = drm_rect_clip_scaled(src, dst, &clip); + plane_state->uapi.visible = drm_rect_clip_scaled(src, dst, clip); drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); if (!can_position && plane_state->uapi.visible && - !drm_rect_equals(dst, &clip)) { + !drm_rect_equals(dst, clip)) { drm_dbg_kms(&i915->drm, "Plane must cover entire CRTC\n"); drm_rect_debug_print("dst: ", dst, false); - drm_rect_debug_print("clip: ", &clip, false); + drm_rect_debug_print("clip: ", clip, false); return -EINVAL; } + /* final plane coordinates will be relative to the plane's pipe */ + drm_rect_translate(dst, -clip->x1, -clip->y1); + return 0; } @@ -997,7 +1047,8 @@ intel_prepare_plane_fb(struct drm_plane *_plane, if (ret < 0) goto unpin_fb; - dma_resv_iter_begin(&cursor, obj->base.resv, false); + dma_resv_iter_begin(&cursor, obj->base.resv, + DMA_RESV_USAGE_WRITE); dma_resv_for_each_fence_unlocked(&cursor, fence) { add_rps_boost_after_vblank(new_plane_state->hw.crtc, fence); diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.h b/drivers/gpu/drm/i915/display/intel_atomic_plane.h index f4763a53541e..74b6d3b169a7 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.h +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.h @@ -25,7 +25,8 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_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 intel_plane_state *plane_state, + int color_plane); void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc); diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index 3bdca0fe2cee..f0f0dfce27ce 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -337,8 +337,6 @@ static void g4x_audio_codec_disable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); u32 eldv, tmp; - drm_dbg_kms(&dev_priv->drm, "Disable audio codec\n"); - tmp = intel_de_read(dev_priv, G4X_AUD_VID_DID); if (tmp == INTEL_AUDIO_DEVBLC || tmp == INTEL_AUDIO_DEVCL) eldv = G4X_ELDV_DEVCL_DEVBLC; @@ -362,9 +360,6 @@ static void g4x_audio_codec_enable(struct intel_encoder *encoder, u32 tmp; int len, i; - drm_dbg_kms(&dev_priv->drm, "Enable audio codec, %u bytes ELD\n", - drm_eld_size(eld)); - tmp = intel_de_read(dev_priv, G4X_AUD_VID_DID); if (tmp == INTEL_AUDIO_DEVBLC || tmp == INTEL_AUDIO_DEVCL) eldv = G4X_ELDV_DEVCL_DEVBLC; @@ -383,7 +378,6 @@ static void g4x_audio_codec_enable(struct intel_encoder *encoder, intel_de_write(dev_priv, G4X_AUD_CNTL_ST, tmp); len = min(drm_eld_size(eld) / 4, len); - drm_dbg(&dev_priv->drm, "ELD size %d\n", len); for (i = 0; i < len; i++) intel_de_write(dev_priv, G4X_HDMIW_HDMIEDID, *((const u32 *)eld + i)); @@ -501,9 +495,6 @@ static void hsw_audio_codec_disable(struct intel_encoder *encoder, enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder; u32 tmp; - drm_dbg_kms(&dev_priv->drm, "Disable audio codec on transcoder %s\n", - transcoder_name(cpu_transcoder)); - mutex_lock(&dev_priv->audio.mutex); /* Disable timestamps */ @@ -647,10 +638,6 @@ static void hsw_audio_codec_enable(struct intel_encoder *encoder, u32 tmp; int len, i; - drm_dbg_kms(&dev_priv->drm, - "Enable audio codec on transcoder %s, %u bytes ELD\n", - transcoder_name(cpu_transcoder), drm_eld_size(eld)); - mutex_lock(&dev_priv->audio.mutex); /* Enable Audio WA for 4k DSC usecases */ @@ -703,11 +690,6 @@ static void ilk_audio_codec_disable(struct intel_encoder *encoder, u32 tmp, eldv; i915_reg_t aud_config, aud_cntrl_st2; - drm_dbg_kms(&dev_priv->drm, - "Disable audio codec on [ENCODER:%d:%s], pipe %c\n", - encoder->base.base.id, encoder->base.name, - pipe_name(pipe)); - if (drm_WARN_ON(&dev_priv->drm, port == PORT_A)) return; @@ -754,11 +736,6 @@ static void ilk_audio_codec_enable(struct intel_encoder *encoder, int len, i; i915_reg_t hdmiw_hdmiedid, aud_config, aud_cntl_st, aud_cntrl_st2; - drm_dbg_kms(&dev_priv->drm, - "Enable audio codec on [ENCODER:%d:%s], pipe %c, %u bytes ELD\n", - encoder->base.base.id, encoder->base.name, - pipe_name(pipe), drm_eld_size(eld)); - if (drm_WARN_ON(&dev_priv->drm, port == PORT_A)) return; @@ -844,18 +821,20 @@ void intel_audio_codec_enable(struct intel_encoder *encoder, enum port port = encoder->port; enum pipe pipe = crtc->pipe; + if (!crtc_state->has_audio) + return; + + drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s][ENCODER:%d:%s] Enable audio codec on pipe %c, %u bytes ELD\n", + connector->base.id, connector->name, + encoder->base.base.id, encoder->base.name, + pipe_name(pipe), drm_eld_size(connector->eld)); + /* FIXME precompute the ELD in .compute_config() */ if (!connector->eld[0]) drm_dbg_kms(&dev_priv->drm, "Bogus ELD on [CONNECTOR:%d:%s]\n", connector->base.id, connector->name); - drm_dbg(&dev_priv->drm, "ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", - connector->base.id, - connector->name, - encoder->base.base.id, - encoder->base.name); - connector->eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2; if (dev_priv->audio.funcs) @@ -900,9 +879,17 @@ void intel_audio_codec_disable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct i915_audio_component *acomp = dev_priv->audio.component; struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc); + struct drm_connector *connector = old_conn_state->connector; enum port port = encoder->port; enum pipe pipe = crtc->pipe; + if (!old_crtc_state->has_audio) + return; + + drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s][ENCODER:%d:%s] Disable audio codec on pipe %c\n", + connector->base.id, connector->name, + encoder->base.base.id, encoder->base.name, pipe_name(pipe)); + if (dev_priv->audio.funcs) dev_priv->audio.funcs->audio_codec_disable(encoder, old_crtc_state, diff --git a/drivers/gpu/drm/i915/display/intel_backlight.c b/drivers/gpu/drm/i915/display/intel_backlight.c index 98f7ea44042f..c8e1fc53a881 100644 --- a/drivers/gpu/drm/i915/display/intel_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_backlight.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/pwm.h> +#include <linux/string_helpers.h> #include "intel_backlight.h" #include "intel_connector.h" @@ -1633,7 +1634,7 @@ int intel_backlight_setup(struct intel_connector *connector, enum pipe pipe) drm_dbg_kms(&dev_priv->drm, "Connector %s backlight initialized, %s, brightness %u/%u\n", connector->base.name, - enableddisabled(panel->backlight.enabled), + str_enabled_disabled(panel->backlight.enabled), panel->backlight.level, panel->backlight.max); return 0; diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 40b5e7ed12c2..0c5638f5b72b 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -25,7 +25,8 @@ * */ -#include <drm/dp/drm_dp_helper.h> +#include <drm/display/drm_dp_helper.h> +#include <drm/display/drm_dsc_helper.h> #include "display/intel_display.h" #include "display/intel_display_types.h" @@ -88,7 +89,7 @@ static u32 get_blocksize(const void *block_data) } static const void * -find_section(const void *_bdb, enum bdb_block_id section_id) +find_raw_section(const void *_bdb, enum bdb_block_id section_id) { const struct bdb_header *bdb = _bdb; const u8 *base = _bdb; @@ -118,6 +119,406 @@ find_section(const void *_bdb, enum bdb_block_id section_id) return NULL; } +/* + * Offset from the start of BDB to the start of the + * block data (just past the block header). + */ +static u32 block_offset(const void *bdb, enum bdb_block_id section_id) +{ + const void *block; + + block = find_raw_section(bdb, section_id); + if (!block) + return 0; + + return block - bdb; +} + +/* size of the block excluding the header */ +static u32 block_size(const void *bdb, enum bdb_block_id section_id) +{ + const void *block; + + block = find_raw_section(bdb, section_id); + if (!block) + return 0; + + return get_blocksize(block); +} + +struct bdb_block_entry { + struct list_head node; + enum bdb_block_id section_id; + u8 data[]; +}; + +static const void * +find_section(struct drm_i915_private *i915, + enum bdb_block_id section_id) +{ + struct bdb_block_entry *entry; + + list_for_each_entry(entry, &i915->vbt.bdb_blocks, node) { + if (entry->section_id == section_id) + return entry->data + 3; + } + + return NULL; +} + +static const struct { + enum bdb_block_id section_id; + size_t min_size; +} bdb_blocks[] = { + { .section_id = BDB_GENERAL_FEATURES, + .min_size = sizeof(struct bdb_general_features), }, + { .section_id = BDB_GENERAL_DEFINITIONS, + .min_size = sizeof(struct bdb_general_definitions), }, + { .section_id = BDB_PSR, + .min_size = sizeof(struct bdb_psr), }, + { .section_id = BDB_DRIVER_FEATURES, + .min_size = sizeof(struct bdb_driver_features), }, + { .section_id = BDB_SDVO_LVDS_OPTIONS, + .min_size = sizeof(struct bdb_sdvo_lvds_options), }, + { .section_id = BDB_SDVO_PANEL_DTDS, + .min_size = sizeof(struct bdb_sdvo_panel_dtds), }, + { .section_id = BDB_EDP, + .min_size = sizeof(struct bdb_edp), }, + { .section_id = BDB_LVDS_OPTIONS, + .min_size = sizeof(struct bdb_lvds_options), }, + /* + * BDB_LVDS_LFP_DATA depends on BDB_LVDS_LFP_DATA_PTRS, + * so keep the two ordered. + */ + { .section_id = BDB_LVDS_LFP_DATA_PTRS, + .min_size = sizeof(struct bdb_lvds_lfp_data_ptrs), }, + { .section_id = BDB_LVDS_LFP_DATA, + .min_size = 0, /* special case */ }, + { .section_id = BDB_LVDS_BACKLIGHT, + .min_size = sizeof(struct bdb_lfp_backlight_data), }, + { .section_id = BDB_LFP_POWER, + .min_size = sizeof(struct bdb_lfp_power), }, + { .section_id = BDB_MIPI_CONFIG, + .min_size = sizeof(struct bdb_mipi_config), }, + { .section_id = BDB_MIPI_SEQUENCE, + .min_size = sizeof(struct bdb_mipi_sequence) }, + { .section_id = BDB_COMPRESSION_PARAMETERS, + .min_size = sizeof(struct bdb_compression_parameters), }, + { .section_id = BDB_GENERIC_DTD, + .min_size = sizeof(struct bdb_generic_dtd), }, +}; + +static size_t lfp_data_min_size(struct drm_i915_private *i915) +{ + const struct bdb_lvds_lfp_data_ptrs *ptrs; + size_t size; + + ptrs = find_section(i915, BDB_LVDS_LFP_DATA_PTRS); + if (!ptrs) + return 0; + + size = sizeof(struct bdb_lvds_lfp_data); + if (ptrs->panel_name.table_size) + size = max(size, ptrs->panel_name.offset + + sizeof(struct bdb_lvds_lfp_data_tail)); + + return size; +} + +static bool validate_lfp_data_ptrs(const void *bdb, + const struct bdb_lvds_lfp_data_ptrs *ptrs) +{ + int fp_timing_size, dvo_timing_size, panel_pnp_id_size, panel_name_size; + int data_block_size, lfp_data_size; + int i; + + data_block_size = block_size(bdb, BDB_LVDS_LFP_DATA); + if (data_block_size == 0) + return false; + + /* always 3 indicating the presence of fp_timing+dvo_timing+panel_pnp_id */ + if (ptrs->lvds_entries != 3) + return false; + + fp_timing_size = ptrs->ptr[0].fp_timing.table_size; + dvo_timing_size = ptrs->ptr[0].dvo_timing.table_size; + panel_pnp_id_size = ptrs->ptr[0].panel_pnp_id.table_size; + panel_name_size = ptrs->panel_name.table_size; + + /* fp_timing has variable size */ + if (fp_timing_size < 32 || + dvo_timing_size != sizeof(struct lvds_dvo_timing) || + panel_pnp_id_size != sizeof(struct lvds_pnp_id)) + return false; + + /* panel_name is not present in old VBTs */ + if (panel_name_size != 0 && + panel_name_size != sizeof(struct lvds_lfp_panel_name)) + return false; + + lfp_data_size = ptrs->ptr[1].fp_timing.offset - ptrs->ptr[0].fp_timing.offset; + if (16 * lfp_data_size > data_block_size) + return false; + + /* + * Except for vlv/chv machines all real VBTs seem to have 6 + * unaccounted bytes in the fp_timing table. And it doesn't + * appear to be a really intentional hole as the fp_timing + * 0xffff terminator is always within those 6 missing bytes. + */ + if (fp_timing_size + dvo_timing_size + panel_pnp_id_size != lfp_data_size && + fp_timing_size + 6 + dvo_timing_size + panel_pnp_id_size != lfp_data_size) + return false; + + if (ptrs->ptr[0].fp_timing.offset + fp_timing_size > ptrs->ptr[0].dvo_timing.offset || + ptrs->ptr[0].dvo_timing.offset + dvo_timing_size != ptrs->ptr[0].panel_pnp_id.offset || + ptrs->ptr[0].panel_pnp_id.offset + panel_pnp_id_size != lfp_data_size) + return false; + + /* make sure the table entries have uniform size */ + for (i = 1; i < 16; i++) { + if (ptrs->ptr[i].fp_timing.table_size != fp_timing_size || + ptrs->ptr[i].dvo_timing.table_size != dvo_timing_size || + ptrs->ptr[i].panel_pnp_id.table_size != panel_pnp_id_size) + return false; + + if (ptrs->ptr[i].fp_timing.offset - ptrs->ptr[i-1].fp_timing.offset != lfp_data_size || + ptrs->ptr[i].dvo_timing.offset - ptrs->ptr[i-1].dvo_timing.offset != lfp_data_size || + ptrs->ptr[i].panel_pnp_id.offset - ptrs->ptr[i-1].panel_pnp_id.offset != lfp_data_size) + return false; + } + + /* make sure the tables fit inside the data block */ + for (i = 0; i < 16; i++) { + if (ptrs->ptr[i].fp_timing.offset + fp_timing_size > data_block_size || + ptrs->ptr[i].dvo_timing.offset + dvo_timing_size > data_block_size || + ptrs->ptr[i].panel_pnp_id.offset + panel_pnp_id_size > data_block_size) + return false; + } + + if (ptrs->panel_name.offset + 16 * panel_name_size > data_block_size) + return false; + + return true; +} + +/* make the data table offsets relative to the data block */ +static bool fixup_lfp_data_ptrs(const void *bdb, void *ptrs_block) +{ + struct bdb_lvds_lfp_data_ptrs *ptrs = ptrs_block; + u32 offset; + int i; + + offset = block_offset(bdb, BDB_LVDS_LFP_DATA); + + for (i = 0; i < 16; i++) { + if (ptrs->ptr[i].fp_timing.offset < offset || + ptrs->ptr[i].dvo_timing.offset < offset || + ptrs->ptr[i].panel_pnp_id.offset < offset) + return false; + + ptrs->ptr[i].fp_timing.offset -= offset; + ptrs->ptr[i].dvo_timing.offset -= offset; + ptrs->ptr[i].panel_pnp_id.offset -= offset; + } + + if (ptrs->panel_name.table_size) { + if (ptrs->panel_name.offset < offset) + return false; + + ptrs->panel_name.offset -= offset; + } + + return validate_lfp_data_ptrs(bdb, ptrs); +} + +static const void *find_fp_timing_terminator(const u8 *data, int size) +{ + int i; + + for (i = 0; i < size - 1; i++) { + if (data[i] == 0xff && data[i+1] == 0xff) + return &data[i]; + } + + return NULL; +} + +static int make_lfp_data_ptr(struct lvds_lfp_data_ptr_table *table, + int table_size, int total_size) +{ + if (total_size < table_size) + return total_size; + + table->table_size = table_size; + table->offset = total_size - table_size; + + return total_size - table_size; +} + +static void next_lfp_data_ptr(struct lvds_lfp_data_ptr_table *next, + const struct lvds_lfp_data_ptr_table *prev, + int size) +{ + next->table_size = prev->table_size; + next->offset = prev->offset + size; +} + +static void *generate_lfp_data_ptrs(struct drm_i915_private *i915, + const void *bdb) +{ + int i, size, table_size, block_size, offset; + const void *t0, *t1, *block; + struct bdb_lvds_lfp_data_ptrs *ptrs; + void *ptrs_block; + + block = find_raw_section(bdb, BDB_LVDS_LFP_DATA); + if (!block) + return NULL; + + drm_dbg_kms(&i915->drm, "Generating LFP data table pointers\n"); + + block_size = get_blocksize(block); + + size = block_size; + t0 = find_fp_timing_terminator(block, size); + if (!t0) + return NULL; + + size -= t0 - block - 2; + t1 = find_fp_timing_terminator(t0 + 2, size); + if (!t1) + return NULL; + + size = t1 - t0; + if (size * 16 > block_size) + return NULL; + + ptrs_block = kzalloc(sizeof(*ptrs) + 3, GFP_KERNEL); + if (!ptrs_block) + return NULL; + + *(u8 *)(ptrs_block + 0) = BDB_LVDS_LFP_DATA_PTRS; + *(u16 *)(ptrs_block + 1) = sizeof(*ptrs); + ptrs = ptrs_block + 3; + + table_size = sizeof(struct lvds_pnp_id); + size = make_lfp_data_ptr(&ptrs->ptr[0].panel_pnp_id, table_size, size); + + table_size = sizeof(struct lvds_dvo_timing); + size = make_lfp_data_ptr(&ptrs->ptr[0].dvo_timing, table_size, size); + + table_size = t0 - block + 2; + size = make_lfp_data_ptr(&ptrs->ptr[0].fp_timing, table_size, size); + + if (ptrs->ptr[0].fp_timing.table_size) + ptrs->lvds_entries++; + if (ptrs->ptr[0].dvo_timing.table_size) + ptrs->lvds_entries++; + if (ptrs->ptr[0].panel_pnp_id.table_size) + ptrs->lvds_entries++; + + if (size != 0 || ptrs->lvds_entries != 3) { + kfree(ptrs); + return NULL; + } + + size = t1 - t0; + for (i = 1; i < 16; i++) { + next_lfp_data_ptr(&ptrs->ptr[i].fp_timing, &ptrs->ptr[i-1].fp_timing, size); + next_lfp_data_ptr(&ptrs->ptr[i].dvo_timing, &ptrs->ptr[i-1].dvo_timing, size); + next_lfp_data_ptr(&ptrs->ptr[i].panel_pnp_id, &ptrs->ptr[i-1].panel_pnp_id, size); + } + + size = t1 - t0; + table_size = sizeof(struct lvds_lfp_panel_name); + + if (16 * (size + table_size) <= block_size) { + ptrs->panel_name.table_size = table_size; + ptrs->panel_name.offset = size * 16; + } + + offset = block - bdb; + + for (i = 0; i < 16; i++) { + ptrs->ptr[i].fp_timing.offset += offset; + ptrs->ptr[i].dvo_timing.offset += offset; + ptrs->ptr[i].panel_pnp_id.offset += offset; + } + + if (ptrs->panel_name.table_size) + ptrs->panel_name.offset += offset; + + return ptrs_block; +} + +static void +init_bdb_block(struct drm_i915_private *i915, + const void *bdb, enum bdb_block_id section_id, + size_t min_size) +{ + struct bdb_block_entry *entry; + void *temp_block = NULL; + const void *block; + size_t block_size; + + block = find_raw_section(bdb, section_id); + + /* Modern VBTs lack the LFP data table pointers block, make one up */ + if (!block && section_id == BDB_LVDS_LFP_DATA_PTRS) { + temp_block = generate_lfp_data_ptrs(i915, bdb); + if (temp_block) + block = temp_block + 3; + } + if (!block) + return; + + drm_WARN(&i915->drm, min_size == 0, + "Block %d min_size is zero\n", section_id); + + block_size = get_blocksize(block); + + entry = kzalloc(struct_size(entry, data, max(min_size, block_size) + 3), + GFP_KERNEL); + if (!entry) { + kfree(temp_block); + return; + } + + entry->section_id = section_id; + memcpy(entry->data, block - 3, block_size + 3); + + kfree(temp_block); + + drm_dbg_kms(&i915->drm, "Found BDB block %d (size %zu, min size %zu)\n", + section_id, block_size, min_size); + + if (section_id == BDB_LVDS_LFP_DATA_PTRS && + !fixup_lfp_data_ptrs(bdb, entry->data + 3)) { + drm_err(&i915->drm, "VBT has malformed LFP data table pointers\n"); + kfree(entry); + return; + } + + list_add_tail(&entry->node, &i915->vbt.bdb_blocks); +} + +static void init_bdb_blocks(struct drm_i915_private *i915, + const void *bdb) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bdb_blocks); i++) { + enum bdb_block_id section_id = bdb_blocks[i].section_id; + size_t min_size = bdb_blocks[i].min_size; + + if (section_id == BDB_LVDS_LFP_DATA) + min_size = lfp_data_min_size(i915); + + init_bdb_block(i915, bdb, section_id, min_size); + } +} + static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, const struct lvds_dvo_timing *dvo_timing) @@ -169,82 +570,124 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, } static const struct lvds_dvo_timing * -get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data, - const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs, +get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *data, + const struct bdb_lvds_lfp_data_ptrs *ptrs, int index) { - /* - * the size of fp_timing varies on the different platform. - * So calculate the DVO timing relative offset in LVDS data - * entry to get the DVO timing entry - */ - - int lfp_data_size = - lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; - int dvo_timing_offset = - lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].fp_timing_offset; - char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index; - - return (struct lvds_dvo_timing *)(entry + dvo_timing_offset); + return (const void *)data + ptrs->ptr[index].dvo_timing.offset; } -/* get lvds_fp_timing entry - * this function may return NULL if the corresponding entry is invalid - */ static const struct lvds_fp_timing * -get_lvds_fp_timing(const struct bdb_header *bdb, - const struct bdb_lvds_lfp_data *data, +get_lvds_fp_timing(const struct bdb_lvds_lfp_data *data, const struct bdb_lvds_lfp_data_ptrs *ptrs, int index) { - size_t data_ofs = (const u8 *)data - (const u8 *)bdb; - u16 data_size = ((const u16 *)data)[-1]; /* stored in header */ - size_t ofs; + return (const void *)data + ptrs->ptr[index].fp_timing.offset; +} - if (index >= ARRAY_SIZE(ptrs->ptr)) - return NULL; - ofs = ptrs->ptr[index].fp_timing_offset; - if (ofs < data_ofs || - ofs + sizeof(struct lvds_fp_timing) > data_ofs + data_size) +static const struct bdb_lvds_lfp_data_tail * +get_lfp_data_tail(const struct bdb_lvds_lfp_data *data, + const struct bdb_lvds_lfp_data_ptrs *ptrs) +{ + if (ptrs->panel_name.table_size) + return (const void *)data + ptrs->panel_name.offset; + else return NULL; - return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs); +} + +static int opregion_get_panel_type(struct drm_i915_private *i915) +{ + return intel_opregion_get_panel_type(i915); +} + +static int vbt_get_panel_type(struct drm_i915_private *i915) +{ + const struct bdb_lvds_options *lvds_options; + + lvds_options = find_section(i915, BDB_LVDS_OPTIONS); + if (!lvds_options) + return -1; + + if (lvds_options->panel_type > 0xf) { + drm_dbg_kms(&i915->drm, "Invalid VBT panel type 0x%x\n", + lvds_options->panel_type); + return -1; + } + + return lvds_options->panel_type; +} + +static int fallback_get_panel_type(struct drm_i915_private *i915) +{ + return 0; +} + +enum panel_type { + PANEL_TYPE_OPREGION, + PANEL_TYPE_VBT, + PANEL_TYPE_FALLBACK, +}; + +static int get_panel_type(struct drm_i915_private *i915) +{ + struct { + const char *name; + int (*get_panel_type)(struct drm_i915_private *i915); + int panel_type; + } panel_types[] = { + [PANEL_TYPE_OPREGION] = { + .name = "OpRegion", + .get_panel_type = opregion_get_panel_type, + }, + [PANEL_TYPE_VBT] = { + .name = "VBT", + .get_panel_type = vbt_get_panel_type, + }, + [PANEL_TYPE_FALLBACK] = { + .name = "fallback", + .get_panel_type = fallback_get_panel_type, + }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(panel_types); i++) { + panel_types[i].panel_type = panel_types[i].get_panel_type(i915); + + drm_WARN_ON(&i915->drm, panel_types[i].panel_type > 0xf); + + if (panel_types[i].panel_type >= 0) + drm_dbg_kms(&i915->drm, "Panel type (%s): %d\n", + panel_types[i].name, panel_types[i].panel_type); + } + + if (panel_types[PANEL_TYPE_OPREGION].panel_type >= 0) + i = PANEL_TYPE_OPREGION; + else if (panel_types[PANEL_TYPE_VBT].panel_type >= 0) + i = PANEL_TYPE_VBT; + else + i = PANEL_TYPE_FALLBACK; + + drm_dbg_kms(&i915->drm, "Selected panel type (%s): %d\n", + panel_types[i].name, panel_types[i].panel_type); + + return panel_types[i].panel_type; } /* Parse general panel options */ static void -parse_panel_options(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_panel_options(struct drm_i915_private *i915) { const struct bdb_lvds_options *lvds_options; int panel_type; int drrs_mode; - int ret; - lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); + lvds_options = find_section(i915, BDB_LVDS_OPTIONS); if (!lvds_options) return; i915->vbt.lvds_dither = lvds_options->pixel_dither; - ret = intel_opregion_get_panel_type(i915); - if (ret >= 0) { - drm_WARN_ON(&i915->drm, ret > 0xf); - panel_type = ret; - drm_dbg_kms(&i915->drm, "Panel type: %d (OpRegion)\n", - panel_type); - } else { - if (lvds_options->panel_type > 0xf) { - drm_dbg_kms(&i915->drm, - "Invalid VBT panel type 0x%x\n", - lvds_options->panel_type); - return; - } - panel_type = lvds_options->panel_type; - drm_dbg_kms(&i915->drm, "Panel type: %d (VBT)\n", - panel_type); - } + panel_type = get_panel_type(i915); i915->vbt.panel_type = panel_type; @@ -257,42 +700,32 @@ parse_panel_options(struct drm_i915_private *i915, */ switch (drrs_mode) { case 0: - i915->vbt.drrs_type = STATIC_DRRS_SUPPORT; + i915->vbt.drrs_type = DRRS_TYPE_STATIC; drm_dbg_kms(&i915->drm, "DRRS supported mode is static\n"); break; case 2: - i915->vbt.drrs_type = SEAMLESS_DRRS_SUPPORT; + i915->vbt.drrs_type = DRRS_TYPE_SEAMLESS; drm_dbg_kms(&i915->drm, "DRRS supported mode is seamless\n"); break; default: - i915->vbt.drrs_type = DRRS_NOT_SUPPORTED; + i915->vbt.drrs_type = DRRS_TYPE_NONE; drm_dbg_kms(&i915->drm, "DRRS not supported (VBT input)\n"); break; } } -/* Try to find integrated panel timing data */ static void parse_lfp_panel_dtd(struct drm_i915_private *i915, - const struct bdb_header *bdb) + const struct bdb_lvds_lfp_data *lvds_lfp_data, + const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs) { - const struct bdb_lvds_lfp_data *lvds_lfp_data; - const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; const struct lvds_dvo_timing *panel_dvo_timing; const struct lvds_fp_timing *fp_timing; struct drm_display_mode *panel_fixed_mode; int panel_type = i915->vbt.panel_type; - lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); - if (!lvds_lfp_data) - return; - - lvds_lfp_data_ptrs = find_section(bdb, BDB_LVDS_LFP_DATA_PTRS); - if (!lvds_lfp_data_ptrs) - return; - panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, lvds_lfp_data_ptrs, panel_type); @@ -306,34 +739,75 @@ parse_lfp_panel_dtd(struct drm_i915_private *i915, i915->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; drm_dbg_kms(&i915->drm, - "Found panel mode in BIOS VBT legacy lfp table:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + "Found panel mode in BIOS VBT legacy lfp table: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); - fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data, + fp_timing = get_lvds_fp_timing(lvds_lfp_data, lvds_lfp_data_ptrs, panel_type); - if (fp_timing) { - /* check the resolution, just to be sure */ - if (fp_timing->x_res == panel_fixed_mode->hdisplay && - fp_timing->y_res == panel_fixed_mode->vdisplay) { - i915->vbt.bios_lvds_val = fp_timing->lvds_reg_val; - drm_dbg_kms(&i915->drm, - "VBT initial LVDS value %x\n", - i915->vbt.bios_lvds_val); - } + + /* check the resolution, just to be sure */ + if (fp_timing->x_res == panel_fixed_mode->hdisplay && + fp_timing->y_res == panel_fixed_mode->vdisplay) { + i915->vbt.bios_lvds_val = fp_timing->lvds_reg_val; + drm_dbg_kms(&i915->drm, + "VBT initial LVDS value %x\n", + i915->vbt.bios_lvds_val); + } +} + +static void +parse_lfp_data(struct drm_i915_private *i915) +{ + const struct bdb_lvds_lfp_data *data; + const struct bdb_lvds_lfp_data_tail *tail; + const struct bdb_lvds_lfp_data_ptrs *ptrs; + int panel_type = i915->vbt.panel_type; + + ptrs = find_section(i915, BDB_LVDS_LFP_DATA_PTRS); + if (!ptrs) + return; + + data = find_section(i915, BDB_LVDS_LFP_DATA); + if (!data) + return; + + if (!i915->vbt.lfp_lvds_vbt_mode) + parse_lfp_panel_dtd(i915, data, ptrs); + + tail = get_lfp_data_tail(data, ptrs); + if (!tail) + return; + + if (i915->vbt.version >= 188) { + i915->vbt.seamless_drrs_min_refresh_rate = + tail->seamless_drrs_min_refresh_rate[panel_type]; + drm_dbg_kms(&i915->drm, + "Seamless DRRS min refresh rate: %d Hz\n", + i915->vbt.seamless_drrs_min_refresh_rate); } } static void -parse_generic_dtd(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_generic_dtd(struct drm_i915_private *i915) { const struct bdb_generic_dtd *generic_dtd; const struct generic_dtd_entry *dtd; struct drm_display_mode *panel_fixed_mode; int num_dtd; - generic_dtd = find_section(bdb, BDB_GENERIC_DTD); + /* + * Older VBTs provided DTD information for internal displays through + * the "LFP panel tables" block (42). As of VBT revision 229 the + * DTD information should be provided via a newer "generic DTD" + * block (58). Just to be safe, we'll try the new generic DTD block + * first on VBT >= 229, but still fall back to trying the old LFP + * block if that fails. + */ + if (i915->vbt.version < 229) + return; + + generic_dtd = find_section(i915, BDB_GENERIC_DTD); if (!generic_dtd) return; @@ -397,40 +871,21 @@ parse_generic_dtd(struct drm_i915_private *i915, panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC; drm_dbg_kms(&i915->drm, - "Found panel mode in BIOS VBT generic dtd table:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + "Found panel mode in BIOS VBT generic dtd table: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); i915->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; } static void -parse_panel_dtd(struct drm_i915_private *i915, - const struct bdb_header *bdb) -{ - /* - * Older VBTs provided provided DTD information for internal displays - * through the "LFP panel DTD" block (42). As of VBT revision 229, - * that block is now deprecated and DTD information should be provided - * via a newer "generic DTD" block (58). Just to be safe, we'll - * try the new generic DTD block first on VBT >= 229, but still fall - * back to trying the old LFP block if that fails. - */ - if (bdb->version >= 229) - parse_generic_dtd(i915, bdb); - if (!i915->vbt.lfp_lvds_vbt_mode) - parse_lfp_panel_dtd(i915, bdb); -} - -static void -parse_lfp_backlight(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_lfp_backlight(struct drm_i915_private *i915) { const struct bdb_lfp_backlight_data *backlight_data; const struct lfp_backlight_data_entry *entry; int panel_type = i915->vbt.panel_type; u16 level; - backlight_data = find_section(bdb, BDB_LVDS_BACKLIGHT); + backlight_data = find_section(i915, BDB_LVDS_BACKLIGHT); if (!backlight_data) return; @@ -452,12 +907,12 @@ parse_lfp_backlight(struct drm_i915_private *i915, } i915->vbt.backlight.type = INTEL_BACKLIGHT_DISPLAY_DDI; - if (bdb->version >= 191) { + if (i915->vbt.version >= 191) { size_t exp_size; - if (bdb->version >= 236) + if (i915->vbt.version >= 236) exp_size = sizeof(struct bdb_lfp_backlight_data); - else if (bdb->version >= 234) + else if (i915->vbt.version >= 234) exp_size = EXP_BDB_LFP_BL_DATA_SIZE_REV_234; else exp_size = EXP_BDB_LFP_BL_DATA_SIZE_REV_191; @@ -474,14 +929,14 @@ parse_lfp_backlight(struct drm_i915_private *i915, i915->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz; i915->vbt.backlight.active_low_pwm = entry->active_low_pwm; - if (bdb->version >= 234) { + if (i915->vbt.version >= 234) { u16 min_level; bool scale; level = backlight_data->brightness_level[panel_type].level; min_level = backlight_data->brightness_min_level[panel_type].level; - if (bdb->version >= 236) + if (i915->vbt.version >= 236) scale = backlight_data->brightness_precision_bits[panel_type] == 16; else scale = level > 255; @@ -514,8 +969,7 @@ parse_lfp_backlight(struct drm_i915_private *i915, /* Try to find sdvo panel data */ static void -parse_sdvo_panel_data(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_sdvo_panel_data(struct drm_i915_private *i915) { const struct bdb_sdvo_panel_dtds *dtds; struct drm_display_mode *panel_fixed_mode; @@ -531,14 +985,14 @@ parse_sdvo_panel_data(struct drm_i915_private *i915, if (index == -1) { const struct bdb_sdvo_lvds_options *sdvo_lvds_options; - sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); + sdvo_lvds_options = find_section(i915, BDB_SDVO_LVDS_OPTIONS); if (!sdvo_lvds_options) return; index = sdvo_lvds_options->panel_type; } - dtds = find_section(bdb, BDB_SDVO_PANEL_DTDS); + dtds = find_section(i915, BDB_SDVO_PANEL_DTDS); if (!dtds) return; @@ -551,8 +1005,8 @@ parse_sdvo_panel_data(struct drm_i915_private *i915, i915->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode; drm_dbg_kms(&i915->drm, - "Found SDVO panel mode in BIOS VBT tables:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + "Found SDVO panel mode in BIOS VBT tables: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(panel_fixed_mode)); } static int intel_bios_ssc_frequency(struct drm_i915_private *i915, @@ -570,18 +1024,17 @@ static int intel_bios_ssc_frequency(struct drm_i915_private *i915, } static void -parse_general_features(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_general_features(struct drm_i915_private *i915) { const struct bdb_general_features *general; - general = find_section(bdb, BDB_GENERAL_FEATURES); + general = find_section(i915, BDB_GENERAL_FEATURES); if (!general) return; i915->vbt.int_tv_support = general->int_tv_support; /* int_crt_support can't be trusted on earlier platforms */ - if (bdb->version >= 155 && + if (i915->vbt.version >= 155 && (HAS_DDI(i915) || IS_VALLEYVIEW(i915))) i915->vbt.int_crt_support = general->int_crt_support; i915->vbt.lvds_use_ssc = general->enable_ssc; @@ -589,7 +1042,7 @@ parse_general_features(struct drm_i915_private *i915, intel_bios_ssc_frequency(i915, general->ssc_freq); i915->vbt.display_clock_mode = general->display_clock_mode; i915->vbt.fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; - if (bdb->version >= 181) { + if (i915->vbt.version >= 181) { i915->vbt.orientation = general->rotate_180 ? DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP : DRM_MODE_PANEL_ORIENTATION_NORMAL; @@ -597,7 +1050,7 @@ parse_general_features(struct drm_i915_private *i915, i915->vbt.orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; } - if (bdb->version >= 249 && general->afc_startup_config) { + if (i915->vbt.version >= 249 && general->afc_startup_config) { i915->vbt.override_afc_startup = true; i915->vbt.override_afc_startup_val = general->afc_startup_config == 0x1 ? 0x0 : 0x7; } @@ -695,12 +1148,11 @@ parse_sdvo_device_mapping(struct drm_i915_private *i915) } static void -parse_driver_features(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_driver_features(struct drm_i915_private *i915) { const struct bdb_driver_features *driver; - driver = find_section(bdb, BDB_DRIVER_FEATURES); + driver = find_section(i915, BDB_DRIVER_FEATURES); if (!driver) return; @@ -724,13 +1176,13 @@ parse_driver_features(struct drm_i915_private *i915, * in the wild with the bits correctly populated. Version * 108 (on i85x) does not have the bits correctly populated. */ - if (bdb->version >= 134 && + if (i915->vbt.version >= 134 && driver->lvds_config != BDB_DRIVER_FEATURE_INT_LVDS && driver->lvds_config != BDB_DRIVER_FEATURE_INT_SDVO_LVDS) i915->vbt.int_lvds_support = 0; } - if (bdb->version < 228) { + if (i915->vbt.version < 228) { drm_dbg_kms(&i915->drm, "DRRS State Enabled:%d\n", driver->drrs_enabled); /* @@ -740,23 +1192,22 @@ parse_driver_features(struct drm_i915_private *i915, * driver->drrs_enabled=false */ if (!driver->drrs_enabled) - i915->vbt.drrs_type = DRRS_NOT_SUPPORTED; + i915->vbt.drrs_type = DRRS_TYPE_NONE; i915->vbt.psr.enable = driver->psr_enabled; } } static void -parse_power_conservation_features(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_power_conservation_features(struct drm_i915_private *i915) { const struct bdb_lfp_power *power; u8 panel_type = i915->vbt.panel_type; - if (bdb->version < 228) + if (i915->vbt.version < 228) return; - power = find_section(bdb, BDB_LFP_POWER); + power = find_section(i915, BDB_LFP_POWER); if (!power) return; @@ -769,21 +1220,21 @@ parse_power_conservation_features(struct drm_i915_private *i915, * power->drrs & BIT(panel_type)=false */ if (!(power->drrs & BIT(panel_type))) - i915->vbt.drrs_type = DRRS_NOT_SUPPORTED; + i915->vbt.drrs_type = DRRS_TYPE_NONE; - if (bdb->version >= 232) + if (i915->vbt.version >= 232) i915->vbt.edp.hobl = power->hobl & BIT(panel_type); } static void -parse_edp(struct drm_i915_private *i915, const struct bdb_header *bdb) +parse_edp(struct drm_i915_private *i915) { const struct bdb_edp *edp; const struct edp_power_seq *edp_pps; const struct edp_fast_link_params *edp_link_params; int panel_type = i915->vbt.panel_type; - edp = find_section(bdb, BDB_EDP); + edp = find_section(i915, BDB_EDP); if (!edp) return; @@ -876,7 +1327,7 @@ parse_edp(struct drm_i915_private *i915, const struct bdb_header *bdb) break; } - if (bdb->version >= 173) { + if (i915->vbt.version >= 173) { u8 vswing; /* Don't read from VBT if module parameter has valid value*/ @@ -888,16 +1339,19 @@ parse_edp(struct drm_i915_private *i915, const struct bdb_header *bdb) i915->vbt.edp.low_vswing = vswing == 0; } } + + i915->vbt.edp.drrs_msa_timing_delay = + (edp->sdrrs_msa_timing_delay >> (panel_type * 2)) & 3; } static void -parse_psr(struct drm_i915_private *i915, const struct bdb_header *bdb) +parse_psr(struct drm_i915_private *i915) { const struct bdb_psr *psr; const struct psr_table *psr_table; int panel_type = i915->vbt.panel_type; - psr = find_section(bdb, BDB_PSR); + psr = find_section(i915, BDB_PSR); if (!psr) { drm_dbg_kms(&i915->drm, "No PSR BDB found.\n"); return; @@ -916,7 +1370,7 @@ parse_psr(struct drm_i915_private *i915, const struct bdb_header *bdb) * New psr options 0=500us, 1=100us, 2=2500us, 3=0us * Old decimal value is wake up time in multiples of 100 us. */ - if (bdb->version >= 205 && + if (i915->vbt.version >= 205 && (DISPLAY_VER(i915) >= 9 && !IS_BROXTON(i915))) { switch (psr_table->tp1_wakeup_time) { case 0: @@ -962,7 +1416,7 @@ parse_psr(struct drm_i915_private *i915, const struct bdb_header *bdb) i915->vbt.psr.tp2_tp3_wakeup_time_us = psr_table->tp2_tp3_wakeup_time * 100; } - if (bdb->version >= 226) { + if (i915->vbt.version >= 226) { u32 wakeup_time = psr->psr2_tp2_tp3_wakeup_time; wakeup_time = (wakeup_time >> (2 * panel_type)) & 0x3; @@ -1031,8 +1485,7 @@ static void parse_dsi_backlight_ports(struct drm_i915_private *i915, } static void -parse_mipi_config(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_mipi_config(struct drm_i915_private *i915) { const struct bdb_mipi_config *start; const struct mipi_config *config; @@ -1055,7 +1508,7 @@ parse_mipi_config(struct drm_i915_private *i915, /* Parse #52 for panel index used from panel_type already * parsed */ - start = find_section(bdb, BDB_MIPI_CONFIG); + start = find_section(i915, BDB_MIPI_CONFIG); if (!start) { drm_dbg_kms(&i915->drm, "No MIPI config BDB found"); return; @@ -1082,7 +1535,7 @@ parse_mipi_config(struct drm_i915_private *i915, return; } - parse_dsi_backlight_ports(i915, bdb->version, port); + parse_dsi_backlight_ports(i915, i915->vbt.version, port); /* FIXME is the 90 vs. 270 correct? */ switch (config->rotation) { @@ -1351,8 +1804,7 @@ static void fixup_mipi_sequences(struct drm_i915_private *i915) } static void -parse_mipi_sequence(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_mipi_sequence(struct drm_i915_private *i915) { int panel_type = i915->vbt.panel_type; const struct bdb_mipi_sequence *sequence; @@ -1365,7 +1817,7 @@ parse_mipi_sequence(struct drm_i915_private *i915, if (i915->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID) return; - sequence = find_section(bdb, BDB_MIPI_SEQUENCE); + sequence = find_section(i915, BDB_MIPI_SEQUENCE); if (!sequence) { drm_dbg_kms(&i915->drm, "No MIPI Sequence found, parsing complete\n"); @@ -1436,8 +1888,7 @@ err: } static void -parse_compression_parameters(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_compression_parameters(struct drm_i915_private *i915) { const struct bdb_compression_parameters *params; struct intel_bios_encoder_data *devdata; @@ -1445,10 +1896,10 @@ parse_compression_parameters(struct drm_i915_private *i915, u16 block_size; int index; - if (bdb->version < 198) + if (i915->vbt.version < 198) return; - params = find_section(bdb, BDB_COMPRESSION_PARAMETERS); + params = find_section(i915, BDB_COMPRESSION_PARAMETERS); if (params) { /* Sanity checks */ if (params->entry_size != sizeof(params->data[0])) { @@ -1955,6 +2406,12 @@ static int _intel_bios_max_tmds_clock(const struct intel_bios_encoder_data *devd fallthrough; case HDMI_MAX_DATA_RATE_PLATFORM: return 0; + case HDMI_MAX_DATA_RATE_594: + return 594000; + case HDMI_MAX_DATA_RATE_340: + return 340000; + case HDMI_MAX_DATA_RATE_300: + return 300000; case HDMI_MAX_DATA_RATE_297: return 297000; case HDMI_MAX_DATA_RATE_165: @@ -2077,8 +2534,7 @@ static void parse_ddi_ports(struct drm_i915_private *i915) } static void -parse_general_definitions(struct drm_i915_private *i915, - const struct bdb_header *bdb) +parse_general_definitions(struct drm_i915_private *i915) { const struct bdb_general_definitions *defs; struct intel_bios_encoder_data *devdata; @@ -2088,7 +2544,7 @@ parse_general_definitions(struct drm_i915_private *i915, u16 block_size; int bus_pin; - defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); + defs = find_section(i915, BDB_GENERAL_DEFINITIONS); if (!defs) { drm_dbg_kms(&i915->drm, "No general definition block is found, no devices defined.\n"); @@ -2108,31 +2564,31 @@ parse_general_definitions(struct drm_i915_private *i915, if (intel_gmbus_is_valid_pin(i915, bus_pin)) i915->vbt.crt_ddc_pin = bus_pin; - if (bdb->version < 106) { + if (i915->vbt.version < 106) { expected_size = 22; - } else if (bdb->version < 111) { + } else if (i915->vbt.version < 111) { expected_size = 27; - } else if (bdb->version < 195) { + } else if (i915->vbt.version < 195) { expected_size = LEGACY_CHILD_DEVICE_CONFIG_SIZE; - } else if (bdb->version == 195) { + } else if (i915->vbt.version == 195) { expected_size = 37; - } else if (bdb->version <= 215) { + } else if (i915->vbt.version <= 215) { expected_size = 38; - } else if (bdb->version <= 237) { + } else if (i915->vbt.version <= 237) { expected_size = 39; } else { expected_size = sizeof(*child); BUILD_BUG_ON(sizeof(*child) < 39); drm_dbg(&i915->drm, "Expected child device config size for VBT version %u not known; assuming %u\n", - bdb->version, expected_size); + i915->vbt.version, expected_size); } /* Flag an error for unexpected size, but continue anyway. */ if (defs->child_dev_size != expected_size) drm_err(&i915->drm, "Unexpected child device config size %u (expected %u for VBT version %u)\n", - defs->child_dev_size, expected_size, bdb->version); + defs->child_dev_size, expected_size, i915->vbt.version); /* The legacy sized child device config is the minimum we need. */ if (defs->child_dev_size < LEGACY_CHILD_DEVICE_CONFIG_SIZE) { @@ -2457,6 +2913,7 @@ void intel_bios_init(struct drm_i915_private *i915) const struct bdb_header *bdb; INIT_LIST_HEAD(&i915->vbt.display_devices); + INIT_LIST_HEAD(&i915->vbt.bdb_blocks); if (!HAS_DISPLAY(i915)) { drm_dbg_kms(&i915->drm, @@ -2488,24 +2945,27 @@ void intel_bios_init(struct drm_i915_private *i915) drm_dbg_kms(&i915->drm, "VBT signature \"%.*s\", BDB version %d\n", - (int)sizeof(vbt->signature), vbt->signature, bdb->version); + (int)sizeof(vbt->signature), vbt->signature, i915->vbt.version); + + init_bdb_blocks(i915, bdb); /* Grab useful general definitions */ - parse_general_features(i915, bdb); - parse_general_definitions(i915, bdb); - parse_panel_options(i915, bdb); - parse_panel_dtd(i915, bdb); - parse_lfp_backlight(i915, bdb); - parse_sdvo_panel_data(i915, bdb); - parse_driver_features(i915, bdb); - parse_power_conservation_features(i915, bdb); - parse_edp(i915, bdb); - parse_psr(i915, bdb); - parse_mipi_config(i915, bdb); - parse_mipi_sequence(i915, bdb); + parse_general_features(i915); + parse_general_definitions(i915); + parse_panel_options(i915); + parse_generic_dtd(i915); + parse_lfp_data(i915); + parse_lfp_backlight(i915); + parse_sdvo_panel_data(i915); + parse_driver_features(i915); + parse_power_conservation_features(i915); + parse_edp(i915); + parse_psr(i915); + parse_mipi_config(i915); + parse_mipi_sequence(i915); /* Depends on child device list */ - parse_compression_parameters(i915, bdb); + parse_compression_parameters(i915); out: if (!vbt) { @@ -2527,14 +2987,20 @@ out: */ void intel_bios_driver_remove(struct drm_i915_private *i915) { - struct intel_bios_encoder_data *devdata, *n; + struct intel_bios_encoder_data *devdata, *nd; + struct bdb_block_entry *entry, *ne; - list_for_each_entry_safe(devdata, n, &i915->vbt.display_devices, node) { + list_for_each_entry_safe(devdata, nd, &i915->vbt.display_devices, node) { list_del(&devdata->node); kfree(devdata->dsc); kfree(devdata); } + list_for_each_entry_safe(entry, ne, &i915->vbt.bdb_blocks, node) { + list_del(&entry->node); + kfree(entry); + } + kfree(i915->vbt.sdvo_lvds_vbt_mode); i915->vbt.sdvo_lvds_vbt_mode = NULL; kfree(i915->vbt.lfp_lvds_vbt_mode); diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index adf58c58513b..37bd7b17f3d0 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -6,6 +6,7 @@ #include <drm/drm_atomic_state_helper.h> #include "i915_reg.h" +#include "i915_utils.h" #include "intel_atomic.h" #include "intel_bw.h" #include "intel_cdclk.h" @@ -124,8 +125,8 @@ int icl_pcode_restrict_qgv_points(struct drm_i915_private *dev_priv, /* bspec says to keep retrying for at least 1 ms */ ret = skl_pcode_request(dev_priv, ICL_PCODE_SAGV_DE_MEM_SS_CONFIG, points_mask, - ICL_PCODE_POINTS_RESTRICTED_MASK, - ICL_PCODE_POINTS_RESTRICTED, + ICL_PCODE_REP_QGV_MASK | ADLS_PCODE_REP_PSF_MASK, + ICL_PCODE_REP_QGV_SAFE | ADLS_PCODE_REP_PSF_SAFE, 1); if (ret < 0) { @@ -464,20 +465,25 @@ static int tgl_get_bw_info(struct drm_i915_private *dev_priv, const struct intel static void dg2_get_bw_info(struct drm_i915_private *i915) { - struct intel_bw_info *bi = &i915->max_bw[0]; + unsigned int deratedbw = IS_DG2_G11(i915) ? 38000 : 50000; + int num_groups = ARRAY_SIZE(i915->max_bw); + int i; /* * DG2 doesn't have SAGV or QGV points, just a constant max bandwidth - * that doesn't depend on the number of planes enabled. Create a - * single dummy QGV point to reflect that. DG2-G10 platforms have a - * constant 50 GB/s bandwidth, whereas DG2-G11 platforms have 38 GB/s. + * that doesn't depend on the number of planes enabled. So fill all the + * plane group with constant bw information for uniformity with other + * platforms. DG2-G10 platforms have a constant 50 GB/s bandwidth, + * whereas DG2-G11 platforms have 38 GB/s. */ - bi->num_planes = 1; - bi->num_qgv_points = 1; - if (IS_DG2_G11(i915)) - bi->deratedbw[0] = 38000; - else - bi->deratedbw[0] = 50000; + for (i = 0; i < num_groups; i++) { + struct intel_bw_info *bi = &i915->max_bw[i]; + + bi->num_planes = 1; + /* Need only one dummy QGV point per group */ + bi->num_qgv_points = 1; + bi->deratedbw[0] = deratedbw; + } i915->sagv_status = I915_SAGV_NOT_CONTROLLED; } @@ -578,6 +584,7 @@ static unsigned int intel_bw_crtc_num_active_planes(const struct intel_crtc_stat static unsigned int intel_bw_crtc_data_rate(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); unsigned int data_rate = 0; enum plane_id plane_id; @@ -590,11 +597,26 @@ static unsigned int intel_bw_crtc_data_rate(const struct intel_crtc_state *crtc_ continue; data_rate += crtc_state->data_rate[plane_id]; + + if (DISPLAY_VER(i915) < 11) + data_rate += crtc_state->data_rate_y[plane_id]; } return data_rate; } +/* "Maximum Pipe Read Bandwidth" */ +static int intel_bw_crtc_min_cdclk(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + + if (DISPLAY_VER(i915) < 12) + return 0; + + return DIV_ROUND_UP_ULL(mul_u32_u32(intel_bw_crtc_data_rate(crtc_state), 10), 512); +} + void intel_bw_crtc_update(struct intel_bw_state *bw_state, const struct intel_crtc_state *crtc_state) { @@ -633,8 +655,8 @@ static unsigned int intel_bw_data_rate(struct drm_i915_private *dev_priv, for_each_pipe(dev_priv, pipe) data_rate += bw_state->data_rate[pipe]; - if (DISPLAY_VER(dev_priv) >= 13 && intel_vtd_active(dev_priv)) - data_rate = data_rate * 105 / 100; + if (DISPLAY_VER(dev_priv) >= 13 && i915_vtd_active(dev_priv)) + data_rate = DIV_ROUND_UP(data_rate * 105, 100); return data_rate; } @@ -674,6 +696,53 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state) return to_intel_bw_state(bw_state); } +static bool intel_bw_state_changed(struct drm_i915_private *i915, + const struct intel_bw_state *old_bw_state, + const struct intel_bw_state *new_bw_state) +{ + enum pipe pipe; + + for_each_pipe(i915, pipe) { + const struct intel_dbuf_bw *old_crtc_bw = + &old_bw_state->dbuf_bw[pipe]; + const struct intel_dbuf_bw *new_crtc_bw = + &new_bw_state->dbuf_bw[pipe]; + enum dbuf_slice slice; + + for_each_dbuf_slice(i915, slice) { + if (old_crtc_bw->max_bw[slice] != new_crtc_bw->max_bw[slice] || + old_crtc_bw->active_planes[slice] != new_crtc_bw->active_planes[slice]) + return true; + } + + if (old_bw_state->min_cdclk[pipe] != new_bw_state->min_cdclk[pipe]) + return true; + } + + return false; +} + +static void skl_plane_calc_dbuf_bw(struct intel_bw_state *bw_state, + struct intel_crtc *crtc, + enum plane_id plane_id, + const struct skl_ddb_entry *ddb, + unsigned int data_rate) +{ + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + struct intel_dbuf_bw *crtc_bw = &bw_state->dbuf_bw[crtc->pipe]; + unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(i915, ddb); + enum dbuf_slice slice; + + /* + * The arbiter can only really guarantee an + * equal share of the total bw to each plane. + */ + for_each_dbuf_slice_in_mask(i915, slice, dbuf_mask) { + crtc_bw->max_bw[slice] = max(crtc_bw->max_bw[slice], data_rate); + crtc_bw->active_planes[slice] |= BIT(plane_id); + } +} + static void skl_crtc_calc_dbuf_bw(struct intel_bw_state *bw_state, const struct intel_crtc_state *crtc_state) { @@ -682,136 +751,145 @@ static void skl_crtc_calc_dbuf_bw(struct intel_bw_state *bw_state, struct intel_dbuf_bw *crtc_bw = &bw_state->dbuf_bw[crtc->pipe]; enum plane_id plane_id; - memset(&crtc_bw->used_bw, 0, sizeof(crtc_bw->used_bw)); + memset(crtc_bw, 0, sizeof(*crtc_bw)); if (!crtc_state->hw.active) return; for_each_plane_id_on_crtc(crtc, plane_id) { - const struct skl_ddb_entry *ddb_y = - &crtc_state->wm.skl.plane_ddb_y[plane_id]; - const struct skl_ddb_entry *ddb_uv = - &crtc_state->wm.skl.plane_ddb_uv[plane_id]; - unsigned int data_rate = crtc_state->data_rate[plane_id]; - unsigned int dbuf_mask = 0; - enum dbuf_slice slice; - - dbuf_mask |= skl_ddb_dbuf_slice_mask(i915, ddb_y); - dbuf_mask |= skl_ddb_dbuf_slice_mask(i915, ddb_uv); - /* - * FIXME: To calculate that more properly we probably - * need to split per plane data_rate into data_rate_y - * and data_rate_uv for multiplanar formats in order not - * to get accounted those twice if they happen to reside - * on different slices. - * However for pre-icl this would work anyway because - * we have only single slice and for icl+ uv plane has - * non-zero data rate. - * So in worst case those calculation are a bit - * pessimistic, which shouldn't pose any significant - * problem anyway. + * We assume cursors are small enough + * to not cause bandwidth problems. */ - for_each_dbuf_slice_in_mask(i915, slice, dbuf_mask) - crtc_bw->used_bw[slice] += data_rate; - } -} - -int skl_bw_calc_min_cdclk(struct intel_atomic_state *state) -{ - struct drm_i915_private *dev_priv = to_i915(state->base.dev); - struct intel_bw_state *new_bw_state = NULL; - struct intel_bw_state *old_bw_state = NULL; - const struct intel_crtc_state *crtc_state; - struct intel_crtc *crtc; - int max_bw = 0; - enum pipe pipe; - int i; - - for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { - new_bw_state = intel_atomic_get_bw_state(state); - if (IS_ERR(new_bw_state)) - return PTR_ERR(new_bw_state); + if (plane_id == PLANE_CURSOR) + continue; - old_bw_state = intel_atomic_get_old_bw_state(state); + skl_plane_calc_dbuf_bw(bw_state, crtc, plane_id, + &crtc_state->wm.skl.plane_ddb[plane_id], + crtc_state->data_rate[plane_id]); - skl_crtc_calc_dbuf_bw(new_bw_state, crtc_state); + if (DISPLAY_VER(i915) < 11) + skl_plane_calc_dbuf_bw(bw_state, crtc, plane_id, + &crtc_state->wm.skl.plane_ddb_y[plane_id], + crtc_state->data_rate[plane_id]); } +} - if (!old_bw_state) - return 0; +/* "Maximum Data Buffer Bandwidth" */ +static int +intel_bw_dbuf_min_cdclk(struct drm_i915_private *i915, + const struct intel_bw_state *bw_state) +{ + unsigned int total_max_bw = 0; + enum dbuf_slice slice; - for_each_pipe(dev_priv, pipe) { - struct intel_dbuf_bw *crtc_bw; - enum dbuf_slice slice; + for_each_dbuf_slice(i915, slice) { + int num_active_planes = 0; + unsigned int max_bw = 0; + enum pipe pipe; - crtc_bw = &new_bw_state->dbuf_bw[pipe]; + /* + * The arbiter can only really guarantee an + * equal share of the total bw to each plane. + */ + for_each_pipe(i915, pipe) { + const struct intel_dbuf_bw *crtc_bw = &bw_state->dbuf_bw[pipe]; - for_each_dbuf_slice(dev_priv, slice) { - /* - * Current experimental observations show that contrary - * to BSpec we get underruns once we exceed 64 * CDCLK - * for slices in total. - * As a temporary measure in order not to keep CDCLK - * bumped up all the time we calculate CDCLK according - * to this formula for overall bw consumed by slices. - */ - max_bw += crtc_bw->used_bw[slice]; + max_bw = max(crtc_bw->max_bw[slice], max_bw); + num_active_planes += hweight8(crtc_bw->active_planes[slice]); } + max_bw *= num_active_planes; + + total_max_bw = max(total_max_bw, max_bw); } - new_bw_state->min_cdclk = max_bw / 64; + return DIV_ROUND_UP(total_max_bw, 64); +} - if (new_bw_state->min_cdclk != old_bw_state->min_cdclk) { - int ret = intel_atomic_lock_global_state(&new_bw_state->base); +int intel_bw_min_cdclk(struct drm_i915_private *i915, + const struct intel_bw_state *bw_state) +{ + enum pipe pipe; + int min_cdclk; - if (ret) - return ret; - } + min_cdclk = intel_bw_dbuf_min_cdclk(i915, bw_state); - return 0; + for_each_pipe(i915, pipe) + min_cdclk = max(bw_state->min_cdclk[pipe], min_cdclk); + + return min_cdclk; } -int intel_bw_calc_min_cdclk(struct intel_atomic_state *state) +int intel_bw_calc_min_cdclk(struct intel_atomic_state *state, + bool *need_cdclk_calc) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_bw_state *new_bw_state = NULL; - struct intel_bw_state *old_bw_state = NULL; + const struct intel_bw_state *old_bw_state = NULL; + const struct intel_cdclk_state *cdclk_state; const struct intel_crtc_state *crtc_state; + int old_min_cdclk, new_min_cdclk; struct intel_crtc *crtc; - int min_cdclk = 0; - enum pipe pipe; int i; + if (DISPLAY_VER(dev_priv) < 9) + return 0; + for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { new_bw_state = intel_atomic_get_bw_state(state); if (IS_ERR(new_bw_state)) return PTR_ERR(new_bw_state); old_bw_state = intel_atomic_get_old_bw_state(state); + + skl_crtc_calc_dbuf_bw(new_bw_state, crtc_state); + + new_bw_state->min_cdclk[crtc->pipe] = + intel_bw_crtc_min_cdclk(crtc_state); } if (!old_bw_state) return 0; - for_each_pipe(dev_priv, pipe) { - struct intel_cdclk_state *cdclk_state; + if (intel_bw_state_changed(dev_priv, old_bw_state, new_bw_state)) { + int ret = intel_atomic_lock_global_state(&new_bw_state->base); + if (ret) + return ret; + } - cdclk_state = intel_atomic_get_new_cdclk_state(state); - if (!cdclk_state) - return 0; + old_min_cdclk = intel_bw_min_cdclk(dev_priv, old_bw_state); + new_min_cdclk = intel_bw_min_cdclk(dev_priv, new_bw_state); - min_cdclk = max(cdclk_state->min_cdclk[pipe], min_cdclk); - } + /* + * No need to check against the cdclk state if + * the min cdclk doesn't increase. + * + * Ie. we only ever increase the cdclk due to bandwidth + * requirements. This can reduce back and forth + * display blinking due to constant cdclk changes. + */ + if (new_min_cdclk <= old_min_cdclk) + return 0; - new_bw_state->min_cdclk = min_cdclk; + cdclk_state = intel_atomic_get_cdclk_state(state); + if (IS_ERR(cdclk_state)) + return PTR_ERR(cdclk_state); - if (new_bw_state->min_cdclk != old_bw_state->min_cdclk) { - int ret = intel_atomic_lock_global_state(&new_bw_state->base); + /* + * No need to recalculate the cdclk state if + * the min cdclk doesn't increase. + * + * Ie. we only ever increase the cdclk due to bandwidth + * requirements. This can reduce back and forth + * display blinking due to constant cdclk changes. + */ + if (new_min_cdclk <= cdclk_state->bw_min_cdclk) + return 0; - if (ret) - return ret; - } + drm_dbg_kms(&dev_priv->drm, + "new bandwidth min cdclk (%d kHz) > old min cdclk (%d kHz)\n", + new_min_cdclk, cdclk_state->bw_min_cdclk); + *need_cdclk_calc = true; return 0; } @@ -820,7 +898,7 @@ static u16 icl_qgv_points_mask(struct drm_i915_private *i915) { unsigned int num_psf_gv_points = i915->max_bw[0].num_psf_gv_points; unsigned int num_qgv_points = i915->max_bw[0].num_qgv_points; - u16 mask = 0; + u16 qgv_points = 0, psf_points = 0; /* * We can _not_ use the whole ADLS_QGV_PT_MASK here, as PCode rejects @@ -828,12 +906,12 @@ static u16 icl_qgv_points_mask(struct drm_i915_private *i915) * So need to operate only with those returned from PCode. */ if (num_qgv_points > 0) - mask |= REG_GENMASK(num_qgv_points - 1, 0); + qgv_points = GENMASK(num_qgv_points - 1, 0); if (num_psf_gv_points > 0) - mask |= REG_GENMASK(num_psf_gv_points - 1, 0) << ADLS_PSF_PT_SHIFT; + psf_points = GENMASK(num_psf_gv_points - 1, 0); - return mask; + return ICL_PCODE_REQ_QGV_PT(qgv_points) | ADLS_PCODE_REQ_PSF_PT(psf_points); } static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *changed) @@ -890,7 +968,7 @@ int intel_bw_atomic_check(struct intel_atomic_state *state) unsigned int data_rate; unsigned int num_active_planes; int i, ret; - u32 allowed_points = 0; + u16 qgv_points = 0, psf_points = 0; unsigned int max_bw_point = 0, max_bw = 0; unsigned int num_qgv_points = dev_priv->max_bw[0].num_qgv_points; unsigned int num_psf_gv_points = dev_priv->max_bw[0].num_psf_gv_points; @@ -948,7 +1026,7 @@ int intel_bw_atomic_check(struct intel_atomic_state *state) max_bw = max_data_rate; } if (max_data_rate >= data_rate) - allowed_points |= REG_FIELD_PREP(ADLS_QGV_PT_MASK, BIT(i)); + qgv_points |= BIT(i); drm_dbg_kms(&dev_priv->drm, "QGV point %d: max bw %d required %d\n", i, max_data_rate, data_rate); @@ -958,7 +1036,7 @@ int intel_bw_atomic_check(struct intel_atomic_state *state) unsigned int max_data_rate = adl_psf_bw(dev_priv, i); if (max_data_rate >= data_rate) - allowed_points |= REG_FIELD_PREP(ADLS_PSF_PT_MASK, BIT(i)); + psf_points |= BIT(i); drm_dbg_kms(&dev_priv->drm, "PSF GV point %d: max bw %d" " required %d\n", @@ -970,20 +1048,18 @@ int intel_bw_atomic_check(struct intel_atomic_state *state) * left, so if we couldn't - simply reject the configuration for obvious * reasons. */ - if ((allowed_points & ADLS_QGV_PT_MASK) == 0) { + if (qgv_points == 0) { drm_dbg_kms(&dev_priv->drm, "No QGV points provide sufficient memory" " bandwidth %d for display configuration(%d active planes).\n", data_rate, num_active_planes); return -EINVAL; } - if (num_psf_gv_points > 0) { - if ((allowed_points & ADLS_PSF_PT_MASK) == 0) { - drm_dbg_kms(&dev_priv->drm, "No PSF GV points provide sufficient memory" - " bandwidth %d for display configuration(%d active planes).\n", - data_rate, num_active_planes); - return -EINVAL; - } + if (num_psf_gv_points > 0 && psf_points == 0) { + drm_dbg_kms(&dev_priv->drm, "No PSF GV points provide sufficient memory" + " bandwidth %d for display configuration(%d active planes).\n", + data_rate, num_active_planes); + return -EINVAL; } /* @@ -992,16 +1068,18 @@ int intel_bw_atomic_check(struct intel_atomic_state *state) * cause. */ if (!intel_can_enable_sagv(dev_priv, new_bw_state)) { - allowed_points &= ADLS_PSF_PT_MASK; - allowed_points |= BIT(max_bw_point); + qgv_points = BIT(max_bw_point); drm_dbg_kms(&dev_priv->drm, "No SAGV, using single QGV point %d\n", max_bw_point); } + /* * We store the ones which need to be masked as that is what PCode * actually accepts as a parameter. */ - new_bw_state->qgv_points_mask = ~allowed_points & + new_bw_state->qgv_points_mask = + ~(ICL_PCODE_REQ_QGV_PT(qgv_points) | + ADLS_PCODE_REQ_PSF_PT(psf_points)) & icl_qgv_points_mask(dev_priv); /* diff --git a/drivers/gpu/drm/i915/display/intel_bw.h b/drivers/gpu/drm/i915/display/intel_bw.h index 0ceaed1c9656..cb7ee3a24a58 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.h +++ b/drivers/gpu/drm/i915/display/intel_bw.h @@ -17,7 +17,8 @@ struct intel_atomic_state; struct intel_crtc_state; struct intel_dbuf_bw { - int used_bw[I915_MAX_DBUF_SLICES]; + unsigned int max_bw[I915_MAX_DBUF_SLICES]; + u8 active_planes[I915_MAX_DBUF_SLICES]; }; struct intel_bw_state { @@ -40,10 +41,9 @@ struct intel_bw_state { */ u16 qgv_points_mask; + int min_cdclk[I915_MAX_PIPES]; unsigned int data_rate[I915_MAX_PIPES]; u8 num_active_planes[I915_MAX_PIPES]; - - int min_cdclk; }; #define to_intel_bw_state(x) container_of((x), struct intel_bw_state, base) @@ -64,7 +64,9 @@ void intel_bw_crtc_update(struct intel_bw_state *bw_state, const struct intel_crtc_state *crtc_state); int icl_pcode_restrict_qgv_points(struct drm_i915_private *dev_priv, u32 points_mask); -int intel_bw_calc_min_cdclk(struct intel_atomic_state *state); -int skl_bw_calc_min_cdclk(struct intel_atomic_state *state); +int intel_bw_calc_min_cdclk(struct intel_atomic_state *state, + bool *need_cdclk_calc); +int intel_bw_min_cdclk(struct drm_i915_private *i915, + const struct intel_bw_state *bw_state); #endif /* __INTEL_BW_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 8888fda8b701..b2017d8161b4 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -72,7 +72,6 @@ struct intel_cdclk_funcs { void (*set_cdclk)(struct drm_i915_private *i915, const struct intel_cdclk_config *cdclk_config, enum pipe pipe); - int (*bw_calc_min_cdclk)(struct intel_atomic_state *state); int (*modeset_calc_cdclk)(struct intel_cdclk_state *state); u8 (*calc_voltage_level)(int cdclk); }; @@ -83,12 +82,6 @@ void intel_cdclk_get_cdclk(struct drm_i915_private *dev_priv, dev_priv->cdclk_funcs->get_cdclk(dev_priv, cdclk_config); } -static int intel_cdclk_bw_calc_min_cdclk(struct intel_atomic_state *state) -{ - struct drm_i915_private *dev_priv = to_i915(state->base.dev); - return dev_priv->cdclk_funcs->bw_calc_min_cdclk(state); -} - static void intel_cdclk_set_cdclk(struct drm_i915_private *dev_priv, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -2325,13 +2318,6 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state) dev_priv->max_cdclk_freq)); } - if (min_cdclk > dev_priv->max_cdclk_freq) { - drm_dbg_kms(&dev_priv->drm, - "required cdclk (%d kHz) exceeds max (%d kHz)\n", - min_cdclk, dev_priv->max_cdclk_freq); - return -EINVAL; - } - return min_cdclk; } @@ -2339,7 +2325,7 @@ static int intel_compute_min_cdclk(struct intel_cdclk_state *cdclk_state) { struct intel_atomic_state *state = cdclk_state->base.state; struct drm_i915_private *dev_priv = to_i915(state->base.dev); - struct intel_bw_state *bw_state = NULL; + const struct intel_bw_state *bw_state; struct intel_crtc *crtc; struct intel_crtc_state *crtc_state; int min_cdclk, i; @@ -2352,10 +2338,6 @@ static int intel_compute_min_cdclk(struct intel_cdclk_state *cdclk_state) if (min_cdclk < 0) return min_cdclk; - bw_state = intel_atomic_get_bw_state(state); - if (IS_ERR(bw_state)) - return PTR_ERR(bw_state); - if (cdclk_state->min_cdclk[crtc->pipe] == min_cdclk) continue; @@ -2366,14 +2348,31 @@ static int intel_compute_min_cdclk(struct intel_cdclk_state *cdclk_state) return ret; } - min_cdclk = cdclk_state->force_min_cdclk; - for_each_pipe(dev_priv, pipe) { - min_cdclk = max(cdclk_state->min_cdclk[pipe], min_cdclk); + bw_state = intel_atomic_get_new_bw_state(state); + if (bw_state) { + min_cdclk = intel_bw_min_cdclk(dev_priv, bw_state); - if (!bw_state) - continue; + if (cdclk_state->bw_min_cdclk != min_cdclk) { + int ret; + + cdclk_state->bw_min_cdclk = min_cdclk; + + ret = intel_atomic_lock_global_state(&cdclk_state->base); + if (ret) + return ret; + } + } + + min_cdclk = max(cdclk_state->force_min_cdclk, + cdclk_state->bw_min_cdclk); + for_each_pipe(dev_priv, pipe) + min_cdclk = max(cdclk_state->min_cdclk[pipe], min_cdclk); - min_cdclk = max(bw_state->min_cdclk, min_cdclk); + if (min_cdclk > dev_priv->max_cdclk_freq) { + drm_dbg_kms(&dev_priv->drm, + "required cdclk (%d kHz) exceeds max (%d kHz)\n", + min_cdclk, dev_priv->max_cdclk_freq); + return -EINVAL; } return min_cdclk; @@ -2654,14 +2653,10 @@ intel_atomic_get_cdclk_state(struct intel_atomic_state *state) int intel_cdclk_atomic_check(struct intel_atomic_state *state, bool *need_cdclk_calc) { - struct drm_i915_private *i915 = to_i915(state->base.dev); const struct intel_cdclk_state *old_cdclk_state; const struct intel_cdclk_state *new_cdclk_state; struct intel_plane_state *plane_state; - struct intel_bw_state *new_bw_state; struct intel_plane *plane; - int min_cdclk = 0; - enum pipe pipe; int ret; int i; @@ -2676,6 +2671,10 @@ int intel_cdclk_atomic_check(struct intel_atomic_state *state, return ret; } + ret = intel_bw_calc_min_cdclk(state, need_cdclk_calc); + if (ret) + return ret; + old_cdclk_state = intel_atomic_get_old_cdclk_state(state); new_cdclk_state = intel_atomic_get_new_cdclk_state(state); @@ -2683,23 +2682,6 @@ int intel_cdclk_atomic_check(struct intel_atomic_state *state, old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk) *need_cdclk_calc = true; - ret = intel_cdclk_bw_calc_min_cdclk(state); - if (ret) - return ret; - - new_bw_state = intel_atomic_get_new_bw_state(state); - - if (!new_cdclk_state || !new_bw_state) - return 0; - - for_each_pipe(i915, pipe) { - min_cdclk = max(new_cdclk_state->min_cdclk[pipe], min_cdclk); - - /* Currently do this change only if we need to increase */ - if (new_bw_state->min_cdclk > min_cdclk) - *need_cdclk_calc = true; - } - return 0; } @@ -3072,7 +3054,6 @@ u32 intel_read_rawclk(struct drm_i915_private *dev_priv) static const struct intel_cdclk_funcs tgl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, - .bw_calc_min_cdclk = skl_bw_calc_min_cdclk, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = tgl_calc_voltage_level, }; @@ -3080,7 +3061,6 @@ static const struct intel_cdclk_funcs tgl_cdclk_funcs = { static const struct intel_cdclk_funcs ehl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, - .bw_calc_min_cdclk = skl_bw_calc_min_cdclk, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = ehl_calc_voltage_level, }; @@ -3088,7 +3068,6 @@ static const struct intel_cdclk_funcs ehl_cdclk_funcs = { static const struct intel_cdclk_funcs icl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, - .bw_calc_min_cdclk = skl_bw_calc_min_cdclk, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = icl_calc_voltage_level, }; @@ -3096,7 +3075,6 @@ static const struct intel_cdclk_funcs icl_cdclk_funcs = { static const struct intel_cdclk_funcs bxt_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, - .bw_calc_min_cdclk = skl_bw_calc_min_cdclk, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = bxt_calc_voltage_level, }; @@ -3104,53 +3082,45 @@ static const struct intel_cdclk_funcs bxt_cdclk_funcs = { static const struct intel_cdclk_funcs skl_cdclk_funcs = { .get_cdclk = skl_get_cdclk, .set_cdclk = skl_set_cdclk, - .bw_calc_min_cdclk = skl_bw_calc_min_cdclk, .modeset_calc_cdclk = skl_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs bdw_cdclk_funcs = { .get_cdclk = bdw_get_cdclk, .set_cdclk = bdw_set_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = bdw_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs chv_cdclk_funcs = { .get_cdclk = vlv_get_cdclk, .set_cdclk = chv_set_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = vlv_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs vlv_cdclk_funcs = { .get_cdclk = vlv_get_cdclk, .set_cdclk = vlv_set_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = vlv_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs hsw_cdclk_funcs = { .get_cdclk = hsw_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; /* SNB, IVB, 965G, 945G */ static const struct intel_cdclk_funcs fixed_400mhz_cdclk_funcs = { .get_cdclk = fixed_400mhz_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs ilk_cdclk_funcs = { .get_cdclk = fixed_450mhz_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs gm45_cdclk_funcs = { .get_cdclk = gm45_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; @@ -3158,7 +3128,6 @@ static const struct intel_cdclk_funcs gm45_cdclk_funcs = { static const struct intel_cdclk_funcs i965gm_cdclk_funcs = { .get_cdclk = i965gm_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; @@ -3166,19 +3135,16 @@ static const struct intel_cdclk_funcs i965gm_cdclk_funcs = { static const struct intel_cdclk_funcs pnv_cdclk_funcs = { .get_cdclk = pnv_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs g33_cdclk_funcs = { .get_cdclk = g33_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs i945gm_cdclk_funcs = { .get_cdclk = i945gm_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; @@ -3186,37 +3152,31 @@ static const struct intel_cdclk_funcs i945gm_cdclk_funcs = { static const struct intel_cdclk_funcs i915gm_cdclk_funcs = { .get_cdclk = i915gm_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs i915g_cdclk_funcs = { .get_cdclk = fixed_333mhz_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs i865g_cdclk_funcs = { .get_cdclk = fixed_266mhz_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs i85x_cdclk_funcs = { .get_cdclk = i85x_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs i845g_cdclk_funcs = { .get_cdclk = fixed_200mhz_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; static const struct intel_cdclk_funcs i830_cdclk_funcs = { .get_cdclk = fixed_133mhz_get_cdclk, - .bw_calc_min_cdclk = intel_bw_calc_min_cdclk, .modeset_calc_cdclk = fixed_modeset_calc_cdclk, }; diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.h b/drivers/gpu/drm/i915/display/intel_cdclk.h index df66f66fbad0..b535cf6a7d9e 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.h +++ b/drivers/gpu/drm/i915/display/intel_cdclk.h @@ -36,6 +36,8 @@ struct intel_cdclk_state { */ struct intel_cdclk_config actual; + /* minimum acceptable cdclk to satisfy bandwidth requirements */ + int bw_min_cdclk; /* minimum acceptable cdclk for each pipe */ int min_cdclk[I915_MAX_PIPES]; /* minimum acceptable voltage level for each pipe */ diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index e94ec57260f1..34128c9c635c 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -31,12 +31,21 @@ struct intel_color_funcs { int (*color_check)(struct intel_crtc_state *crtc_state); /* - * Program double buffered color management registers during - * vblank evasion. The registers should then latch during the - * next vblank start, alongside any other double buffered registers - * involved with the same commit. + * Program non-arming double buffered color management registers + * before vblank evasion. The registers should then latch after + * the arming register is written (by color_commit_arm()) during + * the next vblank start, alongside any other double buffered + * registers involved with the same commit. This hook is optional. + */ + void (*color_commit_noarm)(const struct intel_crtc_state *crtc_state); + /* + * Program arming double buffered color management registers + * during vblank evasion. The registers (and whatever other registers + * they arm that were written by color_commit_noarm) should then latch + * during the next vblank start, alongside any other double buffered + * registers involved with the same commit. */ - void (*color_commit)(const struct intel_crtc_state *crtc_state); + void (*color_commit_arm)(const struct intel_crtc_state *crtc_state); /* * Load LUTs (and other single buffered color management * registers). Will (hopefully) be called during the vblank @@ -337,15 +346,11 @@ static void ilk_load_csc_matrix(const struct intel_crtc_state *crtc_state) ilk_csc_coeff_identity, ilk_csc_off_zero); } - - intel_de_write_fw(dev_priv, PIPE_CSC_MODE(crtc->pipe), - crtc_state->csc_mode); } static void icl_load_csc_matrix(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); if (crtc_state->hw.ctm) { u16 coeff[9]; @@ -364,9 +369,6 @@ static void icl_load_csc_matrix(const struct intel_crtc_state *crtc_state) ilk_csc_coeff_limited_range, ilk_csc_postoff_limited_range); } - - intel_de_write_fw(dev_priv, PIPE_CSC_MODE(crtc->pipe), - crtc_state->csc_mode); } static void chv_load_cgm_csc(struct intel_crtc *crtc, @@ -491,7 +493,17 @@ static void icl_lut_multi_seg_pack(struct drm_color_lut *entry, u32 ldw, u32 udw REG_FIELD_GET(PAL_PREC_MULTI_SEG_BLUE_LDW_MASK, ldw); } -static void i9xx_color_commit(const struct intel_crtc_state *crtc_state) +static void icl_color_commit_noarm(const struct intel_crtc_state *crtc_state) +{ + icl_load_csc_matrix(crtc_state); +} + +static void ilk_color_commit_noarm(const struct intel_crtc_state *crtc_state) +{ + ilk_load_csc_matrix(crtc_state); +} + +static void i9xx_color_commit_arm(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); @@ -504,7 +516,7 @@ static void i9xx_color_commit(const struct intel_crtc_state *crtc_state) intel_de_write(dev_priv, PIPECONF(pipe), val); } -static void ilk_color_commit(const struct intel_crtc_state *crtc_state) +static void ilk_color_commit_arm(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); @@ -516,10 +528,11 @@ static void ilk_color_commit(const struct intel_crtc_state *crtc_state) val |= PIPECONF_GAMMA_MODE(crtc_state->gamma_mode); intel_de_write(dev_priv, PIPECONF(pipe), val); - ilk_load_csc_matrix(crtc_state); + intel_de_write_fw(dev_priv, PIPE_CSC_MODE(pipe), + crtc_state->csc_mode); } -static void hsw_color_commit(const struct intel_crtc_state *crtc_state) +static void hsw_color_commit_arm(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); @@ -527,10 +540,11 @@ static void hsw_color_commit(const struct intel_crtc_state *crtc_state) intel_de_write(dev_priv, GAMMA_MODE(crtc->pipe), crtc_state->gamma_mode); - ilk_load_csc_matrix(crtc_state); + intel_de_write_fw(dev_priv, PIPE_CSC_MODE(crtc->pipe), + crtc_state->csc_mode); } -static void skl_color_commit(const struct intel_crtc_state *crtc_state) +static void skl_color_commit_arm(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); @@ -551,10 +565,8 @@ static void skl_color_commit(const struct intel_crtc_state *crtc_state) intel_de_write(dev_priv, GAMMA_MODE(crtc->pipe), crtc_state->gamma_mode); - if (DISPLAY_VER(dev_priv) >= 11) - icl_load_csc_matrix(crtc_state); - else - ilk_load_csc_matrix(crtc_state); + intel_de_write_fw(dev_priv, PIPE_CSC_MODE(crtc->pipe), + crtc_state->csc_mode); } static void i9xx_load_lut_8(struct intel_crtc *crtc, @@ -1169,11 +1181,19 @@ void intel_color_load_luts(const struct intel_crtc_state *crtc_state) dev_priv->color_funcs->load_luts(crtc_state); } -void intel_color_commit(const struct intel_crtc_state *crtc_state) +void intel_color_commit_noarm(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); + + if (dev_priv->color_funcs->color_commit_noarm) + dev_priv->color_funcs->color_commit_noarm(crtc_state); +} + +void intel_color_commit_arm(const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); - dev_priv->color_funcs->color_commit(crtc_state); + dev_priv->color_funcs->color_commit_arm(crtc_state); } static bool intel_can_preload_luts(const struct intel_crtc_state *new_crtc_state) @@ -2132,70 +2152,77 @@ static void icl_read_luts(struct intel_crtc_state *crtc_state) static const struct intel_color_funcs chv_color_funcs = { .color_check = chv_color_check, - .color_commit = i9xx_color_commit, + .color_commit_arm = i9xx_color_commit_arm, .load_luts = chv_load_luts, .read_luts = chv_read_luts, }; static const struct intel_color_funcs i965_color_funcs = { .color_check = i9xx_color_check, - .color_commit = i9xx_color_commit, + .color_commit_arm = i9xx_color_commit_arm, .load_luts = i965_load_luts, .read_luts = i965_read_luts, }; static const struct intel_color_funcs i9xx_color_funcs = { .color_check = i9xx_color_check, - .color_commit = i9xx_color_commit, + .color_commit_arm = i9xx_color_commit_arm, .load_luts = i9xx_load_luts, .read_luts = i9xx_read_luts, }; static const struct intel_color_funcs icl_color_funcs = { .color_check = icl_color_check, - .color_commit = skl_color_commit, + .color_commit_noarm = icl_color_commit_noarm, + .color_commit_arm = skl_color_commit_arm, .load_luts = icl_load_luts, .read_luts = icl_read_luts, }; static const struct intel_color_funcs glk_color_funcs = { .color_check = glk_color_check, - .color_commit = skl_color_commit, + .color_commit_noarm = ilk_color_commit_noarm, + .color_commit_arm = skl_color_commit_arm, .load_luts = glk_load_luts, .read_luts = glk_read_luts, }; static const struct intel_color_funcs skl_color_funcs = { .color_check = ivb_color_check, - .color_commit = skl_color_commit, + .color_commit_noarm = ilk_color_commit_noarm, + .color_commit_arm = skl_color_commit_arm, .load_luts = bdw_load_luts, .read_luts = NULL, }; static const struct intel_color_funcs bdw_color_funcs = { .color_check = ivb_color_check, - .color_commit = hsw_color_commit, + .color_commit_noarm = ilk_color_commit_noarm, + .color_commit_arm = hsw_color_commit_arm, .load_luts = bdw_load_luts, .read_luts = NULL, }; static const struct intel_color_funcs hsw_color_funcs = { .color_check = ivb_color_check, - .color_commit = hsw_color_commit, + .color_commit_noarm = ilk_color_commit_noarm, + .color_commit_arm = hsw_color_commit_arm, .load_luts = ivb_load_luts, .read_luts = NULL, }; static const struct intel_color_funcs ivb_color_funcs = { .color_check = ivb_color_check, - .color_commit = ilk_color_commit, + .color_commit_noarm = ilk_color_commit_noarm, + .color_commit_arm = ilk_color_commit_arm, .load_luts = ivb_load_luts, .read_luts = NULL, }; static const struct intel_color_funcs ilk_color_funcs = { .color_check = ilk_color_check, - .color_commit = ilk_color_commit, + .color_commit_noarm = ilk_color_commit_noarm, + .color_commit_arm = ilk_color_commit_arm, .load_luts = ilk_load_luts, .read_luts = ilk_read_luts, }; diff --git a/drivers/gpu/drm/i915/display/intel_color.h b/drivers/gpu/drm/i915/display/intel_color.h index 173727aaa24d..fd873425e082 100644 --- a/drivers/gpu/drm/i915/display/intel_color.h +++ b/drivers/gpu/drm/i915/display/intel_color.h @@ -14,7 +14,8 @@ struct drm_property_blob; void intel_color_init(struct intel_crtc *crtc); int intel_color_check(struct intel_crtc_state *crtc_state); -void intel_color_commit(const struct intel_crtc_state *crtc_state); +void intel_color_commit_noarm(const struct intel_crtc_state *crtc_state); +void intel_color_commit_arm(const struct intel_crtc_state *crtc_state); void intel_color_load_luts(const struct intel_crtc_state *crtc_state); void intel_color_get_config(struct intel_crtc_state *crtc_state); int intel_color_get_gamma_bit_precision(const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_combo_phy.c b/drivers/gpu/drm/i915/display/intel_combo_phy.c index 4dfe77351b8b..64890f39c3cc 100644 --- a/drivers/gpu/drm/i915/display/intel_combo_phy.c +++ b/drivers/gpu/drm/i915/display/intel_combo_phy.c @@ -25,18 +25,29 @@ enum { }; static const struct icl_procmon { + const char *name; u32 dw1, dw9, dw10; } icl_procmon_values[] = { - [PROCMON_0_85V_DOT_0] = - { .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, }, - [PROCMON_0_95V_DOT_0] = - { .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, }, - [PROCMON_0_95V_DOT_1] = - { .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, }, - [PROCMON_1_05V_DOT_0] = - { .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, }, - [PROCMON_1_05V_DOT_1] = - { .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, }, + [PROCMON_0_85V_DOT_0] = { + .name = "0.85V dot0 (low-voltage)", + .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, + }, + [PROCMON_0_95V_DOT_0] = { + .name = "0.95V dot0", + .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, + }, + [PROCMON_0_95V_DOT_1] = { + .name = "0.95V dot1", + .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, + }, + [PROCMON_1_05V_DOT_0] = { + .name = "1.05V dot0", + .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, + }, + [PROCMON_1_05V_DOT_1] = { + .name = "1.05V dot1", + .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, + }, }; static const struct icl_procmon * @@ -113,6 +124,10 @@ static bool icl_verify_procmon_ref_values(struct drm_i915_private *dev_priv, procmon = icl_get_procmon_ref_values(dev_priv, phy); + drm_dbg_kms(&dev_priv->drm, + "Combo PHY %c Voltage/Process Info : %s\n", + phy_name(phy), procmon->name); + ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy), (0xff << 16) | 0xff, procmon->dw1); ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy), diff --git a/drivers/gpu/drm/i915/display/intel_connector.c b/drivers/gpu/drm/i915/display/intel_connector.c index c65f95a9a1ec..1dcc268927a2 100644 --- a/drivers/gpu/drm/i915/display/intel_connector.c +++ b/drivers/gpu/drm/i915/display/intel_connector.c @@ -54,6 +54,8 @@ int intel_connector_init(struct intel_connector *connector) __drm_atomic_helper_connector_reset(&connector->base, &conn_state->base); + INIT_LIST_HEAD(&connector->panel.fixed_modes); + return 0; } @@ -100,7 +102,7 @@ void intel_connector_destroy(struct drm_connector *connector) if (!IS_ERR_OR_NULL(intel_connector->edid)) kfree(intel_connector->edid); - intel_panel_fini(&intel_connector->panel); + intel_panel_fini(intel_connector); drm_connector_cleanup(connector); diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index 65827481c1b1..4442aa355f86 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -24,6 +24,7 @@ #include "intel_display_debugfs.h" #include "intel_display_trace.h" #include "intel_display_types.h" +#include "intel_drrs.h" #include "intel_dsi.h" #include "intel_pipe_crc.h" #include "intel_psr.h" @@ -367,6 +368,7 @@ int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) intel_color_init(crtc); + intel_crtc_drrs_init(crtc); intel_crtc_crc_init(crtc); cpu_latency_qos_add_request(&crtc->vblank_pm_qos, PM_QOS_DEFAULT_VALUE); @@ -485,6 +487,8 @@ void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state) intel_crtc_has_type(new_crtc_state, INTEL_OUTPUT_DSI); DEFINE_WAIT(wait); + intel_psr_lock(new_crtc_state); + if (new_crtc_state->do_async_flip) return; @@ -516,7 +520,7 @@ void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state) * VBL interrupts will start the PSR exit and prevent a PSR * re-entry as well. */ - intel_psr_wait_for_idle(new_crtc_state); + intel_psr_wait_for_idle_locked(new_crtc_state); local_irq_disable(); @@ -630,6 +634,8 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state) ktime_t end_vbl_time = ktime_get(); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + intel_psr_unlock(new_crtc_state); + if (new_crtc_state->do_async_flip) return; diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 2ade8fdd9bdd..8c80de877605 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -153,6 +153,11 @@ static int intel_check_cursor(struct intel_crtc_state *crtc_state, plane_state->uapi.src = src; plane_state->uapi.dst = dst; + /* final plane coordinates will be relative to the plane's pipe */ + drm_rect_translate(&plane_state->uapi.dst, + -crtc_state->pipe_src.x1, + -crtc_state->pipe_src.y1); + ret = intel_cursor_check_surface(plane_state); if (ret) return ret; @@ -255,7 +260,6 @@ static void i845_cursor_update_arm(struct intel_plane *plane, { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); u32 cntl = 0, base = 0, pos = 0, size = 0; - unsigned long irqflags; if (plane_state && plane_state->uapi.visible) { unsigned int width = drm_rect_width(&plane_state->uapi.dst); @@ -270,8 +274,6 @@ static void i845_cursor_update_arm(struct intel_plane *plane, pos = intel_cursor_position(plane_state); } - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - /* On these chipsets we can only modify the base/size/stride * whilst the cursor is disabled. */ @@ -290,8 +292,6 @@ static void i845_cursor_update_arm(struct intel_plane *plane, } else { intel_de_write_fw(dev_priv, CURPOS(PIPE_A), pos); } - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void i845_cursor_disable_arm(struct intel_plane *plane, @@ -492,7 +492,6 @@ static void i9xx_cursor_update_arm(struct intel_plane *plane, struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; u32 cntl = 0, base = 0, pos = 0, fbc_ctl = 0; - unsigned long irqflags; if (plane_state && plane_state->uapi.visible) { int width = drm_rect_width(&plane_state->uapi.dst); @@ -508,8 +507,6 @@ static void i9xx_cursor_update_arm(struct intel_plane *plane, pos = intel_cursor_position(plane_state); } - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - /* * On some platforms writing CURCNTR first will also * cause CURPOS to be armed by the CURBASE write. @@ -555,8 +552,6 @@ static void i9xx_cursor_update_arm(struct intel_plane *plane, intel_de_write_fw(dev_priv, CURPOS(pipe), pos); intel_de_write_fw(dev_priv, CURBASE(pipe), base); } - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void i9xx_cursor_disable_arm(struct intel_plane *plane, @@ -637,7 +632,7 @@ intel_legacy_cursor_update(struct drm_plane *_plane, * FIXME bigjoiner fastpath would be good */ if (!crtc_state->hw.active || intel_crtc_needs_modeset(crtc_state) || - crtc_state->update_pipe || crtc_state->bigjoiner) + crtc_state->update_pipe || crtc_state->bigjoiner_pipes) goto slow; /* @@ -715,6 +710,14 @@ intel_legacy_cursor_update(struct drm_plane *_plane, */ crtc_state->active_planes = new_crtc_state->active_planes; + /* + * Technically we should do a vblank evasion here to make + * sure all the cursor registers update on the same frame. + * For now just make sure the register writes happen as + * quickly as possible to minimize the race window. + */ + local_irq_disable(); + if (new_plane_state->uapi.visible) { intel_plane_update_noarm(plane, crtc_state, new_plane_state); intel_plane_update_arm(plane, crtc_state, new_plane_state); @@ -722,6 +725,8 @@ intel_legacy_cursor_update(struct drm_plane *_plane, intel_plane_disable_arm(plane, crtc_state); } + local_irq_enable(); + intel_plane_unpin_fb(old_plane_state); out_free: diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index e4260806c2a4..9e6fa59eabba 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -25,8 +25,10 @@ * */ +#include <linux/string_helpers.h> + +#include <drm/display/drm_scdc_helper.h> #include <drm/drm_privacy_screen_consumer.h> -#include <drm/drm_scdc_helper.h> #include "i915_drv.h" #include "intel_audio.h" @@ -38,12 +40,12 @@ #include "intel_ddi.h" #include "intel_ddi_buf_trans.h" #include "intel_de.h" +#include "intel_display_power.h" #include "intel_display_types.h" #include "intel_dp.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dpio_phy.h" -#include "intel_drrs.h" #include "intel_dsi.h" #include "intel_fdi.h" #include "intel_fifo_underrun.h" @@ -2152,7 +2154,7 @@ static void intel_dp_sink_set_msa_timing_par_ignore_state(struct intel_dp *intel enable ? DP_MSA_TIMING_PAR_IGNORE_EN : 0) <= 0) drm_dbg_kms(&i915->drm, "Failed to %s MSA_TIMING_PAR_IGNORE in the sink\n", - enabledisable(enable)); + str_enable_disable(enable)); } static void intel_dp_sink_set_fec_ready(struct intel_dp *intel_dp, @@ -2818,10 +2820,7 @@ static void intel_enable_ddi_dp(struct intel_atomic_state *state, if (!dig_port->lspcon.active || dig_port->dp.has_hdmi_sink) intel_dp_set_infoframes(encoder, true, crtc_state, conn_state); - intel_drrs_enable(intel_dp, crtc_state); - - if (crtc_state->has_audio) - intel_audio_codec_enable(encoder, crtc_state, conn_state); + intel_audio_codec_enable(encoder, crtc_state, conn_state); trans_port_sync_stop_link_train(state, encoder, crtc_state); } @@ -2915,8 +2914,7 @@ static void intel_enable_ddi_hdmi(struct intel_atomic_state *state, intel_de_write(dev_priv, DDI_BUF_CTL(port), dig_port->saved_port_bits | DDI_BUF_CTL_ENABLE); - if (crtc_state->has_audio) - intel_audio_codec_enable(encoder, crtc_state, conn_state); + intel_audio_codec_enable(encoder, crtc_state, conn_state); } static void intel_enable_ddi(struct intel_atomic_state *state, @@ -2957,11 +2955,8 @@ static void intel_disable_ddi_dp(struct intel_atomic_state *state, intel_dp->link_trained = false; - if (old_crtc_state->has_audio) - intel_audio_codec_disable(encoder, - old_crtc_state, old_conn_state); + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); - intel_drrs_disable(intel_dp, old_crtc_state); intel_psr_disable(intel_dp, old_crtc_state); intel_edp_backlight_off(old_conn_state); /* Disable the decompression in DP Sink */ @@ -2980,9 +2975,7 @@ static void intel_disable_ddi_hdmi(struct intel_atomic_state *state, struct drm_i915_private *i915 = to_i915(encoder->base.dev); struct drm_connector *connector = old_conn_state->connector; - if (old_crtc_state->has_audio) - intel_audio_codec_disable(encoder, - old_crtc_state, old_conn_state); + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); if (!intel_hdmi_handle_sink_scrambling(encoder, connector, false, false)) @@ -3011,12 +3004,9 @@ static void intel_ddi_update_pipe_dp(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - intel_ddi_set_dp_msa(crtc_state, conn_state); intel_dp_set_infoframes(encoder, true, crtc_state, conn_state); - intel_drrs_update(intel_dp, crtc_state); intel_backlight_update(state, encoder, crtc_state, conn_state); drm_connector_update_privacy_screen(conn_state); @@ -4308,6 +4298,13 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) return; } + if (intel_phy_is_snps(dev_priv, phy) && + dev_priv->snps_phy_failed_calibration & BIT(phy)) { + drm_dbg_kms(&dev_priv->drm, + "SNPS PHY %c failed to calibrate, proceeding anyway\n", + phy_name(phy)); + } + dig_port = kzalloc(sizeof(*dig_port), GFP_KERNEL); if (!dig_port) return; @@ -4368,7 +4365,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) encoder->get_power_domains = intel_ddi_get_power_domains; encoder->type = INTEL_OUTPUT_DDI; - encoder->power_domain = intel_port_to_power_domain(port); + encoder->power_domain = intel_display_power_ddi_lanes_domain(dev_priv, port); encoder->port = port; encoder->cloneable = 0; encoder->pipe_mask = ~0; @@ -4496,8 +4493,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) } drm_WARN_ON(&dev_priv->drm, port > PORT_I); - dig_port->ddi_io_power_domain = POWER_DOMAIN_PORT_DDI_A_IO + - port - PORT_A; + dig_port->ddi_io_power_domain = intel_display_power_ddi_io_domain(dev_priv, port); if (init_dp) { if (!intel_ddi_init_dp_connector(dig_port)) diff --git a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c index 934a9f9e7dab..85f58dd3df72 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c +++ b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c @@ -907,7 +907,7 @@ static const union intel_ddi_buf_trans_entry _adlp_combo_phy_trans_dp_hbr[] = { { .icl = { 0xA, 0x4C, 0x3F, 0x00, 0x00 } }, /* 500 500 0.0 */ { .icl = { 0xC, 0x73, 0x34, 0x00, 0x0B } }, /* 500 700 2.9 */ { .icl = { 0x6, 0x7F, 0x2F, 0x00, 0x10 } }, /* 500 900 5.1 */ - { .icl = { 0xC, 0x73, 0x3E, 0x00, 0x01 } }, /* 650 700 0.6 */ + { .icl = { 0xC, 0x7C, 0x3C, 0x00, 0x03 } }, /* 650 700 0.6 */ { .icl = { 0x6, 0x7F, 0x35, 0x00, 0x0A } }, /* 600 900 3.5 */ { .icl = { 0x6, 0x7F, 0x3F, 0x00, 0x00 } }, /* 900 900 0.0 */ }; @@ -921,7 +921,7 @@ static const union intel_ddi_buf_trans_entry _adlp_combo_phy_trans_dp_hbr2_hbr3[ /* NT mV Trans mV db */ { .icl = { 0xA, 0x35, 0x3F, 0x00, 0x00 } }, /* 350 350 0.0 */ { .icl = { 0xA, 0x4F, 0x37, 0x00, 0x08 } }, /* 350 500 3.1 */ - { .icl = { 0xC, 0x71, 0x2F, 0x00, 0x10 } }, /* 350 700 6.0 */ + { .icl = { 0xC, 0x71, 0x30, 0x00, 0x0F } }, /* 350 700 6.0 */ { .icl = { 0x6, 0x7F, 0x2B, 0x00, 0x14 } }, /* 350 900 8.2 */ { .icl = { 0xA, 0x4C, 0x3F, 0x00, 0x00 } }, /* 500 500 0.0 */ { .icl = { 0xC, 0x73, 0x34, 0x00, 0x0B } }, /* 500 700 2.9 */ @@ -931,19 +931,47 @@ static const union intel_ddi_buf_trans_entry _adlp_combo_phy_trans_dp_hbr2_hbr3[ { .icl = { 0x6, 0x7F, 0x3F, 0x00, 0x00 } }, /* 900 900 0.0 */ }; +static const union intel_ddi_buf_trans_entry _adlp_combo_phy_trans_edp_hbr2[] = { + /* NT mV Trans mV db */ + { .icl = { 0x4, 0x50, 0x38, 0x00, 0x07 } }, /* 200 200 0.0 */ + { .icl = { 0x4, 0x58, 0x35, 0x00, 0x0A } }, /* 200 250 1.9 */ + { .icl = { 0x4, 0x60, 0x34, 0x00, 0x0B } }, /* 200 300 3.5 */ + { .icl = { 0x4, 0x6A, 0x32, 0x00, 0x0D } }, /* 200 350 4.9 */ + { .icl = { 0x4, 0x5E, 0x38, 0x00, 0x07 } }, /* 250 250 0.0 */ + { .icl = { 0x4, 0x61, 0x36, 0x00, 0x09 } }, /* 250 300 1.6 */ + { .icl = { 0x4, 0x6B, 0x34, 0x00, 0x0B } }, /* 250 350 2.9 */ + { .icl = { 0x4, 0x69, 0x39, 0x00, 0x06 } }, /* 300 300 0.0 */ + { .icl = { 0x4, 0x73, 0x37, 0x00, 0x08 } }, /* 300 350 1.3 */ + { .icl = { 0x4, 0x7A, 0x38, 0x00, 0x07 } }, /* 350 350 0.0 */ +}; + +static const union intel_ddi_buf_trans_entry _adlp_combo_phy_trans_dp_hbr2_edp_hbr3[] = { + /* NT mV Trans mV db */ + { .icl = { 0xA, 0x35, 0x3F, 0x00, 0x00 } }, /* 350 350 0.0 */ + { .icl = { 0xA, 0x4F, 0x37, 0x00, 0x08 } }, /* 350 500 3.1 */ + { .icl = { 0xC, 0x71, 0x30, 0x00, 0x0f } }, /* 350 700 6.0 */ + { .icl = { 0x6, 0x7F, 0x2B, 0x00, 0x14 } }, /* 350 900 8.2 */ + { .icl = { 0xA, 0x4C, 0x3F, 0x00, 0x00 } }, /* 500 500 0.0 */ + { .icl = { 0xC, 0x73, 0x34, 0x00, 0x0B } }, /* 500 700 2.9 */ + { .icl = { 0x6, 0x7F, 0x2F, 0x00, 0x10 } }, /* 500 900 5.1 */ + { .icl = { 0xC, 0x6C, 0x3C, 0x00, 0x03 } }, /* 650 700 0.6 */ + { .icl = { 0x6, 0x7F, 0x35, 0x00, 0x0A } }, /* 600 900 3.5 */ + { .icl = { 0x6, 0x7F, 0x3F, 0x00, 0x00 } }, /* 900 900 0.0 */ +}; + static const struct intel_ddi_buf_trans adlp_combo_phy_trans_dp_hbr2_hbr3 = { .entries = _adlp_combo_phy_trans_dp_hbr2_hbr3, .num_entries = ARRAY_SIZE(_adlp_combo_phy_trans_dp_hbr2_hbr3), }; static const struct intel_ddi_buf_trans adlp_combo_phy_trans_edp_hbr3 = { - .entries = _icl_combo_phy_trans_dp_hbr2_edp_hbr3, - .num_entries = ARRAY_SIZE(_icl_combo_phy_trans_dp_hbr2_edp_hbr3), + .entries = _adlp_combo_phy_trans_dp_hbr2_edp_hbr3, + .num_entries = ARRAY_SIZE(_adlp_combo_phy_trans_dp_hbr2_edp_hbr3), }; static const struct intel_ddi_buf_trans adlp_combo_phy_trans_edp_up_to_hbr2 = { - .entries = _icl_combo_phy_trans_edp_hbr2, - .num_entries = ARRAY_SIZE(_icl_combo_phy_trans_edp_hbr2), + .entries = _adlp_combo_phy_trans_edp_hbr2, + .num_entries = ARRAY_SIZE(_adlp_combo_phy_trans_edp_hbr2), }; static const union intel_ddi_buf_trans_entry _adlp_dkl_phy_trans_dp_hbr[] = { @@ -1645,7 +1673,9 @@ void intel_ddi_buf_trans_init(struct intel_encoder *encoder) encoder->get_buf_trans = skl_get_buf_trans; } else if (IS_BROADWELL(i915)) { encoder->get_buf_trans = bdw_get_buf_trans; - } else { + } else if (IS_HASWELL(i915)) { encoder->get_buf_trans = hsw_get_buf_trans; + } else { + MISSING_CASE(INTEL_INFO(i915)->platform); } } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 7dfeb458aa65..806d50b302ab 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -32,13 +32,14 @@ #include <linux/module.h> #include <linux/dma-resv.h> #include <linux/slab.h> +#include <linux/string_helpers.h> #include <linux/vga_switcheroo.h> +#include <drm/display/drm_dp_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_uapi.h> #include <drm/drm_damage_helper.h> -#include <drm/dp/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> #include <drm/drm_plane_helper.h> @@ -50,6 +51,7 @@ #include "display/intel_crt.h" #include "display/intel_ddi.h" #include "display/intel_display_debugfs.h" +#include "display/intel_display_power.h" #include "display/intel_dp.h" #include "display/intel_dp_mst.h" #include "display/intel_dpll.h" @@ -76,6 +78,7 @@ #include "g4x_hdmi.h" #include "hsw_ips.h" #include "i915_drv.h" +#include "i915_utils.h" #include "icl_dsi.h" #include "intel_acpi.h" #include "intel_atomic.h" @@ -368,6 +371,11 @@ bool intel_crtc_is_bigjoiner_master(const struct intel_crtc_state *crtc_state) crtc->pipe == bigjoiner_master_pipe(crtc_state); } +static int intel_bigjoiner_num_pipes(const struct intel_crtc_state *crtc_state) +{ + return hweight8(crtc_state->bigjoiner_pipes); +} + struct intel_crtc *intel_master_crtc(const struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); @@ -400,7 +408,7 @@ static void wait_for_pipe_scanline_moving(struct intel_crtc *crtc, bool state) if (wait_for(pipe_scanline_is_moving(dev_priv, pipe) == state, 100)) drm_err(&dev_priv->drm, "pipe %c scanline %s wait timed out\n", - pipe_name(pipe), onoff(state)); + pipe_name(pipe), str_on_off(state)); } static void intel_wait_for_pipe_scanline_stopped(struct intel_crtc *crtc) @@ -456,7 +464,7 @@ void assert_transcoder(struct drm_i915_private *dev_priv, I915_STATE_WARN(cur_state != state, "transcoder %s assertion failure (expected %s, current %s)\n", transcoder_name(cpu_transcoder), - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } static void assert_plane(struct intel_plane *plane, bool state) @@ -468,7 +476,8 @@ static void assert_plane(struct intel_plane *plane, bool state) I915_STATE_WARN(cur_state != state, "%s assertion failure (expected %s, current %s)\n", - plane->base.name, onoff(state), onoff(cur_state)); + plane->base.name, str_on_off(state), + str_on_off(cur_state)); } #define assert_plane_enabled(p) assert_plane(p, true) @@ -517,16 +526,6 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv, expected_mask); } -enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - - if (HAS_PCH_LPT(dev_priv)) - return PIPE_A; - else - return crtc->pipe; -} - void intel_enable_transcoder(const struct intel_crtc_state *new_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); @@ -783,6 +782,9 @@ void intel_plane_disable_noatomic(struct intel_crtc *crtc, intel_set_plane_visible(crtc_state, plane_state, false); fixup_plane_bitmasks(crtc_state); crtc_state->data_rate[plane->id] = 0; + crtc_state->data_rate_y[plane->id] = 0; + crtc_state->rel_data_rate[plane->id] = 0; + crtc_state->rel_data_rate_y[plane->id] = 0; crtc_state->min_cdclk[plane->id] = 0; if ((crtc_state->active_planes & ~BIT(PLANE_CURSOR)) == 0 && @@ -1117,13 +1119,13 @@ static void ilk_pfit_enable(const struct intel_crtc_state *crtc_state) * e.g. x201. */ if (IS_IVYBRIDGE(dev_priv) || IS_HASWELL(dev_priv)) - intel_de_write(dev_priv, PF_CTL(pipe), PF_ENABLE | - PF_FILTER_MED_3x3 | PF_PIPE_SEL_IVB(pipe)); + intel_de_write_fw(dev_priv, PF_CTL(pipe), PF_ENABLE | + PF_FILTER_MED_3x3 | PF_PIPE_SEL_IVB(pipe)); else - intel_de_write(dev_priv, PF_CTL(pipe), PF_ENABLE | - PF_FILTER_MED_3x3); - intel_de_write(dev_priv, PF_WIN_POS(pipe), x << 16 | y); - intel_de_write(dev_priv, PF_WIN_SZ(pipe), width << 16 | height); + intel_de_write_fw(dev_priv, PF_CTL(pipe), PF_ENABLE | + PF_FILTER_MED_3x3); + intel_de_write_fw(dev_priv, PF_WIN_POS(pipe), x << 16 | y); + intel_de_write_fw(dev_priv, PF_WIN_SZ(pipe), width << 16 | height); } static void intel_crtc_dpms_overlay_disable(struct intel_crtc *crtc) @@ -1197,7 +1199,7 @@ static bool needs_async_flip_vtd_wa(const struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); - return crtc_state->uapi.async_flip && intel_vtd_active(i915) && + return crtc_state->uapi.async_flip && i915_vtd_active(i915) && (DISPLAY_VER(i915) == 9 || IS_BROADWELL(i915) || IS_HASWELL(i915)); } @@ -1232,7 +1234,6 @@ static void intel_post_plane_update(struct intel_atomic_state *state, hsw_ips_post_update(state, crtc); intel_fbc_post_update(state, crtc); - intel_drrs_page_flip(state, crtc); if (needs_async_flip_vtd_wa(old_crtc_state) && !needs_async_flip_vtd_wa(new_crtc_state)) @@ -1250,6 +1251,7 @@ static void intel_post_plane_update(struct intel_atomic_state *state, !needs_cursorclk_wa(new_crtc_state)) icl_wa_cursorclkgating(dev_priv, pipe, false); + intel_drrs_activate(new_crtc_state); } static void intel_crtc_enable_flip_done(struct intel_atomic_state *state, @@ -1327,6 +1329,8 @@ static void intel_pre_plane_update(struct intel_atomic_state *state, intel_atomic_get_new_crtc_state(state, crtc); enum pipe pipe = crtc->pipe; + intel_drrs_deactivate(old_crtc_state); + intel_psr_pre_plane_update(state, crtc); if (hsw_ips_pre_update(state, crtc)) @@ -1777,7 +1781,8 @@ static void ilk_crtc_enable(struct intel_atomic_state *state, * clocks enabled */ intel_color_load_luts(new_crtc_state); - intel_color_commit(new_crtc_state); + intel_color_commit_noarm(new_crtc_state); + intel_color_commit_arm(new_crtc_state); /* update DSPCNTR to configure gamma for pipe bottom color */ intel_disable_primary_plane(new_crtc_state); @@ -1822,29 +1827,6 @@ static void glk_pipe_scaler_clock_gating_wa(struct drm_i915_private *dev_priv, intel_de_write(dev_priv, CLKGATE_DIS_PSL(pipe), val); } -static void icl_pipe_mbus_enable(struct intel_crtc *crtc, bool joined_mbus) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum pipe pipe = crtc->pipe; - u32 val; - - /* Wa_22010947358:adl-p */ - if (IS_ALDERLAKE_P(dev_priv)) - val = joined_mbus ? MBUS_DBOX_A_CREDIT(6) : MBUS_DBOX_A_CREDIT(4); - else - val = MBUS_DBOX_A_CREDIT(2); - - if (DISPLAY_VER(dev_priv) >= 12) { - val |= MBUS_DBOX_BW_CREDIT(2); - val |= MBUS_DBOX_B_CREDIT(12); - } else { - val |= MBUS_DBOX_BW_CREDIT(1); - val |= MBUS_DBOX_B_CREDIT(8); - } - - intel_de_write(dev_priv, PIPE_MBUS_DBOX_CTL(pipe), val); -} - static void hsw_set_linetime_wm(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); @@ -1864,7 +1846,7 @@ static void hsw_set_frame_start_delay(const struct intel_crtc_state *crtc_state) val = intel_de_read(dev_priv, reg); val &= ~HSW_FRAME_START_DELAY_MASK; - val |= HSW_FRAME_START_DELAY(dev_priv->framestart_delay - 1); + val |= HSW_FRAME_START_DELAY(crtc_state->framestart_delay - 1); intel_de_write(dev_priv, reg, val); } @@ -1926,7 +1908,7 @@ static void hsw_crtc_enable(struct intel_atomic_state *state, if (drm_WARN_ON(&dev_priv->drm, crtc->active)) return; - if (!new_crtc_state->bigjoiner) { + if (!new_crtc_state->bigjoiner_pipes) { intel_encoders_pre_pll_enable(state, crtc); if (new_crtc_state->shared_dpll) @@ -1968,7 +1950,8 @@ static void hsw_crtc_enable(struct intel_atomic_state *state, * clocks enabled */ intel_color_load_luts(new_crtc_state); - intel_color_commit(new_crtc_state); + intel_color_commit_noarm(new_crtc_state); + intel_color_commit_arm(new_crtc_state); /* update DSPCNTR to configure gamma/csc for pipe bottom color */ if (DISPLAY_VER(dev_priv) < 9) intel_disable_primary_plane(new_crtc_state); @@ -1980,13 +1963,6 @@ static void hsw_crtc_enable(struct intel_atomic_state *state, intel_initial_watermarks(state, crtc); - if (DISPLAY_VER(dev_priv) >= 11) { - const struct intel_dbuf_state *dbuf_state = - intel_atomic_get_new_dbuf_state(state); - - icl_pipe_mbus_enable(crtc, dbuf_state->joined_mbus); - } - if (intel_crtc_is_bigjoiner_slave(new_crtc_state)) intel_crtc_vblank_on(new_crtc_state); @@ -2021,9 +1997,9 @@ void ilk_pfit_disable(const struct intel_crtc_state *old_crtc_state) if (!old_crtc_state->pch_pfit.enabled) return; - intel_de_write(dev_priv, PF_CTL(pipe), 0); - intel_de_write(dev_priv, PF_WIN_POS(pipe), 0); - intel_de_write(dev_priv, PF_WIN_SZ(pipe), 0); + intel_de_write_fw(dev_priv, PF_CTL(pipe), 0); + intel_de_write_fw(dev_priv, PF_WIN_POS(pipe), 0); + intel_de_write_fw(dev_priv, PF_WIN_SZ(pipe), 0); } static void ilk_crtc_disable(struct intel_atomic_state *state, @@ -2182,153 +2158,82 @@ enum tc_port intel_port_to_tc(struct drm_i915_private *dev_priv, enum port port) return TC_PORT_1 + port - PORT_C; } -enum intel_display_power_domain intel_port_to_power_domain(enum port port) -{ - switch (port) { - case PORT_A: - return POWER_DOMAIN_PORT_DDI_A_LANES; - case PORT_B: - return POWER_DOMAIN_PORT_DDI_B_LANES; - case PORT_C: - return POWER_DOMAIN_PORT_DDI_C_LANES; - case PORT_D: - return POWER_DOMAIN_PORT_DDI_D_LANES; - case PORT_E: - return POWER_DOMAIN_PORT_DDI_E_LANES; - case PORT_F: - return POWER_DOMAIN_PORT_DDI_F_LANES; - case PORT_G: - return POWER_DOMAIN_PORT_DDI_G_LANES; - case PORT_H: - return POWER_DOMAIN_PORT_DDI_H_LANES; - case PORT_I: - return POWER_DOMAIN_PORT_DDI_I_LANES; - default: - MISSING_CASE(port); - return POWER_DOMAIN_PORT_OTHER; - } -} - enum intel_display_power_domain intel_aux_power_domain(struct intel_digital_port *dig_port) { - if (intel_tc_port_in_tbt_alt_mode(dig_port)) { - switch (dig_port->aux_ch) { - case AUX_CH_C: - return POWER_DOMAIN_AUX_C_TBT; - case AUX_CH_D: - return POWER_DOMAIN_AUX_D_TBT; - case AUX_CH_E: - return POWER_DOMAIN_AUX_E_TBT; - case AUX_CH_F: - return POWER_DOMAIN_AUX_F_TBT; - case AUX_CH_G: - return POWER_DOMAIN_AUX_G_TBT; - case AUX_CH_H: - return POWER_DOMAIN_AUX_H_TBT; - case AUX_CH_I: - return POWER_DOMAIN_AUX_I_TBT; - default: - MISSING_CASE(dig_port->aux_ch); - return POWER_DOMAIN_AUX_C_TBT; - } - } + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - return intel_legacy_aux_to_power_domain(dig_port->aux_ch); -} + if (intel_tc_port_in_tbt_alt_mode(dig_port)) + return intel_display_power_tbt_aux_domain(i915, dig_port->aux_ch); -/* - * Converts aux_ch to power_domain without caring about TBT ports for that use - * intel_aux_power_domain() - */ -enum intel_display_power_domain -intel_legacy_aux_to_power_domain(enum aux_ch aux_ch) -{ - switch (aux_ch) { - case AUX_CH_A: - return POWER_DOMAIN_AUX_A; - case AUX_CH_B: - return POWER_DOMAIN_AUX_B; - case AUX_CH_C: - return POWER_DOMAIN_AUX_C; - case AUX_CH_D: - return POWER_DOMAIN_AUX_D; - case AUX_CH_E: - return POWER_DOMAIN_AUX_E; - case AUX_CH_F: - return POWER_DOMAIN_AUX_F; - case AUX_CH_G: - return POWER_DOMAIN_AUX_G; - case AUX_CH_H: - return POWER_DOMAIN_AUX_H; - case AUX_CH_I: - return POWER_DOMAIN_AUX_I; - default: - MISSING_CASE(aux_ch); - return POWER_DOMAIN_AUX_A; - } + return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch); } -static u64 get_crtc_power_domains(struct intel_crtc_state *crtc_state) +static void get_crtc_power_domains(struct intel_crtc_state *crtc_state, + struct intel_power_domain_mask *mask) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; struct drm_encoder *encoder; enum pipe pipe = crtc->pipe; - u64 mask; + + bitmap_zero(mask->bits, POWER_DOMAIN_NUM); if (!crtc_state->hw.active) - return 0; + return; - mask = BIT_ULL(POWER_DOMAIN_PIPE(pipe)); - mask |= BIT_ULL(POWER_DOMAIN_TRANSCODER(cpu_transcoder)); + set_bit(POWER_DOMAIN_PIPE(pipe), mask->bits); + set_bit(POWER_DOMAIN_TRANSCODER(cpu_transcoder), mask->bits); if (crtc_state->pch_pfit.enabled || crtc_state->pch_pfit.force_thru) - mask |= BIT_ULL(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe)); + set_bit(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe), mask->bits); drm_for_each_encoder_mask(encoder, &dev_priv->drm, crtc_state->uapi.encoder_mask) { struct intel_encoder *intel_encoder = to_intel_encoder(encoder); - mask |= BIT_ULL(intel_encoder->power_domain); + set_bit(intel_encoder->power_domain, mask->bits); } if (HAS_DDI(dev_priv) && crtc_state->has_audio) - mask |= BIT_ULL(POWER_DOMAIN_AUDIO_MMIO); + set_bit(POWER_DOMAIN_AUDIO_MMIO, mask->bits); if (crtc_state->shared_dpll) - mask |= BIT_ULL(POWER_DOMAIN_DISPLAY_CORE); + set_bit(POWER_DOMAIN_DISPLAY_CORE, mask->bits); if (crtc_state->dsc.compression_enable) - mask |= BIT_ULL(intel_dsc_power_domain(crtc, cpu_transcoder)); - - return mask; + set_bit(intel_dsc_power_domain(crtc, cpu_transcoder), mask->bits); } -static u64 -modeset_get_crtc_power_domains(struct intel_crtc_state *crtc_state) +static void +modeset_get_crtc_power_domains(struct intel_crtc_state *crtc_state, + struct intel_power_domain_mask *old_domains) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum intel_display_power_domain domain; - u64 domains, new_domains, old_domains; + struct intel_power_domain_mask domains, new_domains; - domains = get_crtc_power_domains(crtc_state); + get_crtc_power_domains(crtc_state, &domains); - new_domains = domains & ~crtc->enabled_power_domains.mask; - old_domains = crtc->enabled_power_domains.mask & ~domains; + bitmap_andnot(new_domains.bits, + domains.bits, + crtc->enabled_power_domains.mask.bits, + POWER_DOMAIN_NUM); + bitmap_andnot(old_domains->bits, + crtc->enabled_power_domains.mask.bits, + domains.bits, + POWER_DOMAIN_NUM); - for_each_power_domain(domain, new_domains) + for_each_power_domain(domain, &new_domains) intel_display_power_get_in_set(dev_priv, &crtc->enabled_power_domains, domain); - - return old_domains; } static void modeset_put_crtc_power_domains(struct intel_crtc *crtc, - u64 domains) + struct intel_power_domain_mask *domains) { intel_display_power_put_mask_in_set(to_i915(crtc->base.dev), &crtc->enabled_power_domains, @@ -2388,7 +2293,8 @@ static void valleyview_crtc_enable(struct intel_atomic_state *state, i9xx_pfit_enable(new_crtc_state); intel_color_load_luts(new_crtc_state); - intel_color_commit(new_crtc_state); + intel_color_commit_noarm(new_crtc_state); + intel_color_commit_arm(new_crtc_state); /* update DSPCNTR to configure gamma for pipe bottom color */ intel_disable_primary_plane(new_crtc_state); @@ -2427,7 +2333,8 @@ static void i9xx_crtc_enable(struct intel_atomic_state *state, i9xx_pfit_enable(new_crtc_state); intel_color_load_luts(new_crtc_state); - intel_color_commit(new_crtc_state); + intel_color_commit_noarm(new_crtc_state); + intel_color_commit_arm(new_crtc_state); /* update DSPCNTR to configure gamma for pipe bottom color */ intel_disable_primary_plane(new_crtc_state); @@ -2683,8 +2590,8 @@ static u32 ilk_pipe_pixel_rate(const struct intel_crtc_state *crtc_state) return pixel_rate; drm_rect_init(&src, 0, 0, - crtc_state->pipe_src_w << 16, - crtc_state->pipe_src_h << 16); + drm_rect_width(&crtc_state->pipe_src) << 16, + drm_rect_height(&crtc_state->pipe_src) << 16); return intel_adjusted_rate(&src, &crtc_state->pch_pfit.dst, pixel_rate); @@ -2724,58 +2631,81 @@ static void intel_crtc_compute_pixel_rate(struct intel_crtc_state *crtc_state) ilk_pipe_pixel_rate(crtc_state); } +static void intel_bigjoiner_adjust_timings(const struct intel_crtc_state *crtc_state, + struct drm_display_mode *mode) +{ + int num_pipes = intel_bigjoiner_num_pipes(crtc_state); + + if (num_pipes < 2) + return; + + mode->crtc_clock /= num_pipes; + mode->crtc_hdisplay /= num_pipes; + mode->crtc_hblank_start /= num_pipes; + mode->crtc_hblank_end /= num_pipes; + mode->crtc_hsync_start /= num_pipes; + mode->crtc_hsync_end /= num_pipes; + mode->crtc_htotal /= num_pipes; +} + +static void intel_splitter_adjust_timings(const struct intel_crtc_state *crtc_state, + struct drm_display_mode *mode) +{ + int overlap = crtc_state->splitter.pixel_overlap; + int n = crtc_state->splitter.link_count; + + if (!crtc_state->splitter.enable) + return; + + /* + * eDP MSO uses segment timings from EDID for transcoder + * timings, but full mode for everything else. + * + * h_full = (h_segment - pixel_overlap) * link_count + */ + mode->crtc_hdisplay = (mode->crtc_hdisplay - overlap) * n; + mode->crtc_hblank_start = (mode->crtc_hblank_start - overlap) * n; + mode->crtc_hblank_end = (mode->crtc_hblank_end - overlap) * n; + mode->crtc_hsync_start = (mode->crtc_hsync_start - overlap) * n; + mode->crtc_hsync_end = (mode->crtc_hsync_end - overlap) * n; + mode->crtc_htotal = (mode->crtc_htotal - overlap) * n; + mode->crtc_clock *= n; +} + static void intel_crtc_readout_derived_state(struct intel_crtc_state *crtc_state) { struct drm_display_mode *mode = &crtc_state->hw.mode; struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode; struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + /* + * Start with the adjusted_mode crtc timings, which + * have been filled with the transcoder timings. + */ drm_mode_copy(pipe_mode, adjusted_mode); - if (crtc_state->bigjoiner) { - /* - * transcoder is programmed to the full mode, - * but pipe timings are half of the transcoder mode - */ - pipe_mode->crtc_hdisplay /= 2; - pipe_mode->crtc_hblank_start /= 2; - pipe_mode->crtc_hblank_end /= 2; - pipe_mode->crtc_hsync_start /= 2; - pipe_mode->crtc_hsync_end /= 2; - pipe_mode->crtc_htotal /= 2; - pipe_mode->crtc_clock /= 2; - } + /* Expand MSO per-segment transcoder timings to full */ + intel_splitter_adjust_timings(crtc_state, pipe_mode); - if (crtc_state->splitter.enable) { - int n = crtc_state->splitter.link_count; - int overlap = crtc_state->splitter.pixel_overlap; + /* + * We want the full numbers in adjusted_mode normal timings, + * adjusted_mode crtc timings are left with the raw transcoder + * timings. + */ + intel_mode_from_crtc_timings(adjusted_mode, pipe_mode); - /* - * eDP MSO uses segment timings from EDID for transcoder - * timings, but full mode for everything else. - * - * h_full = (h_segment - pixel_overlap) * link_count - */ - pipe_mode->crtc_hdisplay = (pipe_mode->crtc_hdisplay - overlap) * n; - pipe_mode->crtc_hblank_start = (pipe_mode->crtc_hblank_start - overlap) * n; - pipe_mode->crtc_hblank_end = (pipe_mode->crtc_hblank_end - overlap) * n; - pipe_mode->crtc_hsync_start = (pipe_mode->crtc_hsync_start - overlap) * n; - pipe_mode->crtc_hsync_end = (pipe_mode->crtc_hsync_end - overlap) * n; - pipe_mode->crtc_htotal = (pipe_mode->crtc_htotal - overlap) * n; - pipe_mode->crtc_clock *= n; - - intel_mode_from_crtc_timings(pipe_mode, pipe_mode); - intel_mode_from_crtc_timings(adjusted_mode, pipe_mode); - } else { - intel_mode_from_crtc_timings(pipe_mode, pipe_mode); - intel_mode_from_crtc_timings(adjusted_mode, adjusted_mode); - } + /* Populate the "user" mode with full numbers */ + drm_mode_copy(mode, pipe_mode); + intel_mode_from_crtc_timings(mode, mode); + mode->hdisplay = drm_rect_width(&crtc_state->pipe_src) * + (intel_bigjoiner_num_pipes(crtc_state) ?: 1); + mode->vdisplay = drm_rect_height(&crtc_state->pipe_src); - intel_crtc_compute_pixel_rate(crtc_state); + /* Derive per-pipe timings in case bigjoiner is used */ + intel_bigjoiner_adjust_timings(crtc_state, pipe_mode); + intel_mode_from_crtc_timings(pipe_mode, pipe_mode); - drm_mode_copy(mode, adjusted_mode); - mode->hdisplay = crtc_state->pipe_src_w << crtc_state->bigjoiner; - mode->vdisplay = crtc_state->pipe_src_h; + intel_crtc_compute_pixel_rate(crtc_state); } static void intel_encoder_get_config(struct intel_encoder *encoder, @@ -2786,44 +2716,77 @@ static void intel_encoder_get_config(struct intel_encoder *encoder, intel_crtc_readout_derived_state(crtc_state); } -static int intel_crtc_compute_config(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config) +static void intel_bigjoiner_compute_pipe_src(struct intel_crtc_state *crtc_state) { - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct drm_display_mode *pipe_mode = &pipe_config->hw.pipe_mode; - int clock_limit = dev_priv->max_dotclk_freq; + int num_pipes = intel_bigjoiner_num_pipes(crtc_state); + int width, height; - drm_mode_copy(pipe_mode, &pipe_config->hw.adjusted_mode); + if (num_pipes < 2) + return; - /* Adjust pipe_mode for bigjoiner, with half the horizontal mode */ - if (pipe_config->bigjoiner) { - pipe_mode->crtc_clock /= 2; - pipe_mode->crtc_hdisplay /= 2; - pipe_mode->crtc_hblank_start /= 2; - pipe_mode->crtc_hblank_end /= 2; - pipe_mode->crtc_hsync_start /= 2; - pipe_mode->crtc_hsync_end /= 2; - pipe_mode->crtc_htotal /= 2; - pipe_config->pipe_src_w /= 2; - } + width = drm_rect_width(&crtc_state->pipe_src); + height = drm_rect_height(&crtc_state->pipe_src); + + drm_rect_init(&crtc_state->pipe_src, 0, 0, + width / num_pipes, height); +} + +static int intel_crtc_compute_pipe_src(struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + + intel_bigjoiner_compute_pipe_src(crtc_state); - if (pipe_config->splitter.enable) { - int n = pipe_config->splitter.link_count; - int overlap = pipe_config->splitter.pixel_overlap; + /* + * Pipe horizontal size must be even in: + * - DVO ganged mode + * - LVDS dual channel mode + * - Double wide pipe + */ + if (drm_rect_width(&crtc_state->pipe_src) & 1) { + if (crtc_state->double_wide) { + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s] Odd pipe source width not supported with double wide pipe\n", + crtc->base.base.id, crtc->base.name); + return -EINVAL; + } - pipe_mode->crtc_hdisplay = (pipe_mode->crtc_hdisplay - overlap) * n; - pipe_mode->crtc_hblank_start = (pipe_mode->crtc_hblank_start - overlap) * n; - pipe_mode->crtc_hblank_end = (pipe_mode->crtc_hblank_end - overlap) * n; - pipe_mode->crtc_hsync_start = (pipe_mode->crtc_hsync_start - overlap) * n; - pipe_mode->crtc_hsync_end = (pipe_mode->crtc_hsync_end - overlap) * n; - pipe_mode->crtc_htotal = (pipe_mode->crtc_htotal - overlap) * n; - pipe_mode->crtc_clock *= n; + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && + intel_is_dual_link_lvds(i915)) { + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s] Odd pipe source width not supported with dual link LVDS\n", + crtc->base.base.id, crtc->base.name); + return -EINVAL; + } } + return 0; +} + +static int intel_crtc_compute_pipe_mode(struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode; + int clock_limit = i915->max_dotclk_freq; + + /* + * Start with the adjusted_mode crtc timings, which + * have been filled with the transcoder timings. + */ + drm_mode_copy(pipe_mode, adjusted_mode); + + /* Expand MSO per-segment transcoder timings to full */ + intel_splitter_adjust_timings(crtc_state, pipe_mode); + + /* Derive per-pipe timings in case bigjoiner is used */ + intel_bigjoiner_adjust_timings(crtc_state, pipe_mode); intel_mode_from_crtc_timings(pipe_mode, pipe_mode); - if (DISPLAY_VER(dev_priv) < 4) { - clock_limit = dev_priv->max_cdclk_freq * 9 / 10; + if (DISPLAY_VER(i915) < 4) { + clock_limit = i915->max_cdclk_freq * 9 / 10; /* * Enable double wide mode when the dot clock @@ -2831,44 +2794,40 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc, */ if (intel_crtc_supports_double_wide(crtc) && pipe_mode->crtc_clock > clock_limit) { - clock_limit = dev_priv->max_dotclk_freq; - pipe_config->double_wide = true; + clock_limit = i915->max_dotclk_freq; + crtc_state->double_wide = true; } } if (pipe_mode->crtc_clock > clock_limit) { - drm_dbg_kms(&dev_priv->drm, - "requested pixel clock (%d kHz) too high (max: %d kHz, double wide: %s)\n", + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s] requested pixel clock (%d kHz) too high (max: %d kHz, double wide: %s)\n", + crtc->base.base.id, crtc->base.name, pipe_mode->crtc_clock, clock_limit, - yesno(pipe_config->double_wide)); + str_yes_no(crtc_state->double_wide)); return -EINVAL; } - /* - * Pipe horizontal size must be even in: - * - DVO ganged mode - * - LVDS dual channel mode - * - Double wide pipe - */ - if (pipe_config->pipe_src_w & 1) { - if (pipe_config->double_wide) { - drm_dbg_kms(&dev_priv->drm, - "Odd pipe source width not supported with double wide pipe\n"); - return -EINVAL; - } + return 0; +} - if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_LVDS) && - intel_is_dual_link_lvds(dev_priv)) { - drm_dbg_kms(&dev_priv->drm, - "Odd pipe source width not supported with dual link LVDS\n"); - return -EINVAL; - } - } +static int intel_crtc_compute_config(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + int ret; - intel_crtc_compute_pixel_rate(pipe_config); + ret = intel_crtc_compute_pipe_src(crtc_state); + if (ret) + return ret; - if (pipe_config->has_pch_encoder) - return ilk_fdi_compute_config(crtc, pipe_config); + ret = intel_crtc_compute_pipe_mode(crtc_state); + if (ret) + return ret; + + intel_crtc_compute_pixel_rate(crtc_state); + + if (crtc_state->has_pch_encoder) + return ilk_fdi_compute_config(crtc, crtc_state); return 0; } @@ -2941,8 +2900,8 @@ static void intel_panel_sanitize_ssc(struct drm_i915_private *dev_priv) if (dev_priv->vbt.lvds_use_ssc != bios_lvds_use_ssc) { drm_dbg_kms(&dev_priv->drm, "SSC %s by BIOS, overriding VBT which says %s\n", - enableddisabled(bios_lvds_use_ssc), - enableddisabled(dev_priv->vbt.lvds_use_ssc)); + str_enabled_disabled(bios_lvds_use_ssc), + str_enabled_disabled(dev_priv->vbt.lvds_use_ssc)); dev_priv->vbt.lvds_use_ssc = bios_lvds_use_ssc; } } @@ -3072,14 +3031,15 @@ static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + int width = drm_rect_width(&crtc_state->pipe_src); + int height = drm_rect_height(&crtc_state->pipe_src); enum pipe pipe = crtc->pipe; /* pipesrc controls the size that is scaled from, which should * always be the user's requested size. */ intel_de_write(dev_priv, PIPESRC(pipe), - PIPESRC_WIDTH(crtc_state->pipe_src_w - 1) | - PIPESRC_HEIGHT(crtc_state->pipe_src_h - 1)); + PIPESRC_WIDTH(width - 1) | PIPESRC_HEIGHT(height - 1)); } static bool intel_pipe_is_interlaced(const struct intel_crtc_state *crtc_state) @@ -3142,6 +3102,23 @@ static void intel_get_transcoder_timings(struct intel_crtc *crtc, } } +static void intel_bigjoiner_adjust_pipe_src(struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + int num_pipes = intel_bigjoiner_num_pipes(crtc_state); + enum pipe master_pipe, pipe = crtc->pipe; + int width; + + if (num_pipes < 2) + return; + + master_pipe = bigjoiner_master_pipe(crtc_state); + width = drm_rect_width(&crtc_state->pipe_src); + + drm_rect_translate_to(&crtc_state->pipe_src, + (pipe - master_pipe) * width, 0); +} + static void intel_get_pipe_src_size(struct intel_crtc *crtc, struct intel_crtc_state *pipe_config) { @@ -3150,8 +3127,12 @@ static void intel_get_pipe_src_size(struct intel_crtc *crtc, u32 tmp; tmp = intel_de_read(dev_priv, PIPESRC(crtc->pipe)); - pipe_config->pipe_src_w = REG_FIELD_GET(PIPESRC_WIDTH_MASK, tmp) + 1; - pipe_config->pipe_src_h = REG_FIELD_GET(PIPESRC_HEIGHT_MASK, tmp) + 1; + + drm_rect_init(&pipe_config->pipe_src, 0, 0, + REG_FIELD_GET(PIPESRC_WIDTH_MASK, tmp) + 1, + REG_FIELD_GET(PIPESRC_HEIGHT_MASK, tmp) + 1); + + intel_bigjoiner_adjust_pipe_src(pipe_config); } static void i9xx_set_pipeconf(const struct intel_crtc_state *crtc_state) @@ -3207,7 +3188,7 @@ static void i9xx_set_pipeconf(const struct intel_crtc_state *crtc_state) pipeconf |= PIPECONF_GAMMA_MODE(crtc_state->gamma_mode); - pipeconf |= PIPECONF_FRAME_START_DELAY(dev_priv->framestart_delay - 1); + pipeconf |= PIPECONF_FRAME_START_DELAY(crtc_state->framestart_delay - 1); intel_de_write(dev_priv, PIPECONF(crtc->pipe), pipeconf); intel_de_posting_read(dev_priv, PIPECONF(crtc->pipe)); @@ -3397,6 +3378,8 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, pipe_config->gamma_mode = REG_FIELD_GET(PIPECONF_GAMMA_MODE_MASK_I9XX, tmp); + pipe_config->framestart_delay = REG_FIELD_GET(PIPECONF_FRAME_START_DELAY_MASK, tmp) + 1; + if (IS_CHERRYVIEW(dev_priv)) pipe_config->cgm_mode = intel_de_read(dev_priv, CGM_PIPE_MODE(crtc->pipe)); @@ -3522,7 +3505,8 @@ static void ilk_set_pipeconf(const struct intel_crtc_state *crtc_state) val |= PIPECONF_GAMMA_MODE(crtc_state->gamma_mode); - val |= PIPECONF_FRAME_START_DELAY(dev_priv->framestart_delay - 1); + val |= PIPECONF_FRAME_START_DELAY(crtc_state->framestart_delay - 1); + val |= PIPECONF_MSA_TIMING_DELAY(crtc_state->msa_timing_delay); intel_de_write(dev_priv, PIPECONF(pipe), val); intel_de_posting_read(dev_priv, PIPECONF(pipe)); @@ -3554,12 +3538,8 @@ static void hsw_set_transconf(const struct intel_crtc_state *crtc_state) static void bdw_set_pipemisc(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - const struct intel_crtc_scaler_state *scaler_state = - &crtc_state->scaler_state; - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); u32 val = 0; - int i; switch (crtc_state->pipe_bpp) { case 18: @@ -3598,23 +3578,6 @@ static void bdw_set_pipemisc(const struct intel_crtc_state *crtc_state) if (DISPLAY_VER(dev_priv) >= 12) val |= PIPEMISC_PIXEL_ROUNDING_TRUNC; - if (IS_ALDERLAKE_P(dev_priv)) { - bool scaler_in_use = false; - - for (i = 0; i < crtc->num_scalers; i++) { - if (!scaler_state->scalers[i].in_use) - continue; - - scaler_in_use = true; - break; - } - - intel_de_rmw(dev_priv, PIPE_MISC2(crtc->pipe), - PIPE_MISC2_BUBBLE_COUNTER_MASK, - scaler_in_use ? PIPE_MISC2_BUBBLE_COUNTER_SCALER_EN : - PIPE_MISC2_BUBBLE_COUNTER_SCALER_DIS); - } - intel_de_write(dev_priv, PIPEMISC(crtc->pipe), val); } @@ -3830,6 +3793,10 @@ static bool ilk_get_pipe_config(struct intel_crtc *crtc, pipe_config->gamma_mode = REG_FIELD_GET(PIPECONF_GAMMA_MODE_MASK_ILK, tmp); + pipe_config->framestart_delay = REG_FIELD_GET(PIPECONF_FRAME_START_DELAY_MASK, tmp) + 1; + + pipe_config->msa_timing_delay = REG_FIELD_GET(PIPECONF_MSA_TIMING_DELAY_MASK, tmp); + pipe_config->csc_mode = intel_de_read(dev_priv, PIPE_CSC_MODE(crtc->pipe)); @@ -4164,7 +4131,6 @@ static void intel_bigjoiner_get_config(struct intel_crtc_state *crtc_state) if (((master_pipes | slave_pipes) & BIT(pipe)) == 0) return; - crtc_state->bigjoiner = true; crtc_state->bigjoiner_pipes = BIT(get_bigjoiner_master_pipe(pipe, master_pipes, slave_pipes)) | get_bigjoiner_slave_pipes(pipe, master_pipes, slave_pipes); @@ -4265,6 +4231,15 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc, pipe_config->pixel_multiplier = 1; } + if (!transcoder_is_dsi(pipe_config->cpu_transcoder)) { + tmp = intel_de_read(dev_priv, CHICKEN_TRANS(pipe_config->cpu_transcoder)); + + pipe_config->framestart_delay = REG_FIELD_GET(HSW_FRAME_START_DELAY_MASK, tmp) + 1; + } else { + /* no idea if this is correct */ + pipe_config->framestart_delay = 1; + } + out: intel_display_power_put_all_in_set(dev_priv, &power_domain_set); @@ -4746,6 +4721,8 @@ static int icl_check_nv12_planes(struct intel_crtc_state *crtc_state) crtc_state->enabled_planes &= ~BIT(plane->id); crtc_state->active_planes &= ~BIT(plane->id); crtc_state->update_planes |= BIT(plane->id); + crtc_state->data_rate[plane->id] = 0; + crtc_state->rel_data_rate[plane->id] = 0; } plane_state->planar_slave = false; @@ -4790,6 +4767,10 @@ static int icl_check_nv12_planes(struct intel_crtc_state *crtc_state) crtc_state->enabled_planes |= BIT(linked->id); crtc_state->active_planes |= BIT(linked->id); crtc_state->update_planes |= BIT(linked->id); + crtc_state->data_rate[linked->id] = + crtc_state->data_rate_y[plane->id]; + crtc_state->rel_data_rate[linked->id] = + crtc_state->rel_data_rate_y[plane->id]; drm_dbg_kms(&dev_priv->drm, "Using %s as Y plane for %s\n", linked->base.name, plane->base.name); @@ -4923,9 +4904,12 @@ static int intel_crtc_atomic_check(struct intel_atomic_state *state, mode_changed && !crtc_state->hw.active) crtc_state->update_wm_post = true; - if (mode_changed && crtc_state->hw.enable && - !drm_WARN_ON(&dev_priv->drm, crtc_state->shared_dpll)) { - ret = intel_dpll_crtc_compute_clock(crtc_state); + if (mode_changed) { + ret = intel_dpll_crtc_compute_clock(state, crtc); + if (ret) + return ret; + + ret = intel_dpll_crtc_get_shared_dpll(state, crtc); if (ret) return ret; } @@ -5222,7 +5206,7 @@ static void intel_dump_plane_state(const struct intel_plane_state *plane_state) drm_dbg_kms(&i915->drm, "[PLANE:%d:%s] fb: [NOFB], visible: %s\n", plane->base.base.id, plane->base.name, - yesno(plane_state->uapi.visible)); + str_yes_no(plane_state->uapi.visible)); return; } @@ -5230,7 +5214,7 @@ static void intel_dump_plane_state(const struct intel_plane_state *plane_state) "[PLANE:%d:%s] fb: [FB:%d] %ux%u format = %p4cc modifier = 0x%llx, visible: %s\n", plane->base.base.id, plane->base.name, fb->base.id, fb->width, fb->height, &fb->format->format, - fb->modifier, yesno(plane_state->uapi.visible)); + fb->modifier, str_yes_no(plane_state->uapi.visible)); drm_dbg_kms(&i915->drm, "\trotation: 0x%x, scaler: %d\n", plane_state->hw.rotation, plane_state->scaler_id); if (plane_state->uapi.visible) @@ -5253,7 +5237,7 @@ static void intel_dump_pipe_config(const struct intel_crtc_state *pipe_config, drm_dbg_kms(&dev_priv->drm, "[CRTC:%d:%s] enable: %s %s\n", crtc->base.base.id, crtc->base.name, - yesno(pipe_config->hw.enable), context); + str_yes_no(pipe_config->hw.enable), context); if (!pipe_config->hw.enable) goto dump_planes; @@ -5261,7 +5245,7 @@ static void intel_dump_pipe_config(const struct intel_crtc_state *pipe_config, snprintf_output_types(buf, sizeof(buf), pipe_config->output_types); drm_dbg_kms(&dev_priv->drm, "active: %s, output_types: %s (0x%x), output format: %s\n", - yesno(pipe_config->hw.active), + str_yes_no(pipe_config->hw.active), buf, pipe_config->output_types, output_formats(pipe_config->output_format)); @@ -5284,7 +5268,7 @@ static void intel_dump_pipe_config(const struct intel_crtc_state *pipe_config, pipe_config->bigjoiner_pipes); drm_dbg_kms(&dev_priv->drm, "splitter: %s, link count %d, overlap %d\n", - enableddisabled(pipe_config->splitter.enable), + str_enabled_disabled(pipe_config->splitter.enable), pipe_config->splitter.link_count, pipe_config->splitter.pixel_overlap); @@ -5302,6 +5286,9 @@ static void intel_dump_pipe_config(const struct intel_crtc_state *pipe_config, &pipe_config->dp_m2_n2); } + drm_dbg_kms(&dev_priv->drm, "framestart delay: %d, MSA timing delay: %d\n", + pipe_config->framestart_delay, pipe_config->msa_timing_delay); + drm_dbg_kms(&dev_priv->drm, "audio: %i, infoframes: %i, infoframes enabled: 0x%x\n", pipe_config->has_audio, pipe_config->has_infoframe, @@ -5331,25 +5318,24 @@ static void intel_dump_pipe_config(const struct intel_crtc_state *pipe_config, intel_dump_dp_vsc_sdp(dev_priv, &pipe_config->infoframes.vsc); drm_dbg_kms(&dev_priv->drm, "vrr: %s, vmin: %d, vmax: %d, pipeline full: %d, guardband: %d flipline: %d, vmin vblank: %d, vmax vblank: %d\n", - yesno(pipe_config->vrr.enable), + str_yes_no(pipe_config->vrr.enable), pipe_config->vrr.vmin, pipe_config->vrr.vmax, pipe_config->vrr.pipeline_full, pipe_config->vrr.guardband, pipe_config->vrr.flipline, intel_vrr_vmin_vblank_start(pipe_config), intel_vrr_vmax_vblank_start(pipe_config)); - drm_dbg_kms(&dev_priv->drm, "requested mode:\n"); - drm_mode_debug_printmodeline(&pipe_config->hw.mode); - drm_dbg_kms(&dev_priv->drm, "adjusted mode:\n"); - drm_mode_debug_printmodeline(&pipe_config->hw.adjusted_mode); + drm_dbg_kms(&dev_priv->drm, "requested mode: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(&pipe_config->hw.mode)); + drm_dbg_kms(&dev_priv->drm, "adjusted mode: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(&pipe_config->hw.adjusted_mode)); intel_dump_crtc_timings(dev_priv, &pipe_config->hw.adjusted_mode); - drm_dbg_kms(&dev_priv->drm, "pipe mode:\n"); - drm_mode_debug_printmodeline(&pipe_config->hw.pipe_mode); + drm_dbg_kms(&dev_priv->drm, "pipe mode: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(&pipe_config->hw.pipe_mode)); intel_dump_crtc_timings(dev_priv, &pipe_config->hw.pipe_mode); drm_dbg_kms(&dev_priv->drm, - "port clock: %d, pipe src size: %dx%d, pixel rate %d\n", - pipe_config->port_clock, - pipe_config->pipe_src_w, pipe_config->pipe_src_h, + "port clock: %d, pipe src: " DRM_RECT_FMT ", pixel rate %d\n", + pipe_config->port_clock, DRM_RECT_ARG(&pipe_config->pipe_src), pipe_config->pixel_rate); drm_dbg_kms(&dev_priv->drm, "linetime: %d, ips linetime: %d\n", @@ -5372,11 +5358,12 @@ static void intel_dump_pipe_config(const struct intel_crtc_state *pipe_config, drm_dbg_kms(&dev_priv->drm, "pch pfit: " DRM_RECT_FMT ", %s, force thru: %s\n", DRM_RECT_ARG(&pipe_config->pch_pfit.dst), - enableddisabled(pipe_config->pch_pfit.enabled), - yesno(pipe_config->pch_pfit.force_thru)); + str_enabled_disabled(pipe_config->pch_pfit.enabled), + str_yes_no(pipe_config->pch_pfit.force_thru)); - drm_dbg_kms(&dev_priv->drm, "ips: %i, double wide: %i\n", - pipe_config->ips_enabled, pipe_config->double_wide); + drm_dbg_kms(&dev_priv->drm, "ips: %i, double wide: %i, drrs: %i\n", + pipe_config->ips_enabled, pipe_config->double_wide, + pipe_config->has_drrs); intel_dpll_dump_hw_state(dev_priv, &pipe_config->dpll_hw_state); @@ -5504,8 +5491,10 @@ intel_crtc_copy_uapi_to_hw_state_modeset(struct intel_atomic_state *state, crtc_state->hw.enable = crtc_state->uapi.enable; crtc_state->hw.active = crtc_state->uapi.active; - crtc_state->hw.mode = crtc_state->uapi.mode; - crtc_state->hw.adjusted_mode = crtc_state->uapi.adjusted_mode; + drm_mode_copy(&crtc_state->hw.mode, + &crtc_state->uapi.mode); + drm_mode_copy(&crtc_state->hw.adjusted_mode, + &crtc_state->uapi.adjusted_mode); crtc_state->hw.scaling_filter = crtc_state->uapi.scaling_filter; intel_crtc_copy_uapi_to_hw_state_nomodeset(state, crtc); @@ -5563,6 +5552,9 @@ copy_bigjoiner_crtc_state_modeset(struct intel_atomic_state *state, intel_atomic_get_new_crtc_state(state, master_crtc); struct intel_crtc_state *saved_state; + WARN_ON(master_crtc_state->bigjoiner_pipes != + slave_crtc_state->bigjoiner_pipes); + saved_state = kmemdup(master_crtc_state, sizeof(*saved_state), GFP_KERNEL); if (!saved_state) return -ENOMEM; @@ -5582,19 +5574,22 @@ copy_bigjoiner_crtc_state_modeset(struct intel_atomic_state *state, memset(&slave_crtc_state->hw, 0, sizeof(slave_crtc_state->hw)); slave_crtc_state->hw.enable = master_crtc_state->hw.enable; slave_crtc_state->hw.active = master_crtc_state->hw.active; - slave_crtc_state->hw.mode = master_crtc_state->hw.mode; - slave_crtc_state->hw.pipe_mode = master_crtc_state->hw.pipe_mode; - slave_crtc_state->hw.adjusted_mode = master_crtc_state->hw.adjusted_mode; + drm_mode_copy(&slave_crtc_state->hw.mode, + &master_crtc_state->hw.mode); + drm_mode_copy(&slave_crtc_state->hw.pipe_mode, + &master_crtc_state->hw.pipe_mode); + drm_mode_copy(&slave_crtc_state->hw.adjusted_mode, + &master_crtc_state->hw.adjusted_mode); slave_crtc_state->hw.scaling_filter = master_crtc_state->hw.scaling_filter; copy_bigjoiner_crtc_state_nomodeset(state, slave_crtc); - /* Some fixups */ slave_crtc_state->uapi.mode_changed = master_crtc_state->uapi.mode_changed; slave_crtc_state->uapi.connectors_changed = master_crtc_state->uapi.connectors_changed; slave_crtc_state->uapi.active_changed = master_crtc_state->uapi.active_changed; - slave_crtc_state->cpu_transcoder = master_crtc_state->cpu_transcoder; - slave_crtc_state->has_audio = master_crtc_state->has_audio; + + WARN_ON(master_crtc_state->bigjoiner_pipes != + slave_crtc_state->bigjoiner_pipes); return 0; } @@ -5647,12 +5642,15 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, struct drm_i915_private *i915 = to_i915(pipe_config->uapi.crtc->dev); struct drm_connector *connector; struct drm_connector_state *connector_state; + int pipe_src_w, pipe_src_h; int base_bpp, ret, i; bool retry = true; pipe_config->cpu_transcoder = (enum transcoder) to_intel_crtc(crtc)->pipe; + pipe_config->framestart_delay = 1; + /* * Sanitize sync polarity flags based on requested ones. If neither * positive or negative polarity is requested, treat this as meaning @@ -5682,8 +5680,9 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, * can be changed by the connectors in the below retry loop. */ drm_mode_get_hv_timing(&pipe_config->hw.mode, - &pipe_config->pipe_src_w, - &pipe_config->pipe_src_h); + &pipe_src_w, &pipe_src_h); + drm_rect_init(&pipe_config->pipe_src, 0, 0, + pipe_src_w, pipe_src_h); for_each_new_connector_in_state(&state->base, connector, connector_state, i) { struct intel_encoder *encoder = @@ -5786,6 +5785,8 @@ intel_modeset_pipe_config_late(struct intel_crtc_state *crtc_state) struct drm_connector *connector; int i; + intel_bigjoiner_adjust_pipe_src(crtc_state); + for_each_new_connector_in_state(&state->base, connector, conn_state, i) { struct intel_encoder *encoder = @@ -6022,8 +6023,8 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, if (current_config->name != pipe_config->name) { \ pipe_config_mismatch(fastset, crtc, __stringify(name), \ "(expected %s, found %s)", \ - yesno(current_config->name), \ - yesno(pipe_config->name)); \ + str_yes_no(current_config->name), \ + str_yes_no(pipe_config->name)); \ ret = false; \ } \ } while (0) @@ -6039,8 +6040,8 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, } else { \ pipe_config_mismatch(fastset, crtc, __stringify(name), \ "unable to verify whether state matches exactly, forcing modeset (expected %s, found %s)", \ - yesno(current_config->name), \ - yesno(pipe_config->name)); \ + str_yes_no(current_config->name), \ + str_yes_no(pipe_config->name)); \ ret = false; \ } \ } while (0) @@ -6190,6 +6191,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_X(output_types); + PIPE_CONF_CHECK_I(framestart_delay); + PIPE_CONF_CHECK_I(msa_timing_delay); + PIPE_CONF_CHECK_I(hw.pipe_mode.crtc_hdisplay); PIPE_CONF_CHECK_I(hw.pipe_mode.crtc_htotal); PIPE_CONF_CHECK_I(hw.pipe_mode.crtc_hblank_start); @@ -6260,8 +6264,10 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_BOOL(pch_pfit.force_thru); if (!fastset) { - PIPE_CONF_CHECK_I(pipe_src_w); - PIPE_CONF_CHECK_I(pipe_src_h); + PIPE_CONF_CHECK_I(pipe_src.x1); + PIPE_CONF_CHECK_I(pipe_src.y1); + PIPE_CONF_CHECK_I(pipe_src.x2); + PIPE_CONF_CHECK_I(pipe_src.y2); PIPE_CONF_CHECK_BOOL(pch_pfit.enabled); if (current_config->pch_pfit.enabled) { @@ -6363,7 +6369,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_X(sync_mode_slaves_mask); PIPE_CONF_CHECK_I(master_transcoder); - PIPE_CONF_CHECK_BOOL(bigjoiner); PIPE_CONF_CHECK_X(bigjoiner_pipes); PIPE_CONF_CHECK_I(dsc.compression_enable); @@ -6420,8 +6425,8 @@ static void verify_wm_state(struct intel_crtc *crtc, { struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct skl_hw_state { + struct skl_ddb_entry ddb[I915_MAX_PLANES]; struct skl_ddb_entry ddb_y[I915_MAX_PLANES]; - struct skl_ddb_entry ddb_uv[I915_MAX_PLANES]; struct skl_pipe_wm wm; } *hw; const struct skl_pipe_wm *sw_wm = &new_crtc_state->wm.skl.optimal; @@ -6438,7 +6443,7 @@ static void verify_wm_state(struct intel_crtc *crtc, skl_pipe_wm_get_hw_state(crtc, &hw->wm); - skl_pipe_ddb_get_hw_state(crtc, hw->ddb_y, hw->ddb_uv); + skl_pipe_ddb_get_hw_state(crtc, hw->ddb, hw->ddb_y); hw_enabled_slices = intel_enabled_dbuf_slices_mask(dev_priv); @@ -6520,8 +6525,8 @@ static void verify_wm_state(struct intel_crtc *crtc, } /* DDB */ - hw_ddb_entry = &hw->ddb_y[plane->id]; - sw_ddb_entry = &new_crtc_state->wm.skl.plane_ddb_y[plane->id]; + hw_ddb_entry = &hw->ddb[PLANE_CURSOR]; + sw_ddb_entry = &new_crtc_state->wm.skl.plane_ddb[PLANE_CURSOR]; if (!skl_ddb_entry_equal(hw_ddb_entry, sw_ddb_entry)) { drm_err(&dev_priv->drm, @@ -6897,8 +6902,9 @@ intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct drm_display_mode adjusted_mode = - crtc_state->hw.adjusted_mode; + struct drm_display_mode adjusted_mode; + + drm_mode_init(&adjusted_mode, &crtc_state->hw.adjusted_mode); if (crtc_state->vrr.enable) { adjusted_mode.crtc_vtotal = crtc_state->vrr.vmax; @@ -6956,14 +6962,10 @@ intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state) static void intel_modeset_clear_plls(struct intel_atomic_state *state) { - struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc_state *new_crtc_state; struct intel_crtc *crtc; int i; - if (!dev_priv->dpll_funcs) - return; - for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { if (!intel_crtc_needs_modeset(new_crtc_state)) continue; @@ -7294,32 +7296,26 @@ static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state, struct intel_crtc_state *master_crtc_state = intel_atomic_get_new_crtc_state(state, master_crtc); struct intel_crtc *slave_crtc; - u8 slave_pipes; - - /* - * TODO: encoder.compute_config() may be the best - * place to populate the bitmask for the master crtc. - * For now encoder.compute_config() just flags things - * as needing bigjoiner and we populate the bitmask - * here. - */ - WARN_ON(master_crtc_state->bigjoiner_pipes); - if (!master_crtc_state->bigjoiner) + if (!master_crtc_state->bigjoiner_pipes) return 0; - slave_pipes = BIT(master_crtc->pipe + 1); + /* sanity check */ + if (drm_WARN_ON(&i915->drm, + master_crtc->pipe != bigjoiner_master_pipe(master_crtc_state))) + return -EINVAL; - if (slave_pipes & ~bigjoiner_pipes(i915)) { + if (master_crtc_state->bigjoiner_pipes & ~bigjoiner_pipes(i915)) { drm_dbg_kms(&i915->drm, "[CRTC:%d:%s] Cannot act as big joiner master " - "(need 0x%x as slave pipes, only 0x%x possible)\n", + "(need 0x%x as pipes, only 0x%x possible)\n", master_crtc->base.base.id, master_crtc->base.name, - slave_pipes, bigjoiner_pipes(i915)); + master_crtc_state->bigjoiner_pipes, bigjoiner_pipes(i915)); return -EINVAL; } - for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc, slave_pipes) { + for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc, + intel_crtc_bigjoiner_slave_pipes(master_crtc_state)) { struct intel_crtc_state *slave_crtc_state; int ret; @@ -7353,10 +7349,8 @@ static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state, slave_crtc->base.base.id, slave_crtc->base.name, master_crtc->base.base.id, master_crtc->base.name); - master_crtc_state->bigjoiner_pipes = - BIT(master_crtc->pipe) | BIT(slave_crtc->pipe); slave_crtc_state->bigjoiner_pipes = - BIT(master_crtc->pipe) | BIT(slave_crtc->pipe); + master_crtc_state->bigjoiner_pipes; ret = copy_bigjoiner_crtc_state_modeset(state, slave_crtc); if (ret) @@ -7379,13 +7373,11 @@ static void kill_bigjoiner_slave(struct intel_atomic_state *state, struct intel_crtc_state *slave_crtc_state = intel_atomic_get_new_crtc_state(state, slave_crtc); - slave_crtc_state->bigjoiner = false; slave_crtc_state->bigjoiner_pipes = 0; intel_crtc_copy_uapi_to_hw_state_modeset(state, slave_crtc); } - master_crtc_state->bigjoiner = false; master_crtc_state->bigjoiner_pipes = 0; } @@ -7479,18 +7471,24 @@ static int intel_async_flip_check_hw(struct intel_atomic_state *state, struct in if (!new_crtc_state->uapi.async_flip) return 0; - if (intel_crtc_needs_modeset(new_crtc_state)) { - drm_dbg_kms(&i915->drm, "Modeset Required. Async flip not supported\n"); + if (!new_crtc_state->hw.active) { + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s] not active\n", + crtc->base.base.id, crtc->base.name); return -EINVAL; } - if (!new_crtc_state->hw.active) { - drm_dbg_kms(&i915->drm, "CRTC inactive\n"); + if (intel_crtc_needs_modeset(new_crtc_state)) { + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s] modeset required\n", + crtc->base.base.id, crtc->base.name); return -EINVAL; } + if (old_crtc_state->active_planes != new_crtc_state->active_planes) { drm_dbg_kms(&i915->drm, - "Active planes cannot be changed during async flip\n"); + "[CRTC:%d:%s] Active planes cannot be in async flip\n", + crtc->base.base.id, crtc->base.name); return -EINVAL; } @@ -7528,78 +7526,98 @@ static int intel_async_flip_check_hw(struct intel_atomic_state *state, struct in case I915_FORMAT_MOD_X_TILED: case I915_FORMAT_MOD_Y_TILED: case I915_FORMAT_MOD_Yf_TILED: + case I915_FORMAT_MOD_4_TILED: break; default: drm_dbg_kms(&i915->drm, - "Linear memory/CCS does not support async flips\n"); + "[PLANE:%d:%s] Modifier does not support async flips\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (new_plane_state->hw.fb->format->num_planes > 1) { drm_dbg_kms(&i915->drm, - "Planar formats not supported with async flips\n"); + "[PLANE:%d:%s] Planar formats do not support async flips\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->view.color_plane[0].mapping_stride != new_plane_state->view.color_plane[0].mapping_stride) { - drm_dbg_kms(&i915->drm, "Stride cannot be changed in async flip\n"); + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] Stride cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->hw.fb->modifier != new_plane_state->hw.fb->modifier) { drm_dbg_kms(&i915->drm, - "Framebuffer modifiers cannot be changed in async flip\n"); + "[PLANE:%d:%s] Modifier cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->hw.fb->format != new_plane_state->hw.fb->format) { drm_dbg_kms(&i915->drm, - "Framebuffer format cannot be changed in async flip\n"); + "[PLANE:%d:%s] Pixel format cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->hw.rotation != new_plane_state->hw.rotation) { - drm_dbg_kms(&i915->drm, "Rotation cannot be changed in async flip\n"); + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] Rotation cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (!drm_rect_equals(&old_plane_state->uapi.src, &new_plane_state->uapi.src) || !drm_rect_equals(&old_plane_state->uapi.dst, &new_plane_state->uapi.dst)) { drm_dbg_kms(&i915->drm, - "Plane size/co-ordinates cannot be changed in async flip\n"); + "[PLANE:%d:%s] Size/co-ordinates cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->hw.alpha != new_plane_state->hw.alpha) { - drm_dbg_kms(&i915->drm, "Alpha value cannot be changed in async flip\n"); + drm_dbg_kms(&i915->drm, + "[PLANES:%d:%s] Alpha value cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->hw.pixel_blend_mode != new_plane_state->hw.pixel_blend_mode) { drm_dbg_kms(&i915->drm, - "Pixel blend mode cannot be changed in async flip\n"); + "[PLANE:%d:%s] Pixel blend mode cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->hw.color_encoding != new_plane_state->hw.color_encoding) { drm_dbg_kms(&i915->drm, - "Color encoding cannot be changed in async flip\n"); + "[PLANE:%d:%s] Color encoding cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } if (old_plane_state->hw.color_range != new_plane_state->hw.color_range) { - drm_dbg_kms(&i915->drm, "Color range cannot be changed in async flip\n"); + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] Color range cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; } /* plane decryption is allow to change only in synchronous flips */ - if (old_plane_state->decrypt != new_plane_state->decrypt) + if (old_plane_state->decrypt != new_plane_state->decrypt) { + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] Decryption cannot be changed in async flip\n", + plane->base.base.id, plane->base.name); return -EINVAL; + } } return 0; @@ -7772,7 +7790,7 @@ static int intel_atomic_check(struct drm_device *dev, } } - if (new_crtc_state->bigjoiner) { + if (new_crtc_state->bigjoiner_pipes) { if (intel_pipes_need_modeset(state, new_crtc_state->bigjoiner_pipes)) { new_crtc_state->uapi.mode_changed = true; new_crtc_state->update_pipe = false; @@ -7970,7 +7988,7 @@ static void commit_pipe_pre_planes(struct intel_atomic_state *state, if (!modeset) { if (new_crtc_state->uapi.color_mgmt_changed || new_crtc_state->update_pipe) - intel_color_commit(new_crtc_state); + intel_color_commit_arm(new_crtc_state); if (DISPLAY_VER(dev_priv) >= 9 || IS_BROADWELL(dev_priv)) bdw_set_pipemisc(new_crtc_state); @@ -8050,6 +8068,11 @@ static void intel_update_crtc(struct intel_atomic_state *state, intel_fbc_update(state, crtc); + if (!modeset && + (new_crtc_state->uapi.color_mgmt_changed || + new_crtc_state->update_pipe)) + intel_color_commit_noarm(new_crtc_state); + intel_crtc_planes_update_noarm(state, crtc); /* Perform vblank evasion around commit operation */ @@ -8384,7 +8407,9 @@ static void intel_atomic_prepare_plane_clear_colors(struct intel_atomic_state *s /* * The layout of the fast clear color value expected by HW - * (the DRM ABI requiring this value to be located in fb at offset 0 of plane#2): + * (the DRM ABI requiring this value to be located in fb at + * offset 0 of cc plane, plane #2 previous generations or + * plane #1 for flat ccs): * - 4 x 4 bytes per-channel value * (in surface type specific float/int format provided by the fb user) * - 8 bytes native color value used by the display @@ -8410,7 +8435,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc_state *new_crtc_state, *old_crtc_state; struct intel_crtc *crtc; - u64 put_domains[I915_MAX_PIPES] = {}; + struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {}; intel_wakeref_t wakeref = 0; int i; @@ -8427,9 +8452,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) new_crtc_state, i) { if (intel_crtc_needs_modeset(new_crtc_state) || new_crtc_state->update_pipe) { - - put_domains[crtc->pipe] = - modeset_get_crtc_power_domains(new_crtc_state); + modeset_get_crtc_power_domains(new_crtc_state, &put_domains[crtc->pipe]); } } @@ -8467,6 +8490,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) intel_encoders_update_prepare(state); intel_dbuf_pre_plane_update(state); + intel_mbus_dbox_update(state); for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { if (new_crtc_state->do_async_flip) @@ -8528,7 +8552,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { intel_post_plane_update(state, crtc); - modeset_put_crtc_power_domains(crtc, put_domains[crtc->pipe]); + modeset_put_crtc_power_domains(crtc, &put_domains[crtc->pipe]); intel_modeset_verify_crtc(crtc, state, old_crtc_state, new_crtc_state); @@ -9573,8 +9597,6 @@ int intel_modeset_init_noirq(struct drm_i915_private *i915) i915->flip_wq = alloc_workqueue("i915_flip", WQ_HIGHPRI | WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE); - i915->framestart_delay = 1; /* 1-4 */ - i915->window2_delay = 0; /* No DSB so no window2 delay */ intel_mode_config_init(i915); @@ -9643,7 +9665,7 @@ int intel_modeset_init_nogem(struct drm_i915_private *i915) } intel_plane_possible_crtcs_init(i915); - intel_shared_dpll_init(dev); + intel_shared_dpll_init(i915); intel_fdi_pll_freq_update(i915); intel_update_czclk(i915); @@ -9750,9 +9772,6 @@ void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) PLL_REF_INPUT_DREFCLK | DPLL_VCO_ENABLE; - intel_de_write(dev_priv, FP0(pipe), fp); - intel_de_write(dev_priv, FP1(pipe), fp); - intel_de_write(dev_priv, HTOTAL(pipe), (640 - 1) | ((800 - 1) << 16)); intel_de_write(dev_priv, HBLANK(pipe), (640 - 1) | ((800 - 1) << 16)); intel_de_write(dev_priv, HSYNC(pipe), (656 - 1) | ((752 - 1) << 16)); @@ -9761,6 +9780,9 @@ void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe) intel_de_write(dev_priv, VSYNC(pipe), (490 - 1) | ((492 - 1) << 16)); intel_de_write(dev_priv, PIPESRC(pipe), ((640 - 1) << 16) | (480 - 1)); + intel_de_write(dev_priv, FP0(pipe), fp); + intel_de_write(dev_priv, FP1(pipe), fp); + /* * Apparently we need to have VGA mode enabled prior to changing * the P1/P2 dividers. Otherwise the DPLL will keep using the old @@ -9871,64 +9893,6 @@ static struct intel_connector *intel_encoder_find_connector(struct intel_encoder return NULL; } -static bool has_pch_trancoder(struct drm_i915_private *dev_priv, - enum pipe pch_transcoder) -{ - return HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) || - (HAS_PCH_LPT_H(dev_priv) && pch_transcoder == PIPE_A); -} - -static void intel_sanitize_frame_start_delay(const struct intel_crtc_state *crtc_state) -{ - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; - - if (DISPLAY_VER(dev_priv) >= 9 || - IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv)) { - i915_reg_t reg = CHICKEN_TRANS(cpu_transcoder); - u32 val; - - if (transcoder_is_dsi(cpu_transcoder)) - return; - - val = intel_de_read(dev_priv, reg); - val &= ~HSW_FRAME_START_DELAY_MASK; - val |= HSW_FRAME_START_DELAY(dev_priv->framestart_delay - 1); - intel_de_write(dev_priv, reg, val); - } else { - i915_reg_t reg = PIPECONF(cpu_transcoder); - u32 val; - - val = intel_de_read(dev_priv, reg); - val &= ~PIPECONF_FRAME_START_DELAY_MASK; - val |= PIPECONF_FRAME_START_DELAY(dev_priv->framestart_delay - 1); - intel_de_write(dev_priv, reg, val); - } - - if (!crtc_state->has_pch_encoder) - return; - - if (HAS_PCH_IBX(dev_priv)) { - i915_reg_t reg = PCH_TRANSCONF(crtc->pipe); - u32 val; - - val = intel_de_read(dev_priv, reg); - val &= ~TRANS_FRAME_START_DELAY_MASK; - val |= TRANS_FRAME_START_DELAY(dev_priv->framestart_delay - 1); - intel_de_write(dev_priv, reg, val); - } else { - enum pipe pch_transcoder = intel_crtc_pch_transcoder(crtc); - i915_reg_t reg = TRANS_CHICKEN2(pch_transcoder); - u32 val; - - val = intel_de_read(dev_priv, reg); - val &= ~TRANS_CHICKEN2_FRAME_START_DELAY_MASK; - val |= TRANS_CHICKEN2_FRAME_START_DELAY(dev_priv->framestart_delay - 1); - intel_de_write(dev_priv, reg, val); - } -} - static void intel_sanitize_crtc(struct intel_crtc *crtc, struct drm_modeset_acquire_ctx *ctx) { @@ -9939,9 +9903,6 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc, if (crtc_state->hw.active) { struct intel_plane *plane; - /* Clear any frame start delays used for debugging left by the BIOS */ - intel_sanitize_frame_start_delay(crtc_state); - /* Disable everything but the primary plane */ for_each_intel_plane_on_crtc(dev, crtc, plane) { const struct intel_plane_state *plane_state = @@ -9953,7 +9914,8 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc, } /* Disable any background color/etc. set by the BIOS */ - intel_color_commit(crtc_state); + intel_color_commit_noarm(crtc_state); + intel_color_commit_arm(crtc_state); } /* Adjust the state of the output pipe according to whether we @@ -9986,7 +9948,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc, * PCH transcoders B and C would prevent enabling the south * error interrupt (see cpt_can_enable_serr_int()). */ - if (has_pch_trancoder(dev_priv, crtc->pipe)) + if (intel_has_pch_trancoder(dev_priv, crtc->pipe)) crtc->pch_fifo_underrun_disabled = true; } } @@ -10105,7 +10067,7 @@ static void readout_plane_state(struct drm_i915_private *dev_priv) drm_dbg_kms(&dev_priv->drm, "[PLANE:%d:%s] hw state readout: %s, pipe %c\n", plane->base.base.id, plane->base.name, - enableddisabled(visible), pipe_name(pipe)); + str_enabled_disabled(visible), pipe_name(pipe)); } for_each_intel_crtc(&dev_priv->drm, crtc) { @@ -10151,7 +10113,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) drm_dbg_kms(&dev_priv->drm, "[CRTC:%d:%s] hw state readout: %s\n", crtc->base.base.id, crtc->base.name, - enableddisabled(crtc_state->hw.active)); + str_enabled_disabled(crtc_state->hw.active)); } cdclk_state->active_pipes = dbuf_state->active_pipes = active_pipes; @@ -10171,7 +10133,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) intel_encoder_get_config(encoder, crtc_state); /* read out to slave crtc as well for bigjoiner */ - if (crtc_state->bigjoiner) { + if (crtc_state->bigjoiner_pipes) { struct intel_crtc *slave_crtc; /* encoder should read be linked to bigjoiner master */ @@ -10195,7 +10157,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) drm_dbg_kms(&dev_priv->drm, "[ENCODER:%d:%s] hw state readout: %s, pipe %c\n", encoder->base.base.id, encoder->base.name, - enableddisabled(encoder->base.crtc), + str_enabled_disabled(encoder->base.crtc), pipe_name(pipe)); } @@ -10233,7 +10195,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s] hw state readout: %s\n", connector->base.base.id, connector->base.name, - enableddisabled(connector->base.encoder)); + str_enabled_disabled(connector->base.encoder)); } drm_connector_list_iter_end(&conn_iter); @@ -10359,66 +10321,6 @@ static void intel_early_display_was(struct drm_i915_private *dev_priv) } } -static void ibx_sanitize_pch_hdmi_port(struct drm_i915_private *dev_priv, - enum port port, i915_reg_t hdmi_reg) -{ - u32 val = intel_de_read(dev_priv, hdmi_reg); - - if (val & SDVO_ENABLE || - (val & SDVO_PIPE_SEL_MASK) == SDVO_PIPE_SEL(PIPE_A)) - return; - - drm_dbg_kms(&dev_priv->drm, - "Sanitizing transcoder select for HDMI %c\n", - port_name(port)); - - val &= ~SDVO_PIPE_SEL_MASK; - val |= SDVO_PIPE_SEL(PIPE_A); - - intel_de_write(dev_priv, hdmi_reg, val); -} - -static void ibx_sanitize_pch_dp_port(struct drm_i915_private *dev_priv, - enum port port, i915_reg_t dp_reg) -{ - u32 val = intel_de_read(dev_priv, dp_reg); - - if (val & DP_PORT_EN || - (val & DP_PIPE_SEL_MASK) == DP_PIPE_SEL(PIPE_A)) - return; - - drm_dbg_kms(&dev_priv->drm, - "Sanitizing transcoder select for DP %c\n", - port_name(port)); - - val &= ~DP_PIPE_SEL_MASK; - val |= DP_PIPE_SEL(PIPE_A); - - intel_de_write(dev_priv, dp_reg, val); -} - -static void ibx_sanitize_pch_ports(struct drm_i915_private *dev_priv) -{ - /* - * The BIOS may select transcoder B on some of the PCH - * ports even it doesn't enable the port. This would trip - * assert_pch_dp_disabled() and assert_pch_hdmi_disabled(). - * Sanitize the transcoder select bits to prevent that. We - * assume that the BIOS never actually enabled the port, - * because if it did we'd actually have to toggle the port - * on and back off to make the transcoder A select stick - * (see. intel_dp_link_down(), intel_disable_hdmi(), - * intel_disable_sdvo()). - */ - ibx_sanitize_pch_dp_port(dev_priv, PORT_B, PCH_DP_B); - ibx_sanitize_pch_dp_port(dev_priv, PORT_C, PCH_DP_C); - ibx_sanitize_pch_dp_port(dev_priv, PORT_D, PCH_DP_D); - - /* PCH SDVOB multiplex with HDMIB */ - ibx_sanitize_pch_hdmi_port(dev_priv, PORT_B, PCH_HDMIB); - ibx_sanitize_pch_hdmi_port(dev_priv, PORT_C, PCH_HDMIC); - ibx_sanitize_pch_hdmi_port(dev_priv, PORT_D, PCH_HDMID); -} /* Scan out the current hw modeset state, * and sanitizes it to the current state @@ -10440,8 +10342,7 @@ intel_modeset_setup_hw_state(struct drm_device *dev, /* HW state is read out, now we need to sanitize this mess. */ get_encoder_power_domains(dev_priv); - if (HAS_PCH_IBX(dev_priv)) - ibx_sanitize_pch_ports(dev_priv); + intel_pch_sanitize(dev_priv); /* * intel_sanitize_plane_mapping() may need to do vblank @@ -10457,6 +10358,8 @@ intel_modeset_setup_hw_state(struct drm_device *dev, intel_crtc_vblank_on(crtc_state); } + intel_fbc_sanitize(dev_priv); + intel_sanitize_plane_mapping(dev_priv); for_each_intel_encoder(dev, encoder) @@ -10490,11 +10393,11 @@ intel_modeset_setup_hw_state(struct drm_device *dev, for_each_intel_crtc(dev, crtc) { struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); - u64 put_domains; + struct intel_power_domain_mask put_domains; - put_domains = modeset_get_crtc_power_domains(crtc_state); - if (drm_WARN_ON(dev, put_domains)) - modeset_put_crtc_power_domains(crtc, put_domains); + modeset_get_crtc_power_domains(crtc_state, &put_domains); + if (drm_WARN_ON(dev, !bitmap_empty(put_domains.bits, POWER_DOMAIN_NUM))) + modeset_put_crtc_power_domains(crtc, &put_domains); } intel_display_power_put(dev_priv, POWER_DOMAIN_INIT, wakeref); @@ -10595,8 +10498,6 @@ void intel_modeset_driver_remove_noirq(struct drm_i915_private *i915) intel_unregister_dsm_handler(); - intel_fbc_global_disable(i915); - /* flush any delayed tasks or pending work */ flush_scheduled_work(); @@ -10697,3 +10598,8 @@ void intel_display_driver_unregister(struct drm_i915_private *i915) acpi_video_unregister(); intel_opregion_unregister(i915); } + +bool intel_scanout_needs_vtd_wa(struct drm_i915_private *i915) +{ + return DISPLAY_VER(i915) >= 6 && i915_vtd_active(i915); +} diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 11d6134c53c8..187910d94ec6 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -565,7 +565,6 @@ void intel_enable_transcoder(const struct intel_crtc_state *new_crtc_state); void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state); void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe); void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe); -enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc); int vlv_get_hpll_vco(struct drm_i915_private *dev_priv); int vlv_get_cck_clock(struct drm_i915_private *dev_priv, const char *name, u32 reg, int ref_freq); @@ -636,11 +635,9 @@ void intel_cpu_transcoder_get_m2_n2(struct intel_crtc *crtc, void i9xx_crtc_clock_get(struct intel_crtc *crtc, struct intel_crtc_state *pipe_config); int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n); -enum intel_display_power_domain intel_port_to_power_domain(enum port port); +enum intel_display_power_domain intel_port_to_power_domain(struct intel_digital_port *dig_port); enum intel_display_power_domain intel_aux_power_domain(struct intel_digital_port *dig_port); -enum intel_display_power_domain -intel_legacy_aux_to_power_domain(enum aux_ch aux_ch); void intel_crtc_arm_fifo_underrun(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state); void ilk_pfit_disable(const struct intel_crtc_state *old_crtc_state); @@ -695,4 +692,6 @@ void assert_transcoder(struct drm_i915_private *dev_priv, #define I915_STATE_WARN_ON(x) \ I915_STATE_WARN((x), "%s", "WARN_ON(" __stringify(x) ")") +bool intel_scanout_needs_vtd_wa(struct drm_i915_private *i915); + #endif diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index ffe6822d7414..452d773fd4e3 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -3,6 +3,8 @@ * Copyright © 2020 Intel Corporation */ +#include <linux/string_helpers.h> + #include <drm/drm_debugfs.h> #include <drm/drm_fourcc.h> @@ -10,6 +12,7 @@ #include "intel_de.h" #include "intel_display_debugfs.h" #include "intel_display_power.h" +#include "intel_display_power_well.h" #include "intel_display_types.h" #include "intel_dmc.h" #include "intel_dp.h" @@ -19,6 +22,7 @@ #include "intel_fbdev.h" #include "intel_hdcp.h" #include "intel_hdmi.h" +#include "intel_panel.h" #include "intel_pm.h" #include "intel_psr.h" #include "intel_sprite.h" @@ -52,7 +56,7 @@ static int i915_ips_status(struct seq_file *m, void *unused) wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); seq_printf(m, "Enabled by kernel parameter: %s\n", - yesno(dev_priv->params.enable_ips)); + str_yes_no(dev_priv->params.enable_ips)); if (DISPLAY_VER(dev_priv) >= 8) { seq_puts(m, "Currently: unknown\n"); @@ -92,7 +96,7 @@ static int i915_sr_status(struct seq_file *m, void *unused) intel_display_power_put(dev_priv, POWER_DOMAIN_INIT, wakeref); - seq_printf(m, "self-refresh: %s\n", enableddisabled(sr_enabled)); + seq_printf(m, "self-refresh: %s\n", str_enabled_disabled(sr_enabled)); return 0; } @@ -260,7 +264,7 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) bool enabled; u32 val; - seq_printf(m, "Sink support: %s", yesno(psr->sink_support)); + seq_printf(m, "Sink support: %s", str_yes_no(psr->sink_support)); if (psr->sink_support) seq_printf(m, " [0x%02x]", intel_dp->psr_dpcd[0]); seq_puts(m, "\n"); @@ -279,7 +283,7 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) if (!psr->enabled) { seq_printf(m, "PSR sink not reliable: %s\n", - yesno(psr->sink_not_reliable)); + str_yes_no(psr->sink_not_reliable)); goto unlock; } @@ -294,7 +298,7 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) enabled = val & EDP_PSR_ENABLE; } seq_printf(m, "Source PSR ctl: %s [0x%08x]\n", - enableddisabled(enabled), val); + str_enabled_disabled(enabled), val); psr_source_status(intel_dp, m); seq_printf(m, "Busy frontbuffer bits: 0x%08x\n", psr->busy_frontbuffer_bits); @@ -341,7 +345,7 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) } seq_printf(m, "PSR2 selective fetch: %s\n", - enableddisabled(psr->psr2_sel_fetch_enabled)); + str_enabled_disabled(psr->psr2_sel_fetch_enabled)); } unlock: @@ -432,75 +436,6 @@ static int i915_power_domain_info(struct seq_file *m, void *unused) return 0; } -static int i915_dmc_info(struct seq_file *m, void *unused) -{ - struct drm_i915_private *dev_priv = node_to_i915(m->private); - intel_wakeref_t wakeref; - struct intel_dmc *dmc; - i915_reg_t dc5_reg, dc6_reg = {}; - - if (!HAS_DMC(dev_priv)) - return -ENODEV; - - dmc = &dev_priv->dmc; - - wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); - - seq_printf(m, "fw loaded: %s\n", yesno(intel_dmc_has_payload(dev_priv))); - seq_printf(m, "path: %s\n", dmc->fw_path); - seq_printf(m, "Pipe A fw support: %s\n", - yesno(GRAPHICS_VER(dev_priv) >= 12)); - seq_printf(m, "Pipe A fw loaded: %s\n", yesno(dmc->dmc_info[DMC_FW_PIPEA].payload)); - seq_printf(m, "Pipe B fw support: %s\n", yesno(IS_ALDERLAKE_P(dev_priv))); - seq_printf(m, "Pipe B fw loaded: %s\n", yesno(dmc->dmc_info[DMC_FW_PIPEB].payload)); - - if (!intel_dmc_has_payload(dev_priv)) - goto out; - - seq_printf(m, "version: %d.%d\n", DMC_VERSION_MAJOR(dmc->version), - DMC_VERSION_MINOR(dmc->version)); - - if (DISPLAY_VER(dev_priv) >= 12) { - if (IS_DGFX(dev_priv)) { - dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; - } else { - dc5_reg = TGL_DMC_DEBUG_DC5_COUNT; - dc6_reg = TGL_DMC_DEBUG_DC6_COUNT; - } - - /* - * NOTE: DMC_DEBUG3 is a general purpose reg. - * According to B.Specs:49196 DMC f/w reuses DC5/6 counter - * reg for DC3CO debugging and validation, - * but TGL DMC f/w is using DMC_DEBUG3 reg for DC3CO counter. - */ - seq_printf(m, "DC3CO count: %d\n", intel_de_read(dev_priv, IS_DGFX(dev_priv) ? - DG1_DMC_DEBUG3 : TGL_DMC_DEBUG3)); - } else { - dc5_reg = IS_BROXTON(dev_priv) ? BXT_DMC_DC3_DC5_COUNT : - SKL_DMC_DC3_DC5_COUNT; - if (!IS_GEMINILAKE(dev_priv) && !IS_BROXTON(dev_priv)) - dc6_reg = SKL_DMC_DC5_DC6_COUNT; - } - - seq_printf(m, "DC3 -> DC5 count: %d\n", - intel_de_read(dev_priv, dc5_reg)); - if (dc6_reg.reg) - seq_printf(m, "DC5 -> DC6 count: %d\n", - intel_de_read(dev_priv, dc6_reg)); - -out: - seq_printf(m, "program base: 0x%08x\n", - intel_de_read(dev_priv, DMC_PROGRAM(dmc->dmc_info[DMC_FW_MAIN].start_mmioaddr, 0))); - seq_printf(m, "ssp base: 0x%08x\n", - intel_de_read(dev_priv, DMC_SSP_BASE)); - seq_printf(m, "htp: 0x%08x\n", intel_de_read(dev_priv, DMC_HTP_SKL)); - - intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref); - - return 0; -} - static void intel_seq_print_mode(struct seq_file *m, int tabs, const struct drm_display_mode *mode) { @@ -537,11 +472,18 @@ static void intel_encoder_info(struct seq_file *m, drm_connector_list_iter_end(&conn_iter); } -static void intel_panel_info(struct seq_file *m, struct intel_panel *panel) +static void intel_panel_info(struct seq_file *m, + struct intel_connector *connector) { - const struct drm_display_mode *mode = panel->fixed_mode; + const struct drm_display_mode *fixed_mode; + + if (list_empty(&connector->panel.fixed_modes)) + return; - seq_printf(m, "\tfixed mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); + seq_puts(m, "\tfixed modes:\n"); + + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) + intel_seq_print_mode(m, 2, fixed_mode); } static void intel_hdcp_info(struct seq_file *m, @@ -577,9 +519,8 @@ static void intel_dp_info(struct seq_file *m, const struct drm_property_blob *edid = intel_connector->base.edid_blob_ptr; seq_printf(m, "\tDPCD rev: %x\n", intel_dp->dpcd[DP_DPCD_REV]); - seq_printf(m, "\taudio support: %s\n", yesno(intel_dp->has_audio)); - if (intel_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) - intel_panel_info(m, &intel_connector->panel); + seq_printf(m, "\taudio support: %s\n", + str_yes_no(intel_dp->has_audio)); drm_dp_downstream_debug(m, intel_dp->dpcd, intel_dp->downstream_ports, edid ? edid->data : NULL, &intel_dp->aux); @@ -590,7 +531,7 @@ static void intel_dp_mst_info(struct seq_file *m, { bool has_audio = intel_connector->port->has_audio; - seq_printf(m, "\taudio support: %s\n", yesno(has_audio)); + seq_printf(m, "\taudio support: %s\n", str_yes_no(has_audio)); } static void intel_hdmi_info(struct seq_file *m, @@ -599,13 +540,8 @@ static void intel_hdmi_info(struct seq_file *m, struct intel_encoder *intel_encoder = intel_attached_encoder(intel_connector); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(intel_encoder); - seq_printf(m, "\taudio support: %s\n", yesno(intel_hdmi->has_audio)); -} - -static void intel_lvds_info(struct seq_file *m, - struct intel_connector *intel_connector) -{ - intel_panel_info(m, &intel_connector->panel); + seq_printf(m, "\taudio support: %s\n", + str_yes_no(intel_hdmi->has_audio)); } static void intel_connector_info(struct seq_file *m, @@ -642,10 +578,6 @@ static void intel_connector_info(struct seq_file *m, else intel_dp_info(m, intel_connector); break; - case DRM_MODE_CONNECTOR_LVDS: - if (encoder->type == INTEL_OUTPUT_LVDS) - intel_lvds_info(m, intel_connector); - break; case DRM_MODE_CONNECTOR_HDMIA: if (encoder->type == INTEL_OUTPUT_HDMI || encoder->type == INTEL_OUTPUT_DDI) @@ -658,6 +590,8 @@ static void intel_connector_info(struct seq_file *m, seq_puts(m, "\tHDCP version: "); intel_hdcp_info(m, intel_connector); + intel_panel_info(m, intel_connector); + seq_printf(m, "\tmodes:\n"); list_for_each_entry(mode, &connector->modes, head) intel_seq_print_mode(m, 2, mode); @@ -757,7 +691,7 @@ static void intel_plane_hw_info(struct seq_file *m, struct intel_plane *plane) DRM_RECT_FP_FMT ", dst=" DRM_RECT_FMT ", rotation=%s\n", fb->base.id, &fb->format->format, fb->modifier, fb->width, fb->height, - yesno(plane_state->uapi.visible), + str_yes_no(plane_state->uapi.visible), DRM_RECT_FP_ARG(&plane_state->uapi.src), DRM_RECT_ARG(&plane_state->uapi.dst), rot_str); @@ -796,7 +730,7 @@ static void intel_scaler_info(struct seq_file *m, struct intel_crtc *crtc) &crtc_state->scaler_state.scalers[i]; seq_printf(m, ", scalers[%d]: use=%s, mode=%x", - i, yesno(sc->in_use), sc->mode); + i, str_yes_no(sc->in_use), sc->mode); } seq_puts(m, "\n"); } else { @@ -919,24 +853,24 @@ static void intel_crtc_info(struct seq_file *m, struct intel_crtc *crtc) crtc->base.base.id, crtc->base.name); seq_printf(m, "\tuapi: enable=%s, active=%s, mode=" DRM_MODE_FMT "\n", - yesno(crtc_state->uapi.enable), - yesno(crtc_state->uapi.active), + str_yes_no(crtc_state->uapi.enable), + str_yes_no(crtc_state->uapi.active), DRM_MODE_ARG(&crtc_state->uapi.mode)); seq_printf(m, "\thw: enable=%s, active=%s\n", - yesno(crtc_state->hw.enable), yesno(crtc_state->hw.active)); + str_yes_no(crtc_state->hw.enable), str_yes_no(crtc_state->hw.active)); seq_printf(m, "\tadjusted_mode=" DRM_MODE_FMT "\n", DRM_MODE_ARG(&crtc_state->hw.adjusted_mode)); seq_printf(m, "\tpipe__mode=" DRM_MODE_FMT "\n", DRM_MODE_ARG(&crtc_state->hw.pipe_mode)); - seq_printf(m, "\tpipe src size=%dx%d, dither=%s, bpp=%d\n", - crtc_state->pipe_src_w, crtc_state->pipe_src_h, - yesno(crtc_state->dither), crtc_state->pipe_bpp); + seq_printf(m, "\tpipe src=" DRM_RECT_FMT ", dither=%s, bpp=%d\n", + DRM_RECT_ARG(&crtc_state->pipe_src), + str_yes_no(crtc_state->dither), crtc_state->pipe_bpp); intel_scaler_info(m, crtc); - if (crtc_state->bigjoiner) + if (crtc_state->bigjoiner_pipes) seq_printf(m, "\tLinked to 0x%x pipes as a %s\n", crtc_state->bigjoiner_pipes, intel_crtc_is_bigjoiner_slave(crtc_state) ? "slave" : "master"); @@ -948,8 +882,8 @@ static void intel_crtc_info(struct seq_file *m, struct intel_crtc *crtc) intel_plane_info(m, crtc); seq_printf(m, "\tunderrun reporting: cpu=%s pch=%s\n", - yesno(!crtc->cpu_fifo_underrun_disabled), - yesno(!crtc->pch_fifo_underrun_disabled)); + str_yes_no(!crtc->cpu_fifo_underrun_disabled), + str_yes_no(!crtc->pch_fifo_underrun_disabled)); crtc_updates_info(m, crtc, "\t"); } @@ -1005,7 +939,8 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused) seq_printf(m, "DPLL%i: %s, id: %i\n", i, pll->info->name, pll->info->id); seq_printf(m, " pipe_mask: 0x%x, active: 0x%x, on: %s\n", - pll->state.pipe_mask, pll->active_mask, yesno(pll->on)); + pll->state.pipe_mask, pll->active_mask, + str_yes_no(pll->on)); seq_printf(m, " tracked hardware state:\n"); seq_printf(m, " dpll: 0x%08x\n", pll->state.hw_state.dpll); seq_printf(m, " dpll_md: 0x%08x\n", @@ -1047,7 +982,7 @@ static int i915_ipc_status_show(struct seq_file *m, void *data) struct drm_i915_private *dev_priv = m->private; seq_printf(m, "Isochronous Priority Control: %s\n", - yesno(dev_priv->ipc_enabled)); + str_yes_no(dev_priv->ipc_enabled)); return 0; } @@ -1117,13 +1052,13 @@ static int i915_ddb_info(struct seq_file *m, void *unused) seq_printf(m, "Pipe %c\n", pipe_name(pipe)); for_each_plane_id_on_crtc(crtc, plane_id) { - entry = &crtc_state->wm.skl.plane_ddb_y[plane_id]; + entry = &crtc_state->wm.skl.plane_ddb[plane_id]; seq_printf(m, " Plane%-8d%8u%8u%8u\n", plane_id + 1, entry->start, entry->end, skl_ddb_entry_size(entry)); } - entry = &crtc_state->wm.skl.plane_ddb_y[PLANE_CURSOR]; + entry = &crtc_state->wm.skl.plane_ddb[PLANE_CURSOR]; seq_printf(m, " %-13s%8u%8u%8u\n", "Cursor", entry->start, entry->end, skl_ddb_entry_size(entry)); } @@ -1133,97 +1068,48 @@ static int i915_ddb_info(struct seq_file *m, void *unused) return 0; } -static void drrs_status_per_crtc(struct seq_file *m, - struct drm_device *dev, - struct intel_crtc *crtc) +static int i915_drrs_status(struct seq_file *m, void *unused) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_drrs *drrs = &dev_priv->drrs; - int vrefresh = 0; - struct drm_connector *connector; + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct drm_connector_list_iter conn_iter; + struct intel_connector *connector; + struct intel_crtc *crtc; - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - bool supported = false; - - if (connector->state->crtc != &crtc->base) - continue; - - seq_printf(m, "%s:\n", connector->name); - - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && - drrs->type == SEAMLESS_DRRS_SUPPORT) - supported = true; - - seq_printf(m, "\tDRRS Supported: %s\n", yesno(supported)); + drm_connector_list_iter_begin(&dev_priv->drm, &conn_iter); + for_each_intel_connector_iter(connector, &conn_iter) { + seq_printf(m, "[CONNECTOR:%d:%s] DRRS type: %s\n", + connector->base.base.id, connector->base.name, + intel_drrs_type_str(intel_panel_drrs_type(connector))); } drm_connector_list_iter_end(&conn_iter); seq_puts(m, "\n"); - if (to_intel_crtc_state(crtc->base.state)->has_drrs) { - struct intel_panel *panel; + for_each_intel_crtc(&dev_priv->drm, crtc) { + const struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); - mutex_lock(&drrs->mutex); - /* DRRS Supported */ - seq_puts(m, "\tDRRS Enabled: Yes\n"); + seq_printf(m, "[CRTC:%d:%s]:\n", + crtc->base.base.id, crtc->base.name); - /* disable_drrs() will make drrs->dp NULL */ - if (!drrs->dp) { - seq_puts(m, "Idleness DRRS: Disabled\n"); - mutex_unlock(&drrs->mutex); - return; - } + mutex_lock(&crtc->drrs.mutex); - panel = &drrs->dp->attached_connector->panel; - seq_printf(m, "\t\tBusy_frontbuffer_bits: 0x%X", - drrs->busy_frontbuffer_bits); - - seq_puts(m, "\n\t\t"); - if (drrs->refresh_rate_type == DRRS_HIGH_RR) { - seq_puts(m, "DRRS_State: DRRS_HIGH_RR\n"); - vrefresh = drm_mode_vrefresh(panel->fixed_mode); - } else if (drrs->refresh_rate_type == DRRS_LOW_RR) { - seq_puts(m, "DRRS_State: DRRS_LOW_RR\n"); - vrefresh = drm_mode_vrefresh(panel->downclock_mode); - } else { - seq_printf(m, "DRRS_State: Unknown(%d)\n", - drrs->refresh_rate_type); - mutex_unlock(&drrs->mutex); - return; - } - seq_printf(m, "\t\tVrefresh: %d", vrefresh); + /* DRRS Supported */ + seq_printf(m, "\tDRRS Enabled: %s\n", + str_yes_no(crtc_state->has_drrs)); - seq_puts(m, "\n\t\t"); - mutex_unlock(&drrs->mutex); - } else { - /* DRRS not supported. Print the VBT parameter*/ - seq_puts(m, "\tDRRS Enabled : No"); - } - seq_puts(m, "\n"); -} + seq_printf(m, "\tDRRS Active: %s\n", + str_yes_no(intel_drrs_is_active(crtc))); -static int i915_drrs_status(struct seq_file *m, void *unused) -{ - struct drm_i915_private *dev_priv = node_to_i915(m->private); - struct drm_device *dev = &dev_priv->drm; - struct intel_crtc *crtc; - int active_crtc_cnt = 0; + seq_printf(m, "\tBusy_frontbuffer_bits: 0x%X\n", + crtc->drrs.busy_frontbuffer_bits); - drm_modeset_lock_all(dev); - for_each_intel_crtc(dev, crtc) { - if (crtc->base.state->active) { - active_crtc_cnt++; - seq_printf(m, "\nCRTC %d: ", active_crtc_cnt); + seq_printf(m, "\tDRRS refresh rate: %s\n", + crtc->drrs.refresh_rate == DRRS_REFRESH_RATE_LOW ? + "low" : "high"); - drrs_status_per_crtc(m, dev, crtc); - } + mutex_unlock(&crtc->drrs.mutex); } - drm_modeset_unlock_all(dev); - - if (!active_crtc_cnt) - seq_puts(m, "No active crtc found\n"); return 0; } @@ -1259,7 +1145,7 @@ static int i915_lpsp_status(struct seq_file *m, void *unused) return 0; } - seq_printf(m, "LPSP: %s\n", enableddisabled(lpsp_enabled)); + seq_printf(m, "LPSP: %s\n", str_enabled_disabled(lpsp_enabled)); return 0; } @@ -1740,7 +1626,7 @@ static int i915_hpd_storm_ctl_show(struct seq_file *m, void *data) seq_printf(m, "Threshold: %d\n", hotplug->hpd_storm_threshold); seq_printf(m, "Detected: %s\n", - yesno(delayed_work_pending(&hotplug->reenable_work))); + str_yes_no(delayed_work_pending(&hotplug->reenable_work))); return 0; } @@ -1814,7 +1700,7 @@ static int i915_hpd_short_storm_ctl_show(struct seq_file *m, void *data) struct drm_i915_private *dev_priv = m->private; seq_printf(m, "Enabled: %s\n", - yesno(dev_priv->hotplug.hpd_short_storm_enabled)); + str_yes_no(dev_priv->hotplug.hpd_short_storm_enabled)); return 0; } @@ -1888,13 +1774,8 @@ static int i915_drrs_ctl_set(void *data, u64 val) struct drm_device *dev = &dev_priv->drm; struct intel_crtc *crtc; - if (DISPLAY_VER(dev_priv) < 7) - return -ENODEV; - for_each_intel_crtc(dev, crtc) { - struct drm_connector_list_iter conn_iter; struct intel_crtc_state *crtc_state; - struct drm_connector *connector; struct drm_crtc_commit *commit; int ret; @@ -1915,30 +1796,13 @@ static int i915_drrs_ctl_set(void *data, u64 val) goto out; } - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - struct intel_encoder *encoder; - struct intel_dp *intel_dp; - - if (!(crtc_state->uapi.connector_mask & - drm_connector_mask(connector))) - continue; - - encoder = intel_attached_encoder(to_intel_connector(connector)); - if (encoder->type != INTEL_OUTPUT_EDP) - continue; - - drm_dbg(&dev_priv->drm, - "Manually %sabling DRRS. %llu\n", - val ? "en" : "dis", val); + drm_dbg(&dev_priv->drm, + "Manually %sactivating DRRS\n", val ? "" : "de"); - intel_dp = enc_to_intel_dp(encoder); - if (val) - intel_drrs_enable(intel_dp, crtc_state); - else - intel_drrs_disable(intel_dp, crtc_state); - } - drm_connector_list_iter_end(&conn_iter); + if (val) + intel_drrs_activate(crtc_state); + else + intel_drrs_deactivate(crtc_state); out: drm_modeset_unlock(&crtc->base.mutex); @@ -2020,7 +1884,6 @@ static const struct drm_info_list intel_display_debugfs_list[] = { {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0}, {"i915_edp_psr_status", i915_edp_psr_status, 0}, {"i915_power_domain_info", i915_power_domain_info, 0}, - {"i915_dmc_info", i915_dmc_info, 0}, {"i915_display_info", i915_display_info, 0}, {"i915_shared_dplls_info", i915_shared_dplls_info, 0}, {"i915_dp_mst_info", i915_dp_mst_info, 0}, @@ -2064,6 +1927,7 @@ void intel_display_debugfs_register(struct drm_i915_private *i915) ARRAY_SIZE(intel_display_debugfs_list), minor->debugfs_root, minor); + intel_dmc_debugfs_register(i915); intel_fbc_debugfs_register(i915); } @@ -2209,14 +2073,14 @@ static int i915_dsc_fec_support_show(struct seq_file *m, void *data) intel_dp = intel_attached_dp(to_intel_connector(connector)); crtc_state = to_intel_crtc_state(crtc->state); seq_printf(m, "DSC_Enabled: %s\n", - yesno(crtc_state->dsc.compression_enable)); + str_yes_no(crtc_state->dsc.compression_enable)); seq_printf(m, "DSC_Sink_Support: %s\n", - yesno(drm_dp_sink_supports_dsc(intel_dp->dsc_dpcd))); + str_yes_no(drm_dp_sink_supports_dsc(intel_dp->dsc_dpcd))); seq_printf(m, "Force_DSC_Enable: %s\n", - yesno(intel_dp->force_dsc_en)); + str_yes_no(intel_dp->force_dsc_en)); if (!intel_dp_is_edp(intel_dp)) seq_printf(m, "FEC_Sink_Support: %s\n", - yesno(drm_dp_sink_supports_fec(intel_dp->fec_capable))); + str_yes_no(drm_dp_sink_supports_fec(intel_dp->fec_capable))); } while (try_again); drm_modeset_drop_locks(&ctx); diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 9ebae7ac3235..949edc983a16 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -3,123 +3,32 @@ * Copyright © 2019 Intel Corporation */ +#include <linux/string_helpers.h> + #include "i915_drv.h" #include "i915_irq.h" #include "intel_cdclk.h" #include "intel_combo_phy.h" -#include "intel_combo_phy_regs.h" -#include "intel_crt.h" #include "intel_de.h" #include "intel_display_power.h" +#include "intel_display_power_map.h" +#include "intel_display_power_well.h" #include "intel_display_types.h" #include "intel_dmc.h" -#include "intel_dpio_phy.h" -#include "intel_dpll.h" -#include "intel_hotplug.h" #include "intel_mchbar_regs.h" #include "intel_pch_refclk.h" #include "intel_pcode.h" #include "intel_pm.h" -#include "intel_pps.h" #include "intel_snps_phy.h" -#include "intel_tc.h" -#include "intel_vga.h" #include "vlv_sideband.h" -struct i915_power_well_ops { - /* - * Synchronize the well's hw state to match the current sw state, for - * example enable/disable it based on the current refcount. Called - * during driver init and resume time, possibly after first calling - * the enable/disable handlers. - */ - void (*sync_hw)(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well); - /* - * Enable the well and resources that depend on it (for example - * interrupts located on the well). Called after the 0->1 refcount - * transition. - */ - void (*enable)(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well); - /* - * Disable the well and resources that depend on it. Called after - * the 1->0 refcount transition. - */ - void (*disable)(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well); - /* Returns the hw enabled state. */ - bool (*is_enabled)(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well); -}; +#define for_each_power_domain_well(__dev_priv, __power_well, __domain) \ + for_each_power_well(__dev_priv, __power_well) \ + for_each_if(test_bit((__domain), (__power_well)->domains.bits)) -struct i915_power_well_regs { - i915_reg_t bios; - i915_reg_t driver; - i915_reg_t kvmr; - i915_reg_t debug; -}; - -/* Power well structure for haswell */ -struct i915_power_well_desc { - const char *name; - bool always_on; - u64 domains; - /* unique identifier for this power well */ - enum i915_power_well_id id; - /* - * Arbitraty data associated with this power well. Platform and power - * well specific. - */ - union { - struct { - /* - * request/status flag index in the PUNIT power well - * control/status registers. - */ - u8 idx; - } vlv; - struct { - enum dpio_phy phy; - } bxt; - struct { - const struct i915_power_well_regs *regs; - /* - * request/status flag index in the power well - * constrol/status registers. - */ - u8 idx; - /* Mask of pipes whose IRQ logic is backed by the pw */ - u8 irq_pipe_mask; - /* - * Instead of waiting for the status bit to ack enables, - * just wait a specific amount of time and then consider - * the well enabled. - */ - u16 fixed_enable_delay; - /* The pw is backing the VGA functionality */ - bool has_vga:1; - bool has_fuses:1; - /* - * The pw is for an ICL+ TypeC PHY port in - * Thunderbolt mode. - */ - bool is_tc_tbt:1; - } hsw; - }; - const struct i915_power_well_ops *ops; -}; - -struct i915_power_well { - const struct i915_power_well_desc *desc; - /* power well enable/disable usage count */ - int count; - /* cached hw enabled state */ - bool hw_enabled; -}; - -bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, - enum i915_power_well_id power_well_id); +#define for_each_power_domain_well_reverse(__dev_priv, __power_well, __domain) \ + for_each_power_well_reverse(__dev_priv, __power_well) \ + for_each_if(test_bit((__domain), (__power_well)->domains.bits)) const char * intel_display_power_domain_str(enum intel_display_power_domain domain) @@ -135,14 +44,14 @@ intel_display_power_domain_str(enum intel_display_power_domain domain) return "PIPE_C"; case POWER_DOMAIN_PIPE_D: return "PIPE_D"; - case POWER_DOMAIN_PIPE_A_PANEL_FITTER: - return "PIPE_A_PANEL_FITTER"; - case POWER_DOMAIN_PIPE_B_PANEL_FITTER: - return "PIPE_B_PANEL_FITTER"; - case POWER_DOMAIN_PIPE_C_PANEL_FITTER: - return "PIPE_C_PANEL_FITTER"; - case POWER_DOMAIN_PIPE_D_PANEL_FITTER: - return "PIPE_D_PANEL_FITTER"; + case POWER_DOMAIN_PIPE_PANEL_FITTER_A: + return "PIPE_PANEL_FITTER_A"; + case POWER_DOMAIN_PIPE_PANEL_FITTER_B: + return "PIPE_PANEL_FITTER_B"; + case POWER_DOMAIN_PIPE_PANEL_FITTER_C: + return "PIPE_PANEL_FITTER_C"; + case POWER_DOMAIN_PIPE_PANEL_FITTER_D: + return "PIPE_PANEL_FITTER_D"; case POWER_DOMAIN_TRANSCODER_A: return "TRANSCODER_A"; case POWER_DOMAIN_TRANSCODER_B: @@ -153,48 +62,60 @@ intel_display_power_domain_str(enum intel_display_power_domain domain) return "TRANSCODER_D"; case POWER_DOMAIN_TRANSCODER_EDP: return "TRANSCODER_EDP"; - case POWER_DOMAIN_TRANSCODER_VDSC_PW2: - return "TRANSCODER_VDSC_PW2"; case POWER_DOMAIN_TRANSCODER_DSI_A: return "TRANSCODER_DSI_A"; case POWER_DOMAIN_TRANSCODER_DSI_C: return "TRANSCODER_DSI_C"; - case POWER_DOMAIN_PORT_DDI_A_LANES: - return "PORT_DDI_A_LANES"; - case POWER_DOMAIN_PORT_DDI_B_LANES: - return "PORT_DDI_B_LANES"; - case POWER_DOMAIN_PORT_DDI_C_LANES: - return "PORT_DDI_C_LANES"; - case POWER_DOMAIN_PORT_DDI_D_LANES: - return "PORT_DDI_D_LANES"; - case POWER_DOMAIN_PORT_DDI_E_LANES: - return "PORT_DDI_E_LANES"; - case POWER_DOMAIN_PORT_DDI_F_LANES: - return "PORT_DDI_F_LANES"; - case POWER_DOMAIN_PORT_DDI_G_LANES: - return "PORT_DDI_G_LANES"; - case POWER_DOMAIN_PORT_DDI_H_LANES: - return "PORT_DDI_H_LANES"; - case POWER_DOMAIN_PORT_DDI_I_LANES: - return "PORT_DDI_I_LANES"; - case POWER_DOMAIN_PORT_DDI_A_IO: - return "PORT_DDI_A_IO"; - case POWER_DOMAIN_PORT_DDI_B_IO: - return "PORT_DDI_B_IO"; - case POWER_DOMAIN_PORT_DDI_C_IO: - return "PORT_DDI_C_IO"; - case POWER_DOMAIN_PORT_DDI_D_IO: - return "PORT_DDI_D_IO"; - case POWER_DOMAIN_PORT_DDI_E_IO: - return "PORT_DDI_E_IO"; - case POWER_DOMAIN_PORT_DDI_F_IO: - return "PORT_DDI_F_IO"; - case POWER_DOMAIN_PORT_DDI_G_IO: - return "PORT_DDI_G_IO"; - case POWER_DOMAIN_PORT_DDI_H_IO: - return "PORT_DDI_H_IO"; - case POWER_DOMAIN_PORT_DDI_I_IO: - return "PORT_DDI_I_IO"; + case POWER_DOMAIN_TRANSCODER_VDSC_PW2: + return "TRANSCODER_VDSC_PW2"; + case POWER_DOMAIN_PORT_DDI_LANES_A: + return "PORT_DDI_LANES_A"; + case POWER_DOMAIN_PORT_DDI_LANES_B: + return "PORT_DDI_LANES_B"; + case POWER_DOMAIN_PORT_DDI_LANES_C: + return "PORT_DDI_LANES_C"; + case POWER_DOMAIN_PORT_DDI_LANES_D: + return "PORT_DDI_LANES_D"; + case POWER_DOMAIN_PORT_DDI_LANES_E: + return "PORT_DDI_LANES_E"; + case POWER_DOMAIN_PORT_DDI_LANES_F: + return "PORT_DDI_LANES_F"; + case POWER_DOMAIN_PORT_DDI_LANES_TC1: + return "PORT_DDI_LANES_TC1"; + case POWER_DOMAIN_PORT_DDI_LANES_TC2: + return "PORT_DDI_LANES_TC2"; + case POWER_DOMAIN_PORT_DDI_LANES_TC3: + return "PORT_DDI_LANES_TC3"; + case POWER_DOMAIN_PORT_DDI_LANES_TC4: + return "PORT_DDI_LANES_TC4"; + case POWER_DOMAIN_PORT_DDI_LANES_TC5: + return "PORT_DDI_LANES_TC5"; + case POWER_DOMAIN_PORT_DDI_LANES_TC6: + return "PORT_DDI_LANES_TC6"; + case POWER_DOMAIN_PORT_DDI_IO_A: + return "PORT_DDI_IO_A"; + case POWER_DOMAIN_PORT_DDI_IO_B: + return "PORT_DDI_IO_B"; + case POWER_DOMAIN_PORT_DDI_IO_C: + return "PORT_DDI_IO_C"; + case POWER_DOMAIN_PORT_DDI_IO_D: + return "PORT_DDI_IO_D"; + case POWER_DOMAIN_PORT_DDI_IO_E: + return "PORT_DDI_IO_E"; + case POWER_DOMAIN_PORT_DDI_IO_F: + return "PORT_DDI_IO_F"; + case POWER_DOMAIN_PORT_DDI_IO_TC1: + return "PORT_DDI_IO_TC1"; + case POWER_DOMAIN_PORT_DDI_IO_TC2: + return "PORT_DDI_IO_TC2"; + case POWER_DOMAIN_PORT_DDI_IO_TC3: + return "PORT_DDI_IO_TC3"; + case POWER_DOMAIN_PORT_DDI_IO_TC4: + return "PORT_DDI_IO_TC4"; + case POWER_DOMAIN_PORT_DDI_IO_TC5: + return "PORT_DDI_IO_TC5"; + case POWER_DOMAIN_PORT_DDI_IO_TC6: + return "PORT_DDI_IO_TC6"; case POWER_DOMAIN_PORT_DSI: return "PORT_DSI"; case POWER_DOMAIN_PORT_CRT: @@ -219,28 +140,32 @@ intel_display_power_domain_str(enum intel_display_power_domain domain) return "AUX_E"; case POWER_DOMAIN_AUX_F: return "AUX_F"; - case POWER_DOMAIN_AUX_G: - return "AUX_G"; - case POWER_DOMAIN_AUX_H: - return "AUX_H"; - case POWER_DOMAIN_AUX_I: - return "AUX_I"; + case POWER_DOMAIN_AUX_USBC1: + return "AUX_USBC1"; + case POWER_DOMAIN_AUX_USBC2: + return "AUX_USBC2"; + case POWER_DOMAIN_AUX_USBC3: + return "AUX_USBC3"; + case POWER_DOMAIN_AUX_USBC4: + return "AUX_USBC4"; + case POWER_DOMAIN_AUX_USBC5: + return "AUX_USBC5"; + case POWER_DOMAIN_AUX_USBC6: + return "AUX_USBC6"; case POWER_DOMAIN_AUX_IO_A: return "AUX_IO_A"; - case POWER_DOMAIN_AUX_C_TBT: - return "AUX_C_TBT"; - case POWER_DOMAIN_AUX_D_TBT: - return "AUX_D_TBT"; - case POWER_DOMAIN_AUX_E_TBT: - return "AUX_E_TBT"; - case POWER_DOMAIN_AUX_F_TBT: - return "AUX_F_TBT"; - case POWER_DOMAIN_AUX_G_TBT: - return "AUX_G_TBT"; - case POWER_DOMAIN_AUX_H_TBT: - return "AUX_H_TBT"; - case POWER_DOMAIN_AUX_I_TBT: - return "AUX_I_TBT"; + case POWER_DOMAIN_AUX_TBT1: + return "AUX_TBT1"; + case POWER_DOMAIN_AUX_TBT2: + return "AUX_TBT2"; + case POWER_DOMAIN_AUX_TBT3: + return "AUX_TBT3"; + case POWER_DOMAIN_AUX_TBT4: + return "AUX_TBT4"; + case POWER_DOMAIN_AUX_TBT5: + return "AUX_TBT5"; + case POWER_DOMAIN_AUX_TBT6: + return "AUX_TBT6"; case POWER_DOMAIN_GMBUS: return "GMBUS"; case POWER_DOMAIN_INIT: @@ -259,40 +184,6 @@ intel_display_power_domain_str(enum intel_display_power_domain domain) } } -static void intel_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - drm_dbg_kms(&dev_priv->drm, "enabling %s\n", power_well->desc->name); - power_well->desc->ops->enable(dev_priv, power_well); - power_well->hw_enabled = true; -} - -static void intel_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - drm_dbg_kms(&dev_priv->drm, "disabling %s\n", power_well->desc->name); - power_well->hw_enabled = false; - power_well->desc->ops->disable(dev_priv, power_well); -} - -static void intel_power_well_get(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - if (!power_well->count++) - intel_power_well_enable(dev_priv, power_well); -} - -static void intel_power_well_put(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - drm_WARN(&dev_priv->drm, !power_well->count, - "Use count on power well %s is already zero", - power_well->desc->name); - - if (!--power_well->count) - intel_power_well_disable(dev_priv, power_well); -} - /** * __intel_display_power_is_enabled - unlocked check for a power domain * @dev_priv: i915 device instance @@ -316,11 +207,11 @@ bool __intel_display_power_is_enabled(struct drm_i915_private *dev_priv, is_enabled = true; - for_each_power_domain_well_reverse(dev_priv, power_well, BIT_ULL(domain)) { - if (power_well->desc->always_on) + for_each_power_domain_well_reverse(dev_priv, power_well, domain) { + if (intel_power_well_is_always_on(power_well)) continue; - if (!power_well->hw_enabled) { + if (!intel_power_well_is_enabled_cached(power_well)) { is_enabled = false; break; } @@ -361,634 +252,6 @@ bool intel_display_power_is_enabled(struct drm_i915_private *dev_priv, return ret; } -/* - * Starting with Haswell, we have a "Power Down Well" that can be turned off - * when not needed anymore. We have 4 registers that can request the power well - * to be enabled, and it will only be disabled if none of the registers is - * requesting it to be enabled. - */ -static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv, - u8 irq_pipe_mask, bool has_vga) -{ - if (has_vga) - intel_vga_reset_io_mem(dev_priv); - - if (irq_pipe_mask) - gen8_irq_power_well_post_enable(dev_priv, irq_pipe_mask); -} - -static void hsw_power_well_pre_disable(struct drm_i915_private *dev_priv, - u8 irq_pipe_mask) -{ - if (irq_pipe_mask) - gen8_irq_power_well_pre_disable(dev_priv, irq_pipe_mask); -} - -#define ICL_AUX_PW_TO_CH(pw_idx) \ - ((pw_idx) - ICL_PW_CTL_IDX_AUX_A + AUX_CH_A) - -#define ICL_TBT_AUX_PW_TO_CH(pw_idx) \ - ((pw_idx) - ICL_PW_CTL_IDX_AUX_TBT1 + AUX_CH_C) - -static enum aux_ch icl_aux_pw_to_ch(const struct i915_power_well *power_well) -{ - int pw_idx = power_well->desc->hsw.idx; - - return power_well->desc->hsw.is_tc_tbt ? ICL_TBT_AUX_PW_TO_CH(pw_idx) : - ICL_AUX_PW_TO_CH(pw_idx); -} - -static struct intel_digital_port * -aux_ch_to_digital_port(struct drm_i915_private *dev_priv, - enum aux_ch aux_ch) -{ - struct intel_digital_port *dig_port = NULL; - struct intel_encoder *encoder; - - for_each_intel_encoder(&dev_priv->drm, encoder) { - /* We'll check the MST primary port */ - if (encoder->type == INTEL_OUTPUT_DP_MST) - continue; - - dig_port = enc_to_dig_port(encoder); - if (!dig_port) - continue; - - if (dig_port->aux_ch != aux_ch) { - dig_port = NULL; - continue; - } - - break; - } - - return dig_port; -} - -static enum phy icl_aux_pw_to_phy(struct drm_i915_private *i915, - const struct i915_power_well *power_well) -{ - enum aux_ch aux_ch = icl_aux_pw_to_ch(power_well); - struct intel_digital_port *dig_port = aux_ch_to_digital_port(i915, aux_ch); - - return intel_port_to_phy(i915, dig_port->base.port); -} - -static void hsw_wait_for_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, - bool timeout_expected) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - int pw_idx = power_well->desc->hsw.idx; - int enable_delay = power_well->desc->hsw.fixed_enable_delay; - - /* - * For some power wells we're not supposed to watch the status bit for - * an ack, but rather just wait a fixed amount of time and then - * proceed. This is only used on DG2. - */ - if (IS_DG2(dev_priv) && enable_delay) { - usleep_range(enable_delay, 2 * enable_delay); - return; - } - - /* Timeout for PW1:10 us, AUX:not specified, other PWs:20 us. */ - if (intel_de_wait_for_set(dev_priv, regs->driver, - HSW_PWR_WELL_CTL_STATE(pw_idx), 1)) { - drm_dbg_kms(&dev_priv->drm, "%s power well enable timeout\n", - power_well->desc->name); - - drm_WARN_ON(&dev_priv->drm, !timeout_expected); - - } -} - -static u32 hsw_power_well_requesters(struct drm_i915_private *dev_priv, - const struct i915_power_well_regs *regs, - int pw_idx) -{ - u32 req_mask = HSW_PWR_WELL_CTL_REQ(pw_idx); - u32 ret; - - ret = intel_de_read(dev_priv, regs->bios) & req_mask ? 1 : 0; - ret |= intel_de_read(dev_priv, regs->driver) & req_mask ? 2 : 0; - if (regs->kvmr.reg) - ret |= intel_de_read(dev_priv, regs->kvmr) & req_mask ? 4 : 0; - ret |= intel_de_read(dev_priv, regs->debug) & req_mask ? 8 : 0; - - return ret; -} - -static void hsw_wait_for_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - int pw_idx = power_well->desc->hsw.idx; - bool disabled; - u32 reqs; - - /* - * Bspec doesn't require waiting for PWs to get disabled, but still do - * this for paranoia. The known cases where a PW will be forced on: - * - a KVMR request on any power well via the KVMR request register - * - a DMC request on PW1 and MISC_IO power wells via the BIOS and - * DEBUG request registers - * Skip the wait in case any of the request bits are set and print a - * diagnostic message. - */ - wait_for((disabled = !(intel_de_read(dev_priv, regs->driver) & - HSW_PWR_WELL_CTL_STATE(pw_idx))) || - (reqs = hsw_power_well_requesters(dev_priv, regs, pw_idx)), 1); - if (disabled) - return; - - drm_dbg_kms(&dev_priv->drm, - "%s forced on (bios:%d driver:%d kvmr:%d debug:%d)\n", - power_well->desc->name, - !!(reqs & 1), !!(reqs & 2), !!(reqs & 4), !!(reqs & 8)); -} - -static void gen9_wait_for_power_well_fuses(struct drm_i915_private *dev_priv, - enum skl_power_gate pg) -{ - /* Timeout 5us for PG#0, for other PGs 1us */ - drm_WARN_ON(&dev_priv->drm, - intel_de_wait_for_set(dev_priv, SKL_FUSE_STATUS, - SKL_FUSE_PG_DIST_STATUS(pg), 1)); -} - -static void hsw_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - int pw_idx = power_well->desc->hsw.idx; - u32 val; - - if (power_well->desc->hsw.has_fuses) { - enum skl_power_gate pg; - - pg = DISPLAY_VER(dev_priv) >= 11 ? ICL_PW_CTL_IDX_TO_PG(pw_idx) : - SKL_PW_CTL_IDX_TO_PG(pw_idx); - - /* Wa_16013190616:adlp */ - if (IS_ALDERLAKE_P(dev_priv) && pg == SKL_PG1) - intel_de_rmw(dev_priv, GEN8_CHICKEN_DCPR_1, 0, DISABLE_FLR_SRC); - - /* - * For PW1 we have to wait both for the PW0/PG0 fuse state - * before enabling the power well and PW1/PG1's own fuse - * state after the enabling. For all other power wells with - * fuses we only have to wait for that PW/PG's fuse state - * after the enabling. - */ - if (pg == SKL_PG1) - gen9_wait_for_power_well_fuses(dev_priv, SKL_PG0); - } - - val = intel_de_read(dev_priv, regs->driver); - intel_de_write(dev_priv, regs->driver, - val | HSW_PWR_WELL_CTL_REQ(pw_idx)); - - hsw_wait_for_power_well_enable(dev_priv, power_well, false); - - if (power_well->desc->hsw.has_fuses) { - enum skl_power_gate pg; - - pg = DISPLAY_VER(dev_priv) >= 11 ? ICL_PW_CTL_IDX_TO_PG(pw_idx) : - SKL_PW_CTL_IDX_TO_PG(pw_idx); - gen9_wait_for_power_well_fuses(dev_priv, pg); - } - - hsw_power_well_post_enable(dev_priv, - power_well->desc->hsw.irq_pipe_mask, - power_well->desc->hsw.has_vga); -} - -static void hsw_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - int pw_idx = power_well->desc->hsw.idx; - u32 val; - - hsw_power_well_pre_disable(dev_priv, - power_well->desc->hsw.irq_pipe_mask); - - val = intel_de_read(dev_priv, regs->driver); - intel_de_write(dev_priv, regs->driver, - val & ~HSW_PWR_WELL_CTL_REQ(pw_idx)); - hsw_wait_for_power_well_disable(dev_priv, power_well); -} - -static void -icl_combo_phy_aux_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - int pw_idx = power_well->desc->hsw.idx; - enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); - u32 val; - - drm_WARN_ON(&dev_priv->drm, !IS_ICELAKE(dev_priv)); - - val = intel_de_read(dev_priv, regs->driver); - intel_de_write(dev_priv, regs->driver, - val | HSW_PWR_WELL_CTL_REQ(pw_idx)); - - if (DISPLAY_VER(dev_priv) < 12) { - val = intel_de_read(dev_priv, ICL_PORT_CL_DW12(phy)); - intel_de_write(dev_priv, ICL_PORT_CL_DW12(phy), - val | ICL_LANE_ENABLE_AUX); - } - - hsw_wait_for_power_well_enable(dev_priv, power_well, false); - - /* Display WA #1178: icl */ - if (pw_idx >= ICL_PW_CTL_IDX_AUX_A && pw_idx <= ICL_PW_CTL_IDX_AUX_B && - !intel_bios_is_port_edp(dev_priv, (enum port)phy)) { - val = intel_de_read(dev_priv, ICL_AUX_ANAOVRD1(pw_idx)); - val |= ICL_AUX_ANAOVRD1_ENABLE | ICL_AUX_ANAOVRD1_LDO_BYPASS; - intel_de_write(dev_priv, ICL_AUX_ANAOVRD1(pw_idx), val); - } -} - -static void -icl_combo_phy_aux_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - int pw_idx = power_well->desc->hsw.idx; - enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); - u32 val; - - drm_WARN_ON(&dev_priv->drm, !IS_ICELAKE(dev_priv)); - - val = intel_de_read(dev_priv, ICL_PORT_CL_DW12(phy)); - intel_de_write(dev_priv, ICL_PORT_CL_DW12(phy), - val & ~ICL_LANE_ENABLE_AUX); - - val = intel_de_read(dev_priv, regs->driver); - intel_de_write(dev_priv, regs->driver, - val & ~HSW_PWR_WELL_CTL_REQ(pw_idx)); - - hsw_wait_for_power_well_disable(dev_priv, power_well); -} - -#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) - -static u64 async_put_domains_mask(struct i915_power_domains *power_domains); - -static int power_well_async_ref_count(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - int refs = hweight64(power_well->desc->domains & - async_put_domains_mask(&dev_priv->power_domains)); - - drm_WARN_ON(&dev_priv->drm, refs > power_well->count); - - return refs; -} - -static void icl_tc_port_assert_ref_held(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, - struct intel_digital_port *dig_port) -{ - /* Bypass the check if all references are released asynchronously */ - if (power_well_async_ref_count(dev_priv, power_well) == - power_well->count) - return; - - if (drm_WARN_ON(&dev_priv->drm, !dig_port)) - return; - - if (DISPLAY_VER(dev_priv) == 11 && intel_tc_cold_requires_aux_pw(dig_port)) - return; - - drm_WARN_ON(&dev_priv->drm, !intel_tc_port_ref_held(dig_port)); -} - -#else - -static void icl_tc_port_assert_ref_held(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, - struct intel_digital_port *dig_port) -{ -} - -#endif - -#define TGL_AUX_PW_TO_TC_PORT(pw_idx) ((pw_idx) - TGL_PW_CTL_IDX_AUX_TC1) - -static void icl_tc_cold_exit(struct drm_i915_private *i915) -{ - int ret, tries = 0; - - while (1) { - ret = snb_pcode_write_timeout(i915, ICL_PCODE_EXIT_TCCOLD, 0, - 250, 1); - if (ret != -EAGAIN || ++tries == 3) - break; - msleep(1); - } - - /* Spec states that TC cold exit can take up to 1ms to complete */ - if (!ret) - msleep(1); - - /* TODO: turn failure into a error as soon i915 CI updates ICL IFWI */ - drm_dbg_kms(&i915->drm, "TC cold block %s\n", ret ? "failed" : - "succeeded"); -} - -static void -icl_tc_phy_aux_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum aux_ch aux_ch = icl_aux_pw_to_ch(power_well); - struct intel_digital_port *dig_port = aux_ch_to_digital_port(dev_priv, aux_ch); - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - bool is_tbt = power_well->desc->hsw.is_tc_tbt; - bool timeout_expected; - u32 val; - - icl_tc_port_assert_ref_held(dev_priv, power_well, dig_port); - - val = intel_de_read(dev_priv, DP_AUX_CH_CTL(aux_ch)); - val &= ~DP_AUX_CH_CTL_TBT_IO; - if (is_tbt) - val |= DP_AUX_CH_CTL_TBT_IO; - intel_de_write(dev_priv, DP_AUX_CH_CTL(aux_ch), val); - - val = intel_de_read(dev_priv, regs->driver); - intel_de_write(dev_priv, regs->driver, - val | HSW_PWR_WELL_CTL_REQ(power_well->desc->hsw.idx)); - - /* - * An AUX timeout is expected if the TBT DP tunnel is down, - * or need to enable AUX on a legacy TypeC port as part of the TC-cold - * exit sequence. - */ - timeout_expected = is_tbt || intel_tc_cold_requires_aux_pw(dig_port); - if (DISPLAY_VER(dev_priv) == 11 && intel_tc_cold_requires_aux_pw(dig_port)) - icl_tc_cold_exit(dev_priv); - - hsw_wait_for_power_well_enable(dev_priv, power_well, timeout_expected); - - if (DISPLAY_VER(dev_priv) >= 12 && !is_tbt) { - enum tc_port tc_port; - - tc_port = TGL_AUX_PW_TO_TC_PORT(power_well->desc->hsw.idx); - intel_de_write(dev_priv, HIP_INDEX_REG(tc_port), - HIP_INDEX_VAL(tc_port, 0x2)); - - if (intel_de_wait_for_set(dev_priv, DKL_CMN_UC_DW_27(tc_port), - DKL_CMN_UC_DW27_UC_HEALTH, 1)) - drm_warn(&dev_priv->drm, - "Timeout waiting TC uC health\n"); - } -} - -static void -icl_tc_phy_aux_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum aux_ch aux_ch = icl_aux_pw_to_ch(power_well); - struct intel_digital_port *dig_port = aux_ch_to_digital_port(dev_priv, aux_ch); - - icl_tc_port_assert_ref_held(dev_priv, power_well, dig_port); - - hsw_power_well_disable(dev_priv, power_well); -} - -static void -icl_aux_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); - - if (intel_phy_is_tc(dev_priv, phy)) - return icl_tc_phy_aux_power_well_enable(dev_priv, power_well); - else if (IS_ICELAKE(dev_priv)) - return icl_combo_phy_aux_power_well_enable(dev_priv, - power_well); - else - return hsw_power_well_enable(dev_priv, power_well); -} - -static void -icl_aux_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); - - if (intel_phy_is_tc(dev_priv, phy)) - return icl_tc_phy_aux_power_well_disable(dev_priv, power_well); - else if (IS_ICELAKE(dev_priv)) - return icl_combo_phy_aux_power_well_disable(dev_priv, - power_well); - else - return hsw_power_well_disable(dev_priv, power_well); -} - -/* - * We should only use the power well if we explicitly asked the hardware to - * enable it, so check if it's enabled and also check if we've requested it to - * be enabled. - */ -static bool hsw_power_well_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - enum i915_power_well_id id = power_well->desc->id; - int pw_idx = power_well->desc->hsw.idx; - u32 mask = HSW_PWR_WELL_CTL_REQ(pw_idx) | - HSW_PWR_WELL_CTL_STATE(pw_idx); - u32 val; - - val = intel_de_read(dev_priv, regs->driver); - - /* - * On GEN9 big core due to a DMC bug the driver's request bits for PW1 - * and the MISC_IO PW will be not restored, so check instead for the - * BIOS's own request bits, which are forced-on for these power wells - * when exiting DC5/6. - */ - if (DISPLAY_VER(dev_priv) == 9 && !IS_BROXTON(dev_priv) && - (id == SKL_DISP_PW_1 || id == SKL_DISP_PW_MISC_IO)) - val |= intel_de_read(dev_priv, regs->bios); - - return (val & mask) == mask; -} - -static void assert_can_enable_dc9(struct drm_i915_private *dev_priv) -{ - drm_WARN_ONCE(&dev_priv->drm, - (intel_de_read(dev_priv, DC_STATE_EN) & DC_STATE_EN_DC9), - "DC9 already programmed to be enabled.\n"); - drm_WARN_ONCE(&dev_priv->drm, - intel_de_read(dev_priv, DC_STATE_EN) & - DC_STATE_EN_UPTO_DC5, - "DC5 still not disabled to enable DC9.\n"); - drm_WARN_ONCE(&dev_priv->drm, - intel_de_read(dev_priv, HSW_PWR_WELL_CTL2) & - HSW_PWR_WELL_CTL_REQ(SKL_PW_CTL_IDX_PW_2), - "Power well 2 on.\n"); - drm_WARN_ONCE(&dev_priv->drm, intel_irqs_enabled(dev_priv), - "Interrupts not disabled yet.\n"); - - /* - * TODO: check for the following to verify the conditions to enter DC9 - * state are satisfied: - * 1] Check relevant display engine registers to verify if mode set - * disable sequence was followed. - * 2] Check if display uninitialize sequence is initialized. - */ -} - -static void assert_can_disable_dc9(struct drm_i915_private *dev_priv) -{ - drm_WARN_ONCE(&dev_priv->drm, intel_irqs_enabled(dev_priv), - "Interrupts not disabled yet.\n"); - drm_WARN_ONCE(&dev_priv->drm, - intel_de_read(dev_priv, DC_STATE_EN) & - DC_STATE_EN_UPTO_DC5, - "DC5 still not disabled.\n"); - - /* - * TODO: check for the following to verify DC9 state was indeed - * entered before programming to disable it: - * 1] Check relevant display engine registers to verify if mode - * set disable sequence was followed. - * 2] Check if display uninitialize sequence is initialized. - */ -} - -static void gen9_write_dc_state(struct drm_i915_private *dev_priv, - u32 state) -{ - int rewrites = 0; - int rereads = 0; - u32 v; - - intel_de_write(dev_priv, DC_STATE_EN, state); - - /* It has been observed that disabling the dc6 state sometimes - * doesn't stick and dmc keeps returning old value. Make sure - * the write really sticks enough times and also force rewrite until - * we are confident that state is exactly what we want. - */ - do { - v = intel_de_read(dev_priv, DC_STATE_EN); - - if (v != state) { - intel_de_write(dev_priv, DC_STATE_EN, state); - rewrites++; - rereads = 0; - } else if (rereads++ > 5) { - break; - } - - } while (rewrites < 100); - - if (v != state) - drm_err(&dev_priv->drm, - "Writing dc state to 0x%x failed, now 0x%x\n", - state, v); - - /* Most of the times we need one retry, avoid spam */ - if (rewrites > 1) - drm_dbg_kms(&dev_priv->drm, - "Rewrote dc state to 0x%x %d times\n", - state, rewrites); -} - -static u32 gen9_dc_mask(struct drm_i915_private *dev_priv) -{ - u32 mask; - - mask = DC_STATE_EN_UPTO_DC5; - - if (DISPLAY_VER(dev_priv) >= 12) - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6 - | DC_STATE_EN_DC9; - else if (DISPLAY_VER(dev_priv) == 11) - mask |= DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; - else if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) - mask |= DC_STATE_EN_DC9; - else - mask |= DC_STATE_EN_UPTO_DC6; - - return mask; -} - -static void gen9_sanitize_dc_state(struct drm_i915_private *dev_priv) -{ - u32 val; - - if (!HAS_DISPLAY(dev_priv)) - return; - - val = intel_de_read(dev_priv, DC_STATE_EN) & gen9_dc_mask(dev_priv); - - drm_dbg_kms(&dev_priv->drm, - "Resetting DC state tracking from %02x to %02x\n", - dev_priv->dmc.dc_state, val); - dev_priv->dmc.dc_state = val; -} - -/** - * gen9_set_dc_state - set target display C power state - * @dev_priv: i915 device instance - * @state: target DC power state - * - DC_STATE_DISABLE - * - DC_STATE_EN_UPTO_DC5 - * - DC_STATE_EN_UPTO_DC6 - * - DC_STATE_EN_DC9 - * - * Signal to DMC firmware/HW the target DC power state passed in @state. - * DMC/HW can turn off individual display clocks and power rails when entering - * a deeper DC power state (higher in number) and turns these back when exiting - * that state to a shallower power state (lower in number). The HW will decide - * when to actually enter a given state on an on-demand basis, for instance - * depending on the active state of display pipes. The state of display - * registers backed by affected power rails are saved/restored as needed. - * - * Based on the above enabling a deeper DC power state is asynchronous wrt. - * enabling it. Disabling a deeper power state is synchronous: for instance - * setting %DC_STATE_DISABLE won't complete until all HW resources are turned - * back on and register state is restored. This is guaranteed by the MMIO write - * to DC_STATE_EN blocking until the state is restored. - */ -static void gen9_set_dc_state(struct drm_i915_private *dev_priv, u32 state) -{ - u32 val; - u32 mask; - - if (!HAS_DISPLAY(dev_priv)) - return; - - if (drm_WARN_ON_ONCE(&dev_priv->drm, - state & ~dev_priv->dmc.allowed_dc_mask)) - state &= dev_priv->dmc.allowed_dc_mask; - - val = intel_de_read(dev_priv, DC_STATE_EN); - mask = gen9_dc_mask(dev_priv); - drm_dbg_kms(&dev_priv->drm, "Setting DC state from %02x to %02x\n", - val & mask, state); - - /* Check if DMC is ignoring our DC state requests */ - if ((val & mask) != dev_priv->dmc.dc_state) - drm_err(&dev_priv->drm, "DC state mismatch (0x%x -> 0x%x)\n", - dev_priv->dmc.dc_state, val & mask); - - val &= ~mask; - val |= state; - - gen9_write_dc_state(dev_priv, val); - - dev_priv->dmc.dc_state = val & mask; -} - static u32 sanitize_target_dc_state(struct drm_i915_private *dev_priv, u32 target_dc_state) @@ -1014,88 +277,6 @@ sanitize_target_dc_state(struct drm_i915_private *dev_priv, return target_dc_state; } -static void tgl_enable_dc3co(struct drm_i915_private *dev_priv) -{ - drm_dbg_kms(&dev_priv->drm, "Enabling DC3CO\n"); - gen9_set_dc_state(dev_priv, DC_STATE_EN_DC3CO); -} - -static void tgl_disable_dc3co(struct drm_i915_private *dev_priv) -{ - u32 val; - - drm_dbg_kms(&dev_priv->drm, "Disabling DC3CO\n"); - val = intel_de_read(dev_priv, DC_STATE_EN); - val &= ~DC_STATE_DC3CO_STATUS; - intel_de_write(dev_priv, DC_STATE_EN, val); - gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); - /* - * Delay of 200us DC3CO Exit time B.Spec 49196 - */ - usleep_range(200, 210); -} - -static void bxt_enable_dc9(struct drm_i915_private *dev_priv) -{ - assert_can_enable_dc9(dev_priv); - - drm_dbg_kms(&dev_priv->drm, "Enabling DC9\n"); - /* - * Power sequencer reset is not needed on - * platforms with South Display Engine on PCH, - * because PPS registers are always on. - */ - if (!HAS_PCH_SPLIT(dev_priv)) - intel_pps_reset_all(dev_priv); - gen9_set_dc_state(dev_priv, DC_STATE_EN_DC9); -} - -static void bxt_disable_dc9(struct drm_i915_private *dev_priv) -{ - assert_can_disable_dc9(dev_priv); - - drm_dbg_kms(&dev_priv->drm, "Disabling DC9\n"); - - gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); - - intel_pps_unlock_regs_wa(dev_priv); -} - -static void assert_dmc_loaded(struct drm_i915_private *dev_priv) -{ - drm_WARN_ONCE(&dev_priv->drm, - !intel_de_read(dev_priv, - DMC_PROGRAM(dev_priv->dmc.dmc_info[DMC_FW_MAIN].start_mmioaddr, 0)), - "DMC program storage start is NULL\n"); - drm_WARN_ONCE(&dev_priv->drm, !intel_de_read(dev_priv, DMC_SSP_BASE), - "DMC SSP Base Not fine\n"); - drm_WARN_ONCE(&dev_priv->drm, !intel_de_read(dev_priv, DMC_HTP_SKL), - "DMC HTP Not fine\n"); -} - -static struct i915_power_well * -lookup_power_well(struct drm_i915_private *dev_priv, - enum i915_power_well_id power_well_id) -{ - struct i915_power_well *power_well; - - for_each_power_well(dev_priv, power_well) - if (power_well->desc->id == power_well_id) - return power_well; - - /* - * It's not feasible to add error checking code to the callers since - * this condition really shouldn't happen and it doesn't even make sense - * to abort things like display initialization sequences. Just return - * the first power well and hope the WARN gets reported so we can fix - * our driver. - */ - drm_WARN(&dev_priv->drm, 1, - "Power well %d not defined for this platform\n", - power_well_id); - return &dev_priv->power_domains.power_wells[0]; -} - /** * intel_display_power_set_target_dc_state - Set target dc state. * @dev_priv: i915 device @@ -1123,934 +304,32 @@ void intel_display_power_set_target_dc_state(struct drm_i915_private *dev_priv, if (state == dev_priv->dmc.target_dc_state) goto unlock; - dc_off_enabled = power_well->desc->ops->is_enabled(dev_priv, - power_well); + dc_off_enabled = intel_power_well_is_enabled(dev_priv, power_well); /* * If DC off power well is disabled, need to enable and disable the * DC off power well to effect target DC state. */ if (!dc_off_enabled) - power_well->desc->ops->enable(dev_priv, power_well); + intel_power_well_enable(dev_priv, power_well); dev_priv->dmc.target_dc_state = state; if (!dc_off_enabled) - power_well->desc->ops->disable(dev_priv, power_well); + intel_power_well_disable(dev_priv, power_well); unlock: mutex_unlock(&power_domains->lock); } -static void assert_can_enable_dc5(struct drm_i915_private *dev_priv) -{ - enum i915_power_well_id high_pg; - - /* Power wells at this level and above must be disabled for DC5 entry */ - if (DISPLAY_VER(dev_priv) == 12) - high_pg = ICL_DISP_PW_3; - else - high_pg = SKL_DISP_PW_2; - - drm_WARN_ONCE(&dev_priv->drm, - intel_display_power_well_is_enabled(dev_priv, high_pg), - "Power wells above platform's DC5 limit still enabled.\n"); - - drm_WARN_ONCE(&dev_priv->drm, - (intel_de_read(dev_priv, DC_STATE_EN) & - DC_STATE_EN_UPTO_DC5), - "DC5 already programmed to be enabled.\n"); - assert_rpm_wakelock_held(&dev_priv->runtime_pm); - - assert_dmc_loaded(dev_priv); -} - -static void gen9_enable_dc5(struct drm_i915_private *dev_priv) -{ - assert_can_enable_dc5(dev_priv); - - drm_dbg_kms(&dev_priv->drm, "Enabling DC5\n"); - - /* Wa Display #1183: skl,kbl,cfl */ - if (DISPLAY_VER(dev_priv) == 9 && !IS_BROXTON(dev_priv)) - intel_de_write(dev_priv, GEN8_CHICKEN_DCPR_1, - intel_de_read(dev_priv, GEN8_CHICKEN_DCPR_1) | SKL_SELECT_ALTERNATE_DC_EXIT); - - gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC5); -} - -static void assert_can_enable_dc6(struct drm_i915_private *dev_priv) -{ - drm_WARN_ONCE(&dev_priv->drm, - intel_de_read(dev_priv, UTIL_PIN_CTL) & UTIL_PIN_ENABLE, - "Backlight is not disabled.\n"); - drm_WARN_ONCE(&dev_priv->drm, - (intel_de_read(dev_priv, DC_STATE_EN) & - DC_STATE_EN_UPTO_DC6), - "DC6 already programmed to be enabled.\n"); - - assert_dmc_loaded(dev_priv); -} - -static void skl_enable_dc6(struct drm_i915_private *dev_priv) -{ - assert_can_enable_dc6(dev_priv); - - drm_dbg_kms(&dev_priv->drm, "Enabling DC6\n"); - - /* Wa Display #1183: skl,kbl,cfl */ - if (DISPLAY_VER(dev_priv) == 9 && !IS_BROXTON(dev_priv)) - intel_de_write(dev_priv, GEN8_CHICKEN_DCPR_1, - intel_de_read(dev_priv, GEN8_CHICKEN_DCPR_1) | SKL_SELECT_ALTERNATE_DC_EXIT); - - gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); -} - -static void hsw_power_well_sync_hw(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - const struct i915_power_well_regs *regs = power_well->desc->hsw.regs; - int pw_idx = power_well->desc->hsw.idx; - u32 mask = HSW_PWR_WELL_CTL_REQ(pw_idx); - u32 bios_req = intel_de_read(dev_priv, regs->bios); - - /* Take over the request bit if set by BIOS. */ - if (bios_req & mask) { - u32 drv_req = intel_de_read(dev_priv, regs->driver); - - if (!(drv_req & mask)) - intel_de_write(dev_priv, regs->driver, drv_req | mask); - intel_de_write(dev_priv, regs->bios, bios_req & ~mask); - } -} - -static void bxt_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - bxt_ddi_phy_init(dev_priv, power_well->desc->bxt.phy); -} - -static void bxt_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - bxt_ddi_phy_uninit(dev_priv, power_well->desc->bxt.phy); -} - -static bool bxt_dpio_cmn_power_well_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - return bxt_ddi_phy_is_enabled(dev_priv, power_well->desc->bxt.phy); -} - -static void bxt_verify_ddi_phy_power_wells(struct drm_i915_private *dev_priv) -{ - struct i915_power_well *power_well; - - power_well = lookup_power_well(dev_priv, BXT_DISP_PW_DPIO_CMN_A); - if (power_well->count > 0) - bxt_ddi_phy_verify_state(dev_priv, power_well->desc->bxt.phy); - - power_well = lookup_power_well(dev_priv, VLV_DISP_PW_DPIO_CMN_BC); - if (power_well->count > 0) - bxt_ddi_phy_verify_state(dev_priv, power_well->desc->bxt.phy); - - if (IS_GEMINILAKE(dev_priv)) { - power_well = lookup_power_well(dev_priv, - GLK_DISP_PW_DPIO_CMN_C); - if (power_well->count > 0) - bxt_ddi_phy_verify_state(dev_priv, - power_well->desc->bxt.phy); - } -} - -static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - return ((intel_de_read(dev_priv, DC_STATE_EN) & DC_STATE_EN_DC3CO) == 0 && - (intel_de_read(dev_priv, DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0); -} - -static void gen9_assert_dbuf_enabled(struct drm_i915_private *dev_priv) -{ - u8 hw_enabled_dbuf_slices = intel_enabled_dbuf_slices_mask(dev_priv); - u8 enabled_dbuf_slices = dev_priv->dbuf.enabled_slices; - - drm_WARN(&dev_priv->drm, - hw_enabled_dbuf_slices != enabled_dbuf_slices, - "Unexpected DBuf power power state (0x%08x, expected 0x%08x)\n", - hw_enabled_dbuf_slices, - enabled_dbuf_slices); -} - -static void gen9_disable_dc_states(struct drm_i915_private *dev_priv) -{ - struct intel_cdclk_config cdclk_config = {}; - - if (dev_priv->dmc.target_dc_state == DC_STATE_EN_DC3CO) { - tgl_disable_dc3co(dev_priv); - return; - } - - gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); - - if (!HAS_DISPLAY(dev_priv)) - return; - - intel_cdclk_get_cdclk(dev_priv, &cdclk_config); - /* Can't read out voltage_level so can't use intel_cdclk_changed() */ - drm_WARN_ON(&dev_priv->drm, - intel_cdclk_needs_modeset(&dev_priv->cdclk.hw, - &cdclk_config)); - - gen9_assert_dbuf_enabled(dev_priv); - - if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) - bxt_verify_ddi_phy_power_wells(dev_priv); - - if (DISPLAY_VER(dev_priv) >= 11) - /* - * DMC retains HW context only for port A, the other combo - * PHY's HW context for port B is lost after DC transitions, - * so we need to restore it manually. - */ - intel_combo_phy_init(dev_priv); -} - -static void gen9_dc_off_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - gen9_disable_dc_states(dev_priv); -} - -static void gen9_dc_off_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - if (!intel_dmc_has_payload(dev_priv)) - return; - - switch (dev_priv->dmc.target_dc_state) { - case DC_STATE_EN_DC3CO: - tgl_enable_dc3co(dev_priv); - break; - case DC_STATE_EN_UPTO_DC6: - skl_enable_dc6(dev_priv); - break; - case DC_STATE_EN_UPTO_DC5: - gen9_enable_dc5(dev_priv); - break; - } -} - -static void i9xx_power_well_sync_hw_noop(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ -} - -static void i9xx_always_on_power_well_noop(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ -} - -static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - return true; -} - -static void i830_pipes_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - if ((intel_de_read(dev_priv, PIPECONF(PIPE_A)) & PIPECONF_ENABLE) == 0) - i830_enable_pipe(dev_priv, PIPE_A); - if ((intel_de_read(dev_priv, PIPECONF(PIPE_B)) & PIPECONF_ENABLE) == 0) - i830_enable_pipe(dev_priv, PIPE_B); -} - -static void i830_pipes_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - i830_disable_pipe(dev_priv, PIPE_B); - i830_disable_pipe(dev_priv, PIPE_A); -} - -static bool i830_pipes_power_well_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - return intel_de_read(dev_priv, PIPECONF(PIPE_A)) & PIPECONF_ENABLE && - intel_de_read(dev_priv, PIPECONF(PIPE_B)) & PIPECONF_ENABLE; -} - -static void i830_pipes_power_well_sync_hw(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - if (power_well->count > 0) - i830_pipes_power_well_enable(dev_priv, power_well); - else - i830_pipes_power_well_disable(dev_priv, power_well); -} - -static void vlv_set_power_well(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, bool enable) -{ - int pw_idx = power_well->desc->vlv.idx; - u32 mask; - u32 state; - u32 ctrl; - - mask = PUNIT_PWRGT_MASK(pw_idx); - state = enable ? PUNIT_PWRGT_PWR_ON(pw_idx) : - PUNIT_PWRGT_PWR_GATE(pw_idx); - - vlv_punit_get(dev_priv); - -#define COND \ - ((vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & mask) == state) - - if (COND) - goto out; - - ctrl = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL); - ctrl &= ~mask; - ctrl |= state; - vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, ctrl); - - if (wait_for(COND, 100)) - drm_err(&dev_priv->drm, - "timeout setting power well state %08x (%08x)\n", - state, - vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL)); - -#undef COND - -out: - vlv_punit_put(dev_priv); -} - -static void vlv_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - vlv_set_power_well(dev_priv, power_well, true); -} - -static void vlv_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - vlv_set_power_well(dev_priv, power_well, false); -} - -static bool vlv_power_well_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - int pw_idx = power_well->desc->vlv.idx; - bool enabled = false; - u32 mask; - u32 state; - u32 ctrl; - - mask = PUNIT_PWRGT_MASK(pw_idx); - ctrl = PUNIT_PWRGT_PWR_ON(pw_idx); - - vlv_punit_get(dev_priv); - - state = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & mask; - /* - * We only ever set the power-on and power-gate states, anything - * else is unexpected. - */ - drm_WARN_ON(&dev_priv->drm, state != PUNIT_PWRGT_PWR_ON(pw_idx) && - state != PUNIT_PWRGT_PWR_GATE(pw_idx)); - if (state == ctrl) - enabled = true; - - /* - * A transient state at this point would mean some unexpected party - * is poking at the power controls too. - */ - ctrl = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL) & mask; - drm_WARN_ON(&dev_priv->drm, ctrl != state); - - vlv_punit_put(dev_priv); - - return enabled; -} - -static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv) -{ - u32 val; - - /* - * On driver load, a pipe may be active and driving a DSI display. - * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck - * (and never recovering) in this case. intel_dsi_post_disable() will - * clear it when we turn off the display. - */ - val = intel_de_read(dev_priv, DSPCLK_GATE_D); - val &= DPOUNIT_CLOCK_GATE_DISABLE; - val |= VRHUNIT_CLOCK_GATE_DISABLE; - intel_de_write(dev_priv, DSPCLK_GATE_D, val); - - /* - * Disable trickle feed and enable pnd deadline calculation - */ - intel_de_write(dev_priv, MI_ARB_VLV, - MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); - intel_de_write(dev_priv, CBR1_VLV, 0); - - drm_WARN_ON(&dev_priv->drm, RUNTIME_INFO(dev_priv)->rawclk_freq == 0); - intel_de_write(dev_priv, RAWCLK_FREQ_VLV, - DIV_ROUND_CLOSEST(RUNTIME_INFO(dev_priv)->rawclk_freq, - 1000)); -} - -static void vlv_display_power_well_init(struct drm_i915_private *dev_priv) -{ - struct intel_encoder *encoder; - enum pipe pipe; - - /* - * Enable the CRI clock source so we can get at the - * display and the reference clock for VGA - * hotplug / manual detection. Supposedly DSI also - * needs the ref clock up and running. - * - * CHV DPLL B/C have some issues if VGA mode is enabled. - */ - for_each_pipe(dev_priv, pipe) { - u32 val = intel_de_read(dev_priv, DPLL(pipe)); - - val |= DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; - if (pipe != PIPE_A) - val |= DPLL_INTEGRATED_CRI_CLK_VLV; - - intel_de_write(dev_priv, DPLL(pipe), val); - } - - vlv_init_display_clock_gating(dev_priv); - - spin_lock_irq(&dev_priv->irq_lock); - valleyview_enable_display_irqs(dev_priv); - spin_unlock_irq(&dev_priv->irq_lock); - - /* - * During driver initialization/resume we can avoid restoring the - * part of the HW/SW state that will be inited anyway explicitly. - */ - if (dev_priv->power_domains.initializing) - return; - - intel_hpd_init(dev_priv); - intel_hpd_poll_disable(dev_priv); - - /* Re-enable the ADPA, if we have one */ - for_each_intel_encoder(&dev_priv->drm, encoder) { - if (encoder->type == INTEL_OUTPUT_ANALOG) - intel_crt_reset(&encoder->base); - } - - intel_vga_redisable_power_on(dev_priv); - - intel_pps_unlock_regs_wa(dev_priv); -} - -static void vlv_display_power_well_deinit(struct drm_i915_private *dev_priv) -{ - spin_lock_irq(&dev_priv->irq_lock); - valleyview_disable_display_irqs(dev_priv); - spin_unlock_irq(&dev_priv->irq_lock); - - /* make sure we're done processing display irqs */ - intel_synchronize_irq(dev_priv); - - intel_pps_reset_all(dev_priv); - - /* Prevent us from re-enabling polling on accident in late suspend */ - if (!dev_priv->drm.dev->power.is_suspended) - intel_hpd_poll_enable(dev_priv); -} - -static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - vlv_set_power_well(dev_priv, power_well, true); - - vlv_display_power_well_init(dev_priv); -} - -static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - vlv_display_power_well_deinit(dev_priv); - - vlv_set_power_well(dev_priv, power_well, false); -} - -static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - /* since ref/cri clock was enabled */ - udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ - - vlv_set_power_well(dev_priv, power_well, true); - - /* - * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx - - * 6. De-assert cmn_reset/side_reset. Same as VLV X0. - * a. GUnit 0x2110 bit[0] set to 1 (def 0) - * b. The other bits such as sfr settings / modesel may all - * be set to 0. - * - * This should only be done on init and resume from S3 with - * both PLLs disabled, or we risk losing DPIO and PLL - * synchronization. - */ - intel_de_write(dev_priv, DPIO_CTL, - intel_de_read(dev_priv, DPIO_CTL) | DPIO_CMNRST); -} - -static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum pipe pipe; - - for_each_pipe(dev_priv, pipe) - assert_pll_disabled(dev_priv, pipe); - - /* Assert common reset */ - intel_de_write(dev_priv, DPIO_CTL, - intel_de_read(dev_priv, DPIO_CTL) & ~DPIO_CMNRST); - - vlv_set_power_well(dev_priv, power_well, false); -} - #define POWER_DOMAIN_MASK (GENMASK_ULL(POWER_DOMAIN_NUM - 1, 0)) -#define BITS_SET(val, bits) (((val) & (bits)) == (bits)) - -static void assert_chv_phy_status(struct drm_i915_private *dev_priv) -{ - struct i915_power_well *cmn_bc = - lookup_power_well(dev_priv, VLV_DISP_PW_DPIO_CMN_BC); - struct i915_power_well *cmn_d = - lookup_power_well(dev_priv, CHV_DISP_PW_DPIO_CMN_D); - u32 phy_control = dev_priv->chv_phy_control; - u32 phy_status = 0; - u32 phy_status_mask = 0xffffffff; - - /* - * The BIOS can leave the PHY is some weird state - * where it doesn't fully power down some parts. - * Disable the asserts until the PHY has been fully - * reset (ie. the power well has been disabled at - * least once). - */ - if (!dev_priv->chv_phy_assert[DPIO_PHY0]) - phy_status_mask &= ~(PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH0) | - PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 0) | - PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 1) | - PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH1) | - PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 0) | - PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 1)); - - if (!dev_priv->chv_phy_assert[DPIO_PHY1]) - phy_status_mask &= ~(PHY_STATUS_CMN_LDO(DPIO_PHY1, DPIO_CH0) | - PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 0) | - PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 1)); - - if (cmn_bc->desc->ops->is_enabled(dev_priv, cmn_bc)) { - phy_status |= PHY_POWERGOOD(DPIO_PHY0); - - /* this assumes override is only used to enable lanes */ - if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0)) == 0) - phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0); - - if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1)) == 0) - phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1); - - /* CL1 is on whenever anything is on in either channel */ - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0) | - PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1))) - phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH0); - - /* - * The DPLLB check accounts for the pipe B + port A usage - * with CL2 powered up but all the lanes in the second channel - * powered down. - */ - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1)) && - (intel_de_read(dev_priv, DPLL(PIPE_B)) & DPLL_VCO_ENABLE) == 0) - phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH1); - - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH0))) - phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 0); - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH0))) - phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 1); - - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH1))) - phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 0); - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH1))) - phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 1); - } - - if (cmn_d->desc->ops->is_enabled(dev_priv, cmn_d)) { - phy_status |= PHY_POWERGOOD(DPIO_PHY1); - - /* this assumes override is only used to enable lanes */ - if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0)) == 0) - phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0); - - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0))) - phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY1, DPIO_CH0); - - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY1, DPIO_CH0))) - phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 0); - if (BITS_SET(phy_control, - PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY1, DPIO_CH0))) - phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 1); - } - - phy_status &= phy_status_mask; - - /* - * The PHY may be busy with some initial calibration and whatnot, - * so the power state can take a while to actually change. - */ - if (intel_de_wait_for_register(dev_priv, DISPLAY_PHY_STATUS, - phy_status_mask, phy_status, 10)) - drm_err(&dev_priv->drm, - "Unexpected PHY_STATUS 0x%08x, expected 0x%08x (PHY_CONTROL=0x%08x)\n", - intel_de_read(dev_priv, DISPLAY_PHY_STATUS) & phy_status_mask, - phy_status, dev_priv->chv_phy_control); -} - -#undef BITS_SET - -static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum dpio_phy phy; - enum pipe pipe; - u32 tmp; - - drm_WARN_ON_ONCE(&dev_priv->drm, - power_well->desc->id != VLV_DISP_PW_DPIO_CMN_BC && - power_well->desc->id != CHV_DISP_PW_DPIO_CMN_D); - - if (power_well->desc->id == VLV_DISP_PW_DPIO_CMN_BC) { - pipe = PIPE_A; - phy = DPIO_PHY0; - } else { - pipe = PIPE_C; - phy = DPIO_PHY1; - } - - /* since ref/cri clock was enabled */ - udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ - vlv_set_power_well(dev_priv, power_well, true); - - /* Poll for phypwrgood signal */ - if (intel_de_wait_for_set(dev_priv, DISPLAY_PHY_STATUS, - PHY_POWERGOOD(phy), 1)) - drm_err(&dev_priv->drm, "Display PHY %d is not power up\n", - phy); - - vlv_dpio_get(dev_priv); - - /* Enable dynamic power down */ - tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); - tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN | - DPIO_SUS_CLK_CONFIG_GATE_CLKREQ; - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); - - if (power_well->desc->id == VLV_DISP_PW_DPIO_CMN_BC) { - tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); - tmp |= DPIO_DYNPWRDOWNEN_CH1; - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); - } else { - /* - * Force the non-existing CL2 off. BXT does this - * too, so maybe it saves some power even though - * CL2 doesn't exist? - */ - tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); - tmp |= DPIO_CL2_LDOFUSE_PWRENB; - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, tmp); - } - - vlv_dpio_put(dev_priv); - - dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy); - intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, - dev_priv->chv_phy_control); - - drm_dbg_kms(&dev_priv->drm, - "Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", - phy, dev_priv->chv_phy_control); - - assert_chv_phy_status(dev_priv); -} - -static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum dpio_phy phy; - - drm_WARN_ON_ONCE(&dev_priv->drm, - power_well->desc->id != VLV_DISP_PW_DPIO_CMN_BC && - power_well->desc->id != CHV_DISP_PW_DPIO_CMN_D); - - if (power_well->desc->id == VLV_DISP_PW_DPIO_CMN_BC) { - phy = DPIO_PHY0; - assert_pll_disabled(dev_priv, PIPE_A); - assert_pll_disabled(dev_priv, PIPE_B); - } else { - phy = DPIO_PHY1; - assert_pll_disabled(dev_priv, PIPE_C); - } - - dev_priv->chv_phy_control &= ~PHY_COM_LANE_RESET_DEASSERT(phy); - intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, - dev_priv->chv_phy_control); - - vlv_set_power_well(dev_priv, power_well, false); - - drm_dbg_kms(&dev_priv->drm, - "Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", - phy, dev_priv->chv_phy_control); - - /* PHY is fully reset now, so we can enable the PHY state asserts */ - dev_priv->chv_phy_assert[phy] = true; - - assert_chv_phy_status(dev_priv); -} - -static void assert_chv_phy_powergate(struct drm_i915_private *dev_priv, enum dpio_phy phy, - enum dpio_channel ch, bool override, unsigned int mask) -{ - enum pipe pipe = phy == DPIO_PHY0 ? PIPE_A : PIPE_C; - u32 reg, val, expected, actual; - - /* - * The BIOS can leave the PHY is some weird state - * where it doesn't fully power down some parts. - * Disable the asserts until the PHY has been fully - * reset (ie. the power well has been disabled at - * least once). - */ - if (!dev_priv->chv_phy_assert[phy]) - return; - - if (ch == DPIO_CH0) - reg = _CHV_CMN_DW0_CH0; - else - reg = _CHV_CMN_DW6_CH1; - - vlv_dpio_get(dev_priv); - val = vlv_dpio_read(dev_priv, pipe, reg); - vlv_dpio_put(dev_priv); - - /* - * This assumes !override is only used when the port is disabled. - * All lanes should power down even without the override when - * the port is disabled. - */ - if (!override || mask == 0xf) { - expected = DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; - /* - * If CH1 common lane is not active anymore - * (eg. for pipe B DPLL) the entire channel will - * shut down, which causes the common lane registers - * to read as 0. That means we can't actually check - * the lane power down status bits, but as the entire - * register reads as 0 it's a good indication that the - * channel is indeed entirely powered down. - */ - if (ch == DPIO_CH1 && val == 0) - expected = 0; - } else if (mask != 0x0) { - expected = DPIO_ANYDL_POWERDOWN; - } else { - expected = 0; - } - - if (ch == DPIO_CH0) - actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH0; - else - actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH1; - actual &= DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; - - drm_WARN(&dev_priv->drm, actual != expected, - "Unexpected DPIO lane power down: all %d, any %d. Expected: all %d, any %d. (0x%x = 0x%08x)\n", - !!(actual & DPIO_ALLDL_POWERDOWN), - !!(actual & DPIO_ANYDL_POWERDOWN), - !!(expected & DPIO_ALLDL_POWERDOWN), - !!(expected & DPIO_ANYDL_POWERDOWN), - reg, val); -} - -bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, - enum dpio_channel ch, bool override) -{ - struct i915_power_domains *power_domains = &dev_priv->power_domains; - bool was_override; - - mutex_lock(&power_domains->lock); - - was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); - - if (override == was_override) - goto out; - - if (override) - dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); - else - dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); - - intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, - dev_priv->chv_phy_control); - - drm_dbg_kms(&dev_priv->drm, - "Power gating DPIO PHY%d CH%d (DPIO_PHY_CONTROL=0x%08x)\n", - phy, ch, dev_priv->chv_phy_control); - - assert_chv_phy_status(dev_priv); - -out: - mutex_unlock(&power_domains->lock); - - return was_override; -} - -void chv_phy_powergate_lanes(struct intel_encoder *encoder, - bool override, unsigned int mask) -{ - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - struct i915_power_domains *power_domains = &dev_priv->power_domains; - enum dpio_phy phy = vlv_dig_port_to_phy(enc_to_dig_port(encoder)); - enum dpio_channel ch = vlv_dig_port_to_channel(enc_to_dig_port(encoder)); - - mutex_lock(&power_domains->lock); - - dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch); - dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch); - - if (override) - dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); - else - dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); - - intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, - dev_priv->chv_phy_control); - - drm_dbg_kms(&dev_priv->drm, - "Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", - phy, ch, mask, dev_priv->chv_phy_control); - - assert_chv_phy_status(dev_priv); - - assert_chv_phy_powergate(dev_priv, phy, ch, override, mask); - - mutex_unlock(&power_domains->lock); -} - -static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - enum pipe pipe = PIPE_A; - bool enabled; - u32 state, ctrl; - - vlv_punit_get(dev_priv); - - state = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM) & DP_SSS_MASK(pipe); - /* - * We only ever set the power-on and power-gate states, anything - * else is unexpected. - */ - drm_WARN_ON(&dev_priv->drm, state != DP_SSS_PWR_ON(pipe) && - state != DP_SSS_PWR_GATE(pipe)); - enabled = state == DP_SSS_PWR_ON(pipe); - - /* - * A transient state at this point would mean some unexpected party - * is poking at the power controls too. - */ - ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM) & DP_SSC_MASK(pipe); - drm_WARN_ON(&dev_priv->drm, ctrl << 16 != state); - - vlv_punit_put(dev_priv); - - return enabled; -} - -static void chv_set_pipe_power_well(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, - bool enable) -{ - enum pipe pipe = PIPE_A; - u32 state; - u32 ctrl; - - state = enable ? DP_SSS_PWR_ON(pipe) : DP_SSS_PWR_GATE(pipe); - - vlv_punit_get(dev_priv); - -#define COND \ - ((vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM) & DP_SSS_MASK(pipe)) == state) - - if (COND) - goto out; - - ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM); - ctrl &= ~DP_SSC_MASK(pipe); - ctrl |= enable ? DP_SSC_PWR_ON(pipe) : DP_SSC_PWR_GATE(pipe); - vlv_punit_write(dev_priv, PUNIT_REG_DSPSSPM, ctrl); - - if (wait_for(COND, 100)) - drm_err(&dev_priv->drm, - "timeout setting power well state %08x (%08x)\n", - state, - vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM)); - -#undef COND - -out: - vlv_punit_put(dev_priv); -} - -static void chv_pipe_power_well_sync_hw(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, - dev_priv->chv_phy_control); -} - -static void chv_pipe_power_well_enable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - chv_set_pipe_power_well(dev_priv, power_well, true); - - vlv_display_power_well_init(dev_priv); -} - -static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - vlv_display_power_well_deinit(dev_priv); - - chv_set_pipe_power_well(dev_priv, power_well, false); -} - -static u64 __async_put_domains_mask(struct i915_power_domains *power_domains) +static void __async_put_domains_mask(struct i915_power_domains *power_domains, + struct intel_power_domain_mask *mask) { - return power_domains->async_put_domains[0] | - power_domains->async_put_domains[1]; + bitmap_or(mask->bits, + power_domains->async_put_domains[0].bits, + power_domains->async_put_domains[1].bits, + POWER_DOMAIN_NUM); } #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) @@ -2061,8 +340,11 @@ assert_async_put_domain_masks_disjoint(struct i915_power_domains *power_domains) struct drm_i915_private *i915 = container_of(power_domains, struct drm_i915_private, power_domains); - return !drm_WARN_ON(&i915->drm, power_domains->async_put_domains[0] & - power_domains->async_put_domains[1]); + + return !drm_WARN_ON(&i915->drm, + bitmap_intersects(power_domains->async_put_domains[0].bits, + power_domains->async_put_domains[1].bits, + POWER_DOMAIN_NUM)); } static bool @@ -2071,14 +353,17 @@ __async_put_domains_state_ok(struct i915_power_domains *power_domains) struct drm_i915_private *i915 = container_of(power_domains, struct drm_i915_private, power_domains); + struct intel_power_domain_mask async_put_mask; enum intel_display_power_domain domain; bool err = false; err |= !assert_async_put_domain_masks_disjoint(power_domains); - err |= drm_WARN_ON(&i915->drm, !!power_domains->async_put_wakeref != - !!__async_put_domains_mask(power_domains)); + __async_put_domains_mask(power_domains, &async_put_mask); + err |= drm_WARN_ON(&i915->drm, + !!power_domains->async_put_wakeref != + !bitmap_empty(async_put_mask.bits, POWER_DOMAIN_NUM)); - for_each_power_domain(domain, __async_put_domains_mask(power_domains)) + for_each_power_domain(domain, &async_put_mask) err |= drm_WARN_ON(&i915->drm, power_domains->domain_use_count[domain] != 1); @@ -2086,14 +371,14 @@ __async_put_domains_state_ok(struct i915_power_domains *power_domains) } static void print_power_domains(struct i915_power_domains *power_domains, - const char *prefix, u64 mask) + const char *prefix, struct intel_power_domain_mask *mask) { struct drm_i915_private *i915 = container_of(power_domains, struct drm_i915_private, power_domains); enum intel_display_power_domain domain; - drm_dbg(&i915->drm, "%s (%lu):\n", prefix, hweight64(mask)); + drm_dbg(&i915->drm, "%s (%d):\n", prefix, bitmap_weight(mask->bits, POWER_DOMAIN_NUM)); for_each_power_domain(domain, mask) drm_dbg(&i915->drm, "%s use_count %d\n", intel_display_power_domain_str(domain), @@ -2111,9 +396,9 @@ print_async_put_domains_state(struct i915_power_domains *power_domains) power_domains->async_put_wakeref); print_power_domains(power_domains, "async_put_domains[0]", - power_domains->async_put_domains[0]); + &power_domains->async_put_domains[0]); print_power_domains(power_domains, "async_put_domains[1]", - power_domains->async_put_domains[1]); + &power_domains->async_put_domains[1]); } static void @@ -2137,11 +422,13 @@ verify_async_put_domains_state(struct i915_power_domains *power_domains) #endif /* CONFIG_DRM_I915_DEBUG_RUNTIME_PM */ -static u64 async_put_domains_mask(struct i915_power_domains *power_domains) +static void async_put_domains_mask(struct i915_power_domains *power_domains, + struct intel_power_domain_mask *mask) + { assert_async_put_domain_masks_disjoint(power_domains); - return __async_put_domains_mask(power_domains); + __async_put_domains_mask(power_domains, mask); } static void @@ -2150,8 +437,8 @@ async_put_domains_clear_domain(struct i915_power_domains *power_domains, { assert_async_put_domain_masks_disjoint(power_domains); - power_domains->async_put_domains[0] &= ~BIT_ULL(domain); - power_domains->async_put_domains[1] &= ~BIT_ULL(domain); + clear_bit(domain, power_domains->async_put_domains[0].bits); + clear_bit(domain, power_domains->async_put_domains[1].bits); } static bool @@ -2159,16 +446,19 @@ intel_display_power_grab_async_put_ref(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain) { struct i915_power_domains *power_domains = &dev_priv->power_domains; + struct intel_power_domain_mask async_put_mask; bool ret = false; - if (!(async_put_domains_mask(power_domains) & BIT_ULL(domain))) + async_put_domains_mask(power_domains, &async_put_mask); + if (!test_bit(domain, async_put_mask.bits)) goto out_verify; async_put_domains_clear_domain(power_domains, domain); ret = true; - if (async_put_domains_mask(power_domains)) + async_put_domains_mask(power_domains, &async_put_mask); + if (!bitmap_empty(async_put_mask.bits, POWER_DOMAIN_NUM)) goto out_verify; cancel_delayed_work(&power_domains->async_put_work); @@ -2190,7 +480,7 @@ __intel_display_power_get_domain(struct drm_i915_private *dev_priv, if (intel_display_power_grab_async_put_ref(dev_priv, domain)) return; - for_each_power_domain_well(dev_priv, power_well, BIT_ULL(domain)) + for_each_power_domain_well(dev_priv, power_well, domain) intel_power_well_get(dev_priv, power_well); power_domains->domain_use_count[domain]++; @@ -2271,20 +561,22 @@ __intel_display_power_put_domain(struct drm_i915_private *dev_priv, struct i915_power_domains *power_domains; struct i915_power_well *power_well; const char *name = intel_display_power_domain_str(domain); + struct intel_power_domain_mask async_put_mask; power_domains = &dev_priv->power_domains; drm_WARN(&dev_priv->drm, !power_domains->domain_use_count[domain], "Use count on domain %s is already zero\n", name); + async_put_domains_mask(power_domains, &async_put_mask); drm_WARN(&dev_priv->drm, - async_put_domains_mask(power_domains) & BIT_ULL(domain), + test_bit(domain, async_put_mask.bits), "Async disabling of domain %s is pending\n", name); power_domains->domain_use_count[domain]--; - for_each_power_domain_well_reverse(dev_priv, power_well, BIT_ULL(domain)) + for_each_power_domain_well_reverse(dev_priv, power_well, domain) intel_power_well_put(dev_priv, power_well); } @@ -2313,7 +605,8 @@ queue_async_put_domains_work(struct i915_power_domains *power_domains, } static void -release_async_put_domains(struct i915_power_domains *power_domains, u64 mask) +release_async_put_domains(struct i915_power_domains *power_domains, + struct intel_power_domain_mask *mask) { struct drm_i915_private *dev_priv = container_of(power_domains, struct drm_i915_private, @@ -2361,12 +654,15 @@ intel_display_power_put_async_work(struct work_struct *work) goto out_verify; release_async_put_domains(power_domains, - power_domains->async_put_domains[0]); + &power_domains->async_put_domains[0]); /* Requeue the work if more domains were async put meanwhile. */ - if (power_domains->async_put_domains[1]) { - power_domains->async_put_domains[0] = - fetch_and_zero(&power_domains->async_put_domains[1]); + if (!bitmap_empty(power_domains->async_put_domains[1].bits, POWER_DOMAIN_NUM)) { + bitmap_copy(power_domains->async_put_domains[0].bits, + power_domains->async_put_domains[1].bits, + POWER_DOMAIN_NUM); + bitmap_zero(power_domains->async_put_domains[1].bits, + POWER_DOMAIN_NUM); queue_async_put_domains_work(power_domains, fetch_and_zero(&new_work_wakeref)); } else { @@ -2418,9 +714,9 @@ void __intel_display_power_put_async(struct drm_i915_private *i915, /* Let a pending work requeue itself or queue a new one. */ if (power_domains->async_put_wakeref) { - power_domains->async_put_domains[1] |= BIT_ULL(domain); + set_bit(domain, power_domains->async_put_domains[1].bits); } else { - power_domains->async_put_domains[0] |= BIT_ULL(domain); + set_bit(domain, power_domains->async_put_domains[0].bits); queue_async_put_domains_work(power_domains, fetch_and_zero(&work_wakeref)); } @@ -2451,6 +747,7 @@ out_verify: void intel_display_power_flush_work(struct drm_i915_private *i915) { struct i915_power_domains *power_domains = &i915->power_domains; + struct intel_power_domain_mask async_put_mask; intel_wakeref_t work_wakeref; mutex_lock(&power_domains->lock); @@ -2459,8 +756,8 @@ void intel_display_power_flush_work(struct drm_i915_private *i915) if (!work_wakeref) goto out_verify; - release_async_put_domains(power_domains, - async_put_domains_mask(power_domains)); + async_put_domains_mask(power_domains, &async_put_mask); + release_async_put_domains(power_domains, &async_put_mask); cancel_delayed_work(&power_domains->async_put_work); out_verify: @@ -2539,13 +836,13 @@ intel_display_power_get_in_set(struct drm_i915_private *i915, { intel_wakeref_t __maybe_unused wf; - drm_WARN_ON(&i915->drm, power_domain_set->mask & BIT_ULL(domain)); + drm_WARN_ON(&i915->drm, test_bit(domain, power_domain_set->mask.bits)); wf = intel_display_power_get(i915, domain); #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) power_domain_set->wakerefs[domain] = wf; #endif - power_domain_set->mask |= BIT_ULL(domain); + set_bit(domain, power_domain_set->mask.bits); } bool @@ -2555,7 +852,7 @@ intel_display_power_get_in_set_if_enabled(struct drm_i915_private *i915, { intel_wakeref_t wf; - drm_WARN_ON(&i915->drm, power_domain_set->mask & BIT_ULL(domain)); + drm_WARN_ON(&i915->drm, test_bit(domain, power_domain_set->mask.bits)); wf = intel_display_power_get_if_enabled(i915, domain); if (!wf) @@ -2564,7 +861,7 @@ intel_display_power_get_in_set_if_enabled(struct drm_i915_private *i915, #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) power_domain_set->wakerefs[domain] = wf; #endif - power_domain_set->mask |= BIT_ULL(domain); + set_bit(domain, power_domain_set->mask.bits); return true; } @@ -2572,11 +869,12 @@ intel_display_power_get_in_set_if_enabled(struct drm_i915_private *i915, void intel_display_power_put_mask_in_set(struct drm_i915_private *i915, struct intel_display_power_domain_set *power_domain_set, - u64 mask) + struct intel_power_domain_mask *mask) { enum intel_display_power_domain domain; - drm_WARN_ON(&i915->drm, mask & ~power_domain_set->mask); + drm_WARN_ON(&i915->drm, + !bitmap_subset(mask->bits, power_domain_set->mask.bits, POWER_DOMAIN_NUM)); for_each_power_domain(domain, mask) { intel_wakeref_t __maybe_unused wf = -1; @@ -2585,2485 +883,10 @@ intel_display_power_put_mask_in_set(struct drm_i915_private *i915, wf = fetch_and_zero(&power_domain_set->wakerefs[domain]); #endif intel_display_power_put(i915, domain, wf); - power_domain_set->mask &= ~BIT_ULL(domain); + clear_bit(domain, power_domain_set->mask.bits); } } -#define I830_PIPES_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_A) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define VLV_DISPLAY_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_DISPLAY_CORE) | \ - BIT_ULL(POWER_DOMAIN_PIPE_A) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DSI) | \ - BIT_ULL(POWER_DOMAIN_PORT_CRT) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_GMBUS) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define VLV_DPIO_CMN_BC_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_CRT) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define CHV_DISPLAY_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_DISPLAY_CORE) | \ - BIT_ULL(POWER_DOMAIN_PIPE_A) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DSI) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_AUX_D) | \ - BIT_ULL(POWER_DOMAIN_GMBUS) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define CHV_DPIO_CMN_BC_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define CHV_DPIO_CMN_D_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_D) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define HSW_DISPLAY_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_CRT) | /* DDI E */ \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define BDW_DISPLAY_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_CRT) | /* DDI E */ \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_E_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_AUX_D) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define SKL_DISPLAY_DDI_IO_A_E_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_A_IO) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_E_IO) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define SKL_DISPLAY_DDI_IO_B_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_IO) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define SKL_DISPLAY_DDI_IO_C_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_IO) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define SKL_DISPLAY_DDI_IO_D_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_IO) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define SKL_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_GT_IRQ) | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define BXT_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_GT_IRQ) | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_GMBUS) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define BXT_DPIO_CMN_A_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_A_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define BXT_DPIO_CMN_BC_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define GLK_DISPLAY_POWERWELL_2_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define GLK_DISPLAY_DDI_IO_A_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_A_IO)) -#define GLK_DISPLAY_DDI_IO_B_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_IO)) -#define GLK_DISPLAY_DDI_IO_C_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_IO)) -#define GLK_DPIO_CMN_A_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_A_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define GLK_DPIO_CMN_B_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define GLK_DPIO_CMN_C_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define GLK_DISPLAY_AUX_A_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_AUX_IO_A) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define GLK_DISPLAY_AUX_B_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define GLK_DISPLAY_AUX_C_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) -#define GLK_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - GLK_DISPLAY_POWERWELL_2_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_GT_IRQ) | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_GMBUS) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -/* - * ICL PW_0/PG_0 domains (HW/DMC control): - * - PCI - * - clocks except port PLL - * - central power except FBC - * - shared functions except pipe interrupts, pipe MBUS, DBUF registers - * ICL PW_1/PG_1 domains (HW/DMC control): - * - DBUF function - * - PIPE_A and its planes, except VGA - * - transcoder EDP + PSR - * - transcoder DSI - * - DDI_A - * - FBC - */ -#define ICL_PW_4_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - /* VDSC/joining */ -#define ICL_PW_3_POWER_DOMAINS ( \ - ICL_PW_4_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_A) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_E_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_F_LANES) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_AUX_D) | \ - BIT_ULL(POWER_DOMAIN_AUX_E) | \ - BIT_ULL(POWER_DOMAIN_AUX_F) | \ - BIT_ULL(POWER_DOMAIN_AUX_C_TBT) | \ - BIT_ULL(POWER_DOMAIN_AUX_D_TBT) | \ - BIT_ULL(POWER_DOMAIN_AUX_E_TBT) | \ - BIT_ULL(POWER_DOMAIN_AUX_F_TBT) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - /* - * - transcoder WD - * - KVMR (HW control) - */ -#define ICL_PW_2_POWER_DOMAINS ( \ - ICL_PW_3_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_VDSC_PW2) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - /* - * - KVMR (HW control) - */ -#define ICL_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - ICL_PW_2_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_DC_OFF) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define ICL_DDI_IO_A_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_A_IO)) -#define ICL_DDI_IO_B_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_B_IO)) -#define ICL_DDI_IO_C_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_IO)) -#define ICL_DDI_IO_D_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_D_IO)) -#define ICL_DDI_IO_E_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_E_IO)) -#define ICL_DDI_IO_F_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_F_IO)) - -#define ICL_AUX_A_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_IO_A) | \ - BIT_ULL(POWER_DOMAIN_AUX_A)) -#define ICL_AUX_B_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_B)) -#define ICL_AUX_C_TC1_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_C)) -#define ICL_AUX_D_TC2_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_D)) -#define ICL_AUX_E_TC3_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_E)) -#define ICL_AUX_F_TC4_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_F)) -#define ICL_AUX_C_TBT1_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_C_TBT)) -#define ICL_AUX_D_TBT2_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_D_TBT)) -#define ICL_AUX_E_TBT3_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_E_TBT)) -#define ICL_AUX_F_TBT4_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_F_TBT)) - -#define TGL_PW_5_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_D) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_D) | \ - BIT_ULL(POWER_DOMAIN_PIPE_D_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define TGL_PW_4_POWER_DOMAINS ( \ - TGL_PW_5_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define TGL_PW_3_POWER_DOMAINS ( \ - TGL_PW_4_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC1) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC2) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC3) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC4) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC5) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC6) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC1) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC2) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC3) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC4) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC5) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC6) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT1) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT2) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT3) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT4) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT5) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT6) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define TGL_PW_2_POWER_DOMAINS ( \ - TGL_PW_3_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_VDSC_PW2) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define TGL_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - TGL_PW_3_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define TGL_DDI_IO_TC1_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC1) -#define TGL_DDI_IO_TC2_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC2) -#define TGL_DDI_IO_TC3_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC3) -#define TGL_DDI_IO_TC4_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC4) -#define TGL_DDI_IO_TC5_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC5) -#define TGL_DDI_IO_TC6_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC6) - -#define TGL_AUX_A_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_IO_A) | \ - BIT_ULL(POWER_DOMAIN_AUX_A)) -#define TGL_AUX_B_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_B)) -#define TGL_AUX_C_IO_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_C)) - -#define TGL_AUX_IO_USBC1_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC1) -#define TGL_AUX_IO_USBC2_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC2) -#define TGL_AUX_IO_USBC3_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC3) -#define TGL_AUX_IO_USBC4_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC4) -#define TGL_AUX_IO_USBC5_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC5) -#define TGL_AUX_IO_USBC6_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC6) - -#define TGL_AUX_IO_TBT1_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT1) -#define TGL_AUX_IO_TBT2_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT2) -#define TGL_AUX_IO_TBT3_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT3) -#define TGL_AUX_IO_TBT4_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT4) -#define TGL_AUX_IO_TBT5_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT5) -#define TGL_AUX_IO_TBT6_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT6) - -#define TGL_TC_COLD_OFF_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_AUX_USBC1) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC2) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC3) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC4) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC5) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC6) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT1) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT2) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT3) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT4) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT5) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT6) | \ - BIT_ULL(POWER_DOMAIN_TC_COLD_OFF)) - -#define RKL_PW_4_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define RKL_PW_3_POWER_DOMAINS ( \ - RKL_PW_4_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC1) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC2) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC1) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC2) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -/* - * There is no PW_2/PG_2 on RKL. - * - * RKL PW_1/PG_1 domains (under HW/DMC control): - * - DBUF function (note: registers are in PW0) - * - PIPE_A and its planes and VDSC/joining, except VGA - * - transcoder A - * - DDI_A and DDI_B - * - FBC - * - * RKL PW_0/PG_0 domains (under HW/DMC control): - * - PCI - * - clocks except port PLL - * - shared functions: - * * interrupts except pipe interrupts - * * MBus except PIPE_MBUS_DBOX_CTL - * * DBUF registers - * - central power except FBC - * - top-level GTC (DDI-level GTC is in the well associated with the DDI) - */ - -#define RKL_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - RKL_PW_3_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -/* - * DG1 onwards Audio MMIO/VERBS lies in PG0 power well. - */ -#define DG1_PW_3_POWER_DOMAINS ( \ - TGL_PW_4_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC1) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC2) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC1) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC2) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define DG1_PW_2_POWER_DOMAINS ( \ - DG1_PW_3_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_VDSC_PW2) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define DG1_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - DG1_PW_3_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -/* - * XE_LPD Power Domains - * - * Previous platforms required that PG(n-1) be enabled before PG(n). That - * dependency chain turns into a dependency tree on XE_LPD: - * - * PG0 - * | - * --PG1-- - * / \ - * PGA --PG2-- - * / | \ - * PGB PGC PGD - * - * Power wells must be enabled from top to bottom and disabled from bottom - * to top. This allows pipes to be power gated independently. - */ - -#define XELPD_PW_D_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_D) | \ - BIT_ULL(POWER_DOMAIN_PIPE_D_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_D) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define XELPD_PW_C_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_C) | \ - BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define XELPD_PW_B_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_B) | \ - BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define XELPD_PW_A_POWER_DOMAINS ( \ - BIT_ULL(POWER_DOMAIN_PIPE_A) | \ - BIT_ULL(POWER_DOMAIN_PIPE_A_PANEL_FITTER) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define XELPD_PW_2_POWER_DOMAINS ( \ - XELPD_PW_B_POWER_DOMAINS | \ - XELPD_PW_C_POWER_DOMAINS | \ - XELPD_PW_D_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_AUDIO_PLAYBACK) | \ - BIT_ULL(POWER_DOMAIN_VGA) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_D_XELPD) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_E_XELPD) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC1) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC2) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC3) | \ - BIT_ULL(POWER_DOMAIN_PORT_DDI_LANES_TC4) | \ - BIT_ULL(POWER_DOMAIN_AUX_C) | \ - BIT_ULL(POWER_DOMAIN_AUX_D_XELPD) | \ - BIT_ULL(POWER_DOMAIN_AUX_E_XELPD) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC1) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC2) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC3) | \ - BIT_ULL(POWER_DOMAIN_AUX_USBC4) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT1) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT2) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT3) | \ - BIT_ULL(POWER_DOMAIN_AUX_TBT4) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -/* - * XELPD PW_1/PG_1 domains (under HW/DMC control): - * - DBUF function (registers are in PW0) - * - Transcoder A - * - DDI_A and DDI_B - * - * XELPD PW_0/PW_1 domains (under HW/DMC control): - * - PCI - * - Clocks except port PLL - * - Shared functions: - * * interrupts except pipe interrupts - * * MBus except PIPE_MBUS_DBOX_CTL - * * DBUF registers - * - Central power except FBC - * - Top-level GTC (DDI-level GTC is in the well associated with the DDI) - */ - -#define XELPD_DISPLAY_DC_OFF_POWER_DOMAINS ( \ - XELPD_PW_2_POWER_DOMAINS | \ - BIT_ULL(POWER_DOMAIN_AUDIO_MMIO) | \ - BIT_ULL(POWER_DOMAIN_MODESET) | \ - BIT_ULL(POWER_DOMAIN_AUX_A) | \ - BIT_ULL(POWER_DOMAIN_AUX_B) | \ - BIT_ULL(POWER_DOMAIN_PORT_DSI) | \ - BIT_ULL(POWER_DOMAIN_INIT)) - -#define XELPD_AUX_IO_D_XELPD_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_D_XELPD) -#define XELPD_AUX_IO_E_XELPD_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_E_XELPD) -#define XELPD_AUX_IO_USBC1_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC1) -#define XELPD_AUX_IO_USBC2_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC2) -#define XELPD_AUX_IO_USBC3_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC3) -#define XELPD_AUX_IO_USBC4_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_USBC4) - -#define XELPD_AUX_IO_TBT1_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT1) -#define XELPD_AUX_IO_TBT2_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT2) -#define XELPD_AUX_IO_TBT3_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT3) -#define XELPD_AUX_IO_TBT4_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_AUX_TBT4) - -#define XELPD_DDI_IO_D_XELPD_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_D_XELPD) -#define XELPD_DDI_IO_E_XELPD_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_E_XELPD) -#define XELPD_DDI_IO_TC1_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC1) -#define XELPD_DDI_IO_TC2_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC2) -#define XELPD_DDI_IO_TC3_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC3) -#define XELPD_DDI_IO_TC4_POWER_DOMAINS BIT_ULL(POWER_DOMAIN_PORT_DDI_IO_TC4) - -static const struct i915_power_well_ops i9xx_always_on_power_well_ops = { - .sync_hw = i9xx_power_well_sync_hw_noop, - .enable = i9xx_always_on_power_well_noop, - .disable = i9xx_always_on_power_well_noop, - .is_enabled = i9xx_always_on_power_well_enabled, -}; - -static const struct i915_power_well_ops chv_pipe_power_well_ops = { - .sync_hw = chv_pipe_power_well_sync_hw, - .enable = chv_pipe_power_well_enable, - .disable = chv_pipe_power_well_disable, - .is_enabled = chv_pipe_power_well_enabled, -}; - -static const struct i915_power_well_ops chv_dpio_cmn_power_well_ops = { - .sync_hw = i9xx_power_well_sync_hw_noop, - .enable = chv_dpio_cmn_power_well_enable, - .disable = chv_dpio_cmn_power_well_disable, - .is_enabled = vlv_power_well_enabled, -}; - -static const struct i915_power_well_desc i9xx_always_on_power_well[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, -}; - -static const struct i915_power_well_ops i830_pipes_power_well_ops = { - .sync_hw = i830_pipes_power_well_sync_hw, - .enable = i830_pipes_power_well_enable, - .disable = i830_pipes_power_well_disable, - .is_enabled = i830_pipes_power_well_enabled, -}; - -static const struct i915_power_well_desc i830_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "pipes", - .domains = I830_PIPES_POWER_DOMAINS, - .ops = &i830_pipes_power_well_ops, - .id = DISP_PW_ID_NONE, - }, -}; - -static const struct i915_power_well_ops hsw_power_well_ops = { - .sync_hw = hsw_power_well_sync_hw, - .enable = hsw_power_well_enable, - .disable = hsw_power_well_disable, - .is_enabled = hsw_power_well_enabled, -}; - -static const struct i915_power_well_ops gen9_dc_off_power_well_ops = { - .sync_hw = i9xx_power_well_sync_hw_noop, - .enable = gen9_dc_off_power_well_enable, - .disable = gen9_dc_off_power_well_disable, - .is_enabled = gen9_dc_off_power_well_enabled, -}; - -static const struct i915_power_well_ops bxt_dpio_cmn_power_well_ops = { - .sync_hw = i9xx_power_well_sync_hw_noop, - .enable = bxt_dpio_cmn_power_well_enable, - .disable = bxt_dpio_cmn_power_well_disable, - .is_enabled = bxt_dpio_cmn_power_well_enabled, -}; - -static const struct i915_power_well_regs hsw_power_well_regs = { - .bios = HSW_PWR_WELL_CTL1, - .driver = HSW_PWR_WELL_CTL2, - .kvmr = HSW_PWR_WELL_CTL3, - .debug = HSW_PWR_WELL_CTL4, -}; - -static const struct i915_power_well_desc hsw_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "display", - .domains = HSW_DISPLAY_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = HSW_DISP_PW_GLOBAL, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = HSW_PW_CTL_IDX_GLOBAL, - .hsw.has_vga = true, - }, - }, -}; - -static const struct i915_power_well_desc bdw_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "display", - .domains = BDW_DISPLAY_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = HSW_DISP_PW_GLOBAL, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = HSW_PW_CTL_IDX_GLOBAL, - .hsw.irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), - .hsw.has_vga = true, - }, - }, -}; - -static const struct i915_power_well_ops vlv_display_power_well_ops = { - .sync_hw = i9xx_power_well_sync_hw_noop, - .enable = vlv_display_power_well_enable, - .disable = vlv_display_power_well_disable, - .is_enabled = vlv_power_well_enabled, -}; - -static const struct i915_power_well_ops vlv_dpio_cmn_power_well_ops = { - .sync_hw = i9xx_power_well_sync_hw_noop, - .enable = vlv_dpio_cmn_power_well_enable, - .disable = vlv_dpio_cmn_power_well_disable, - .is_enabled = vlv_power_well_enabled, -}; - -static const struct i915_power_well_ops vlv_dpio_power_well_ops = { - .sync_hw = i9xx_power_well_sync_hw_noop, - .enable = vlv_power_well_enable, - .disable = vlv_power_well_disable, - .is_enabled = vlv_power_well_enabled, -}; - -static const struct i915_power_well_desc vlv_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "display", - .domains = VLV_DISPLAY_POWER_DOMAINS, - .ops = &vlv_display_power_well_ops, - .id = VLV_DISP_PW_DISP2D, - { - .vlv.idx = PUNIT_PWGT_IDX_DISP2D, - }, - }, - { - .name = "dpio-tx-b-01", - .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS, - .ops = &vlv_dpio_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_B_LANES_01, - }, - }, - { - .name = "dpio-tx-b-23", - .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS, - .ops = &vlv_dpio_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_B_LANES_23, - }, - }, - { - .name = "dpio-tx-c-01", - .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS, - .ops = &vlv_dpio_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_C_LANES_01, - }, - }, - { - .name = "dpio-tx-c-23", - .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS | - VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS, - .ops = &vlv_dpio_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_C_LANES_23, - }, - }, - { - .name = "dpio-common", - .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS, - .ops = &vlv_dpio_cmn_power_well_ops, - .id = VLV_DISP_PW_DPIO_CMN_BC, - { - .vlv.idx = PUNIT_PWGT_IDX_DPIO_CMN_BC, - }, - }, -}; - -static const struct i915_power_well_desc chv_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "display", - /* - * Pipe A power well is the new disp2d well. Pipe B and C - * power wells don't actually exist. Pipe A power well is - * required for any pipe to work. - */ - .domains = CHV_DISPLAY_POWER_DOMAINS, - .ops = &chv_pipe_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "dpio-common-bc", - .domains = CHV_DPIO_CMN_BC_POWER_DOMAINS, - .ops = &chv_dpio_cmn_power_well_ops, - .id = VLV_DISP_PW_DPIO_CMN_BC, - { - .vlv.idx = PUNIT_PWGT_IDX_DPIO_CMN_BC, - }, - }, - { - .name = "dpio-common-d", - .domains = CHV_DPIO_CMN_D_POWER_DOMAINS, - .ops = &chv_dpio_cmn_power_well_ops, - .id = CHV_DISP_PW_DPIO_CMN_D, - { - .vlv.idx = PUNIT_PWGT_IDX_DPIO_CMN_D, - }, - }, -}; - -bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, - enum i915_power_well_id power_well_id) -{ - struct i915_power_well *power_well; - bool ret; - - power_well = lookup_power_well(dev_priv, power_well_id); - ret = power_well->desc->ops->is_enabled(dev_priv, power_well); - - return ret; -} - -static const struct i915_power_well_desc skl_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "MISC IO power well", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_MISC_IO, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_MISC_IO, - }, - }, - { - .name = "DC off", - .domains = SKL_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 2", - .domains = SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_2, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_PW_2, - .hsw.irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "DDI A/E IO power well", - .domains = SKL_DISPLAY_DDI_IO_A_E_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_DDI_A_E, - }, - }, - { - .name = "DDI B IO power well", - .domains = SKL_DISPLAY_DDI_IO_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_DDI_B, - }, - }, - { - .name = "DDI C IO power well", - .domains = SKL_DISPLAY_DDI_IO_C_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_DDI_C, - }, - }, - { - .name = "DDI D IO power well", - .domains = SKL_DISPLAY_DDI_IO_D_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_DDI_D, - }, - }, -}; - -static const struct i915_power_well_desc bxt_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "DC off", - .domains = BXT_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 2", - .domains = BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_2, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_PW_2, - .hsw.irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "dpio-common-a", - .domains = BXT_DPIO_CMN_A_POWER_DOMAINS, - .ops = &bxt_dpio_cmn_power_well_ops, - .id = BXT_DISP_PW_DPIO_CMN_A, - { - .bxt.phy = DPIO_PHY1, - }, - }, - { - .name = "dpio-common-bc", - .domains = BXT_DPIO_CMN_BC_POWER_DOMAINS, - .ops = &bxt_dpio_cmn_power_well_ops, - .id = VLV_DISP_PW_DPIO_CMN_BC, - { - .bxt.phy = DPIO_PHY0, - }, - }, -}; - -static const struct i915_power_well_desc glk_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "DC off", - .domains = GLK_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 2", - .domains = GLK_DISPLAY_POWERWELL_2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_2, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_PW_2, - .hsw.irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "dpio-common-a", - .domains = GLK_DPIO_CMN_A_POWER_DOMAINS, - .ops = &bxt_dpio_cmn_power_well_ops, - .id = BXT_DISP_PW_DPIO_CMN_A, - { - .bxt.phy = DPIO_PHY1, - }, - }, - { - .name = "dpio-common-b", - .domains = GLK_DPIO_CMN_B_POWER_DOMAINS, - .ops = &bxt_dpio_cmn_power_well_ops, - .id = VLV_DISP_PW_DPIO_CMN_BC, - { - .bxt.phy = DPIO_PHY0, - }, - }, - { - .name = "dpio-common-c", - .domains = GLK_DPIO_CMN_C_POWER_DOMAINS, - .ops = &bxt_dpio_cmn_power_well_ops, - .id = GLK_DISP_PW_DPIO_CMN_C, - { - .bxt.phy = DPIO_PHY2, - }, - }, - { - .name = "AUX A", - .domains = GLK_DISPLAY_AUX_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = GLK_PW_CTL_IDX_AUX_A, - }, - }, - { - .name = "AUX B", - .domains = GLK_DISPLAY_AUX_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = GLK_PW_CTL_IDX_AUX_B, - }, - }, - { - .name = "AUX C", - .domains = GLK_DISPLAY_AUX_C_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = GLK_PW_CTL_IDX_AUX_C, - }, - }, - { - .name = "DDI A IO power well", - .domains = GLK_DISPLAY_DDI_IO_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = GLK_PW_CTL_IDX_DDI_A, - }, - }, - { - .name = "DDI B IO power well", - .domains = GLK_DISPLAY_DDI_IO_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_DDI_B, - }, - }, - { - .name = "DDI C IO power well", - .domains = GLK_DISPLAY_DDI_IO_C_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = SKL_PW_CTL_IDX_DDI_C, - }, - }, -}; - -static const struct i915_power_well_ops icl_aux_power_well_ops = { - .sync_hw = hsw_power_well_sync_hw, - .enable = icl_aux_power_well_enable, - .disable = icl_aux_power_well_disable, - .is_enabled = hsw_power_well_enabled, -}; - -static const struct i915_power_well_regs icl_aux_power_well_regs = { - .bios = ICL_PWR_WELL_CTL_AUX1, - .driver = ICL_PWR_WELL_CTL_AUX2, - .debug = ICL_PWR_WELL_CTL_AUX4, -}; - -static const struct i915_power_well_regs icl_ddi_power_well_regs = { - .bios = ICL_PWR_WELL_CTL_DDI1, - .driver = ICL_PWR_WELL_CTL_DDI2, - .debug = ICL_PWR_WELL_CTL_DDI4, -}; - -static const struct i915_power_well_desc icl_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "DC off", - .domains = ICL_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 2", - .domains = ICL_PW_2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_2, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_2, - .hsw.has_fuses = true, - }, - }, - { - .name = "power well 3", - .domains = ICL_PW_3_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = ICL_DISP_PW_3, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_3, - .hsw.irq_pipe_mask = BIT(PIPE_B), - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "DDI A IO", - .domains = ICL_DDI_IO_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_A, - }, - }, - { - .name = "DDI B IO", - .domains = ICL_DDI_IO_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_B, - }, - }, - { - .name = "DDI C IO", - .domains = ICL_DDI_IO_C_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_C, - }, - }, - { - .name = "DDI D IO", - .domains = ICL_DDI_IO_D_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_D, - }, - }, - { - .name = "DDI E IO", - .domains = ICL_DDI_IO_E_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_E, - }, - }, - { - .name = "DDI F IO", - .domains = ICL_DDI_IO_F_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_F, - }, - }, - { - .name = "AUX A", - .domains = ICL_AUX_A_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_A, - }, - }, - { - .name = "AUX B", - .domains = ICL_AUX_B_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_B, - }, - }, - { - .name = "AUX C TC1", - .domains = ICL_AUX_C_TC1_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_C, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX D TC2", - .domains = ICL_AUX_D_TC2_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_D, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX E TC3", - .domains = ICL_AUX_E_TC3_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_E, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX F TC4", - .domains = ICL_AUX_F_TC4_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_F, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX C TBT1", - .domains = ICL_AUX_C_TBT1_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT1, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX D TBT2", - .domains = ICL_AUX_D_TBT2_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT2, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX E TBT3", - .domains = ICL_AUX_E_TBT3_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT3, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX F TBT4", - .domains = ICL_AUX_F_TBT4_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT4, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "power well 4", - .domains = ICL_PW_4_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_4, - .hsw.has_fuses = true, - .hsw.irq_pipe_mask = BIT(PIPE_C), - }, - }, -}; - -static void -tgl_tc_cold_request(struct drm_i915_private *i915, bool block) -{ - u8 tries = 0; - int ret; - - while (1) { - u32 low_val; - u32 high_val = 0; - - if (block) - low_val = TGL_PCODE_EXIT_TCCOLD_DATA_L_BLOCK_REQ; - else - low_val = TGL_PCODE_EXIT_TCCOLD_DATA_L_UNBLOCK_REQ; - - /* - * Spec states that we should timeout the request after 200us - * but the function below will timeout after 500us - */ - ret = snb_pcode_read(i915, TGL_PCODE_TCCOLD, &low_val, &high_val); - if (ret == 0) { - if (block && - (low_val & TGL_PCODE_EXIT_TCCOLD_DATA_L_EXIT_FAILED)) - ret = -EIO; - else - break; - } - - if (++tries == 3) - break; - - msleep(1); - } - - if (ret) - drm_err(&i915->drm, "TC cold %sblock failed\n", - block ? "" : "un"); - else - drm_dbg_kms(&i915->drm, "TC cold %sblock succeeded\n", - block ? "" : "un"); -} - -static void -tgl_tc_cold_off_power_well_enable(struct drm_i915_private *i915, - struct i915_power_well *power_well) -{ - tgl_tc_cold_request(i915, true); -} - -static void -tgl_tc_cold_off_power_well_disable(struct drm_i915_private *i915, - struct i915_power_well *power_well) -{ - tgl_tc_cold_request(i915, false); -} - -static void -tgl_tc_cold_off_power_well_sync_hw(struct drm_i915_private *i915, - struct i915_power_well *power_well) -{ - if (power_well->count > 0) - tgl_tc_cold_off_power_well_enable(i915, power_well); - else - tgl_tc_cold_off_power_well_disable(i915, power_well); -} - -static bool -tgl_tc_cold_off_power_well_is_enabled(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - /* - * Not the correctly implementation but there is no way to just read it - * from PCODE, so returning count to avoid state mismatch errors - */ - return power_well->count; -} - -static const struct i915_power_well_ops tgl_tc_cold_off_ops = { - .sync_hw = tgl_tc_cold_off_power_well_sync_hw, - .enable = tgl_tc_cold_off_power_well_enable, - .disable = tgl_tc_cold_off_power_well_disable, - .is_enabled = tgl_tc_cold_off_power_well_is_enabled, -}; - -static const struct i915_power_well_desc tgl_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "DC off", - .domains = TGL_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 2", - .domains = TGL_PW_2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_2, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_2, - .hsw.has_fuses = true, - }, - }, - { - .name = "power well 3", - .domains = TGL_PW_3_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = ICL_DISP_PW_3, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_3, - .hsw.irq_pipe_mask = BIT(PIPE_B), - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "DDI A IO", - .domains = ICL_DDI_IO_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_A, - } - }, - { - .name = "DDI B IO", - .domains = ICL_DDI_IO_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_B, - } - }, - { - .name = "DDI C IO", - .domains = ICL_DDI_IO_C_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_C, - } - }, - { - .name = "DDI IO TC1", - .domains = TGL_DDI_IO_TC1_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1, - }, - }, - { - .name = "DDI IO TC2", - .domains = TGL_DDI_IO_TC2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2, - }, - }, - { - .name = "DDI IO TC3", - .domains = TGL_DDI_IO_TC3_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC3, - }, - }, - { - .name = "DDI IO TC4", - .domains = TGL_DDI_IO_TC4_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC4, - }, - }, - { - .name = "DDI IO TC5", - .domains = TGL_DDI_IO_TC5_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC5, - }, - }, - { - .name = "DDI IO TC6", - .domains = TGL_DDI_IO_TC6_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC6, - }, - }, - { - .name = "TC cold off", - .domains = TGL_TC_COLD_OFF_POWER_DOMAINS, - .ops = &tgl_tc_cold_off_ops, - .id = TGL_DISP_PW_TC_COLD_OFF, - }, - { - .name = "AUX A", - .domains = TGL_AUX_A_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_A, - }, - }, - { - .name = "AUX B", - .domains = TGL_AUX_B_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_B, - }, - }, - { - .name = "AUX C", - .domains = TGL_AUX_C_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_C, - }, - }, - { - .name = "AUX USBC1", - .domains = TGL_AUX_IO_USBC1_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX USBC2", - .domains = TGL_AUX_IO_USBC2_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX USBC3", - .domains = TGL_AUX_IO_USBC3_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC3, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX USBC4", - .domains = TGL_AUX_IO_USBC4_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC4, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX USBC5", - .domains = TGL_AUX_IO_USBC5_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC5, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX USBC6", - .domains = TGL_AUX_IO_USBC6_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC6, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX TBT1", - .domains = TGL_AUX_IO_TBT1_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT1, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT2", - .domains = TGL_AUX_IO_TBT2_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT2, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT3", - .domains = TGL_AUX_IO_TBT3_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT3, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT4", - .domains = TGL_AUX_IO_TBT4_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT4, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT5", - .domains = TGL_AUX_IO_TBT5_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT5, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT6", - .domains = TGL_AUX_IO_TBT6_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT6, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "power well 4", - .domains = TGL_PW_4_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_4, - .hsw.has_fuses = true, - .hsw.irq_pipe_mask = BIT(PIPE_C), - } - }, - { - .name = "power well 5", - .domains = TGL_PW_5_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_PW_5, - .hsw.has_fuses = true, - .hsw.irq_pipe_mask = BIT(PIPE_D), - }, - }, -}; - -static const struct i915_power_well_desc rkl_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "DC off", - .domains = RKL_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 3", - .domains = RKL_PW_3_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = ICL_DISP_PW_3, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_3, - .hsw.irq_pipe_mask = BIT(PIPE_B), - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "power well 4", - .domains = RKL_PW_4_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_4, - .hsw.has_fuses = true, - .hsw.irq_pipe_mask = BIT(PIPE_C), - } - }, - { - .name = "DDI A IO", - .domains = ICL_DDI_IO_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_A, - } - }, - { - .name = "DDI B IO", - .domains = ICL_DDI_IO_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_B, - } - }, - { - .name = "DDI IO TC1", - .domains = TGL_DDI_IO_TC1_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1, - }, - }, - { - .name = "DDI IO TC2", - .domains = TGL_DDI_IO_TC2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2, - }, - }, - { - .name = "AUX A", - .domains = ICL_AUX_A_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_A, - }, - }, - { - .name = "AUX B", - .domains = ICL_AUX_B_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_B, - }, - }, - { - .name = "AUX USBC1", - .domains = TGL_AUX_IO_USBC1_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1, - }, - }, - { - .name = "AUX USBC2", - .domains = TGL_AUX_IO_USBC2_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2, - }, - }, -}; - -static const struct i915_power_well_desc dg1_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "DC off", - .domains = DG1_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 2", - .domains = DG1_PW_2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_2, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_2, - .hsw.has_fuses = true, - }, - }, - { - .name = "power well 3", - .domains = DG1_PW_3_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = ICL_DISP_PW_3, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_3, - .hsw.irq_pipe_mask = BIT(PIPE_B), - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "DDI A IO", - .domains = ICL_DDI_IO_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_A, - } - }, - { - .name = "DDI B IO", - .domains = ICL_DDI_IO_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_B, - } - }, - { - .name = "DDI IO TC1", - .domains = TGL_DDI_IO_TC1_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1, - }, - }, - { - .name = "DDI IO TC2", - .domains = TGL_DDI_IO_TC2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2, - }, - }, - { - .name = "AUX A", - .domains = TGL_AUX_A_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_A, - }, - }, - { - .name = "AUX B", - .domains = TGL_AUX_B_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_B, - }, - }, - { - .name = "AUX USBC1", - .domains = TGL_AUX_IO_USBC1_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "AUX USBC2", - .domains = TGL_AUX_IO_USBC2_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2, - .hsw.is_tc_tbt = false, - }, - }, - { - .name = "power well 4", - .domains = TGL_PW_4_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_4, - .hsw.has_fuses = true, - .hsw.irq_pipe_mask = BIT(PIPE_C), - } - }, - { - .name = "power well 5", - .domains = TGL_PW_5_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_PW_5, - .hsw.has_fuses = true, - .hsw.irq_pipe_mask = BIT(PIPE_D), - }, - }, -}; - -static const struct i915_power_well_desc xelpd_power_wells[] = { - { - .name = "always-on", - .always_on = true, - .domains = POWER_DOMAIN_MASK, - .ops = &i9xx_always_on_power_well_ops, - .id = DISP_PW_ID_NONE, - }, - { - .name = "power well 1", - /* Handled by the DMC firmware */ - .always_on = true, - .domains = 0, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_1, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_1, - .hsw.has_fuses = true, - }, - }, - { - .name = "DC off", - .domains = XELPD_DISPLAY_DC_OFF_POWER_DOMAINS, - .ops = &gen9_dc_off_power_well_ops, - .id = SKL_DISP_DC_OFF, - }, - { - .name = "power well 2", - .domains = XELPD_PW_2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = SKL_DISP_PW_2, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_PW_2, - .hsw.has_vga = true, - .hsw.has_fuses = true, - }, - }, - { - .name = "power well A", - .domains = XELPD_PW_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_PW_A, - .hsw.irq_pipe_mask = BIT(PIPE_A), - .hsw.has_fuses = true, - }, - }, - { - .name = "power well B", - .domains = XELPD_PW_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_PW_B, - .hsw.irq_pipe_mask = BIT(PIPE_B), - .hsw.has_fuses = true, - }, - }, - { - .name = "power well C", - .domains = XELPD_PW_C_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_PW_C, - .hsw.irq_pipe_mask = BIT(PIPE_C), - .hsw.has_fuses = true, - }, - }, - { - .name = "power well D", - .domains = XELPD_PW_D_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &hsw_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_PW_D, - .hsw.irq_pipe_mask = BIT(PIPE_D), - .hsw.has_fuses = true, - }, - }, - { - .name = "DDI A IO", - .domains = ICL_DDI_IO_A_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_A, - } - }, - { - .name = "DDI B IO", - .domains = ICL_DDI_IO_B_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_B, - } - }, - { - .name = "DDI C IO", - .domains = ICL_DDI_IO_C_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_DDI_C, - } - }, - { - .name = "DDI IO D_XELPD", - .domains = XELPD_DDI_IO_D_XELPD_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_DDI_D, - } - }, - { - .name = "DDI IO E_XELPD", - .domains = XELPD_DDI_IO_E_XELPD_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_DDI_E, - } - }, - { - .name = "DDI IO TC1", - .domains = XELPD_DDI_IO_TC1_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1, - } - }, - { - .name = "DDI IO TC2", - .domains = XELPD_DDI_IO_TC2_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2, - } - }, - { - .name = "DDI IO TC3", - .domains = XELPD_DDI_IO_TC3_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC3, - } - }, - { - .name = "DDI IO TC4", - .domains = XELPD_DDI_IO_TC4_POWER_DOMAINS, - .ops = &hsw_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_ddi_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_DDI_TC4, - } - }, - { - .name = "AUX A", - .domains = ICL_AUX_A_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_A, - .hsw.fixed_enable_delay = 600, - }, - }, - { - .name = "AUX B", - .domains = ICL_AUX_B_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_B, - .hsw.fixed_enable_delay = 600, - }, - }, - { - .name = "AUX C", - .domains = TGL_AUX_C_IO_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = ICL_PW_CTL_IDX_AUX_C, - .hsw.fixed_enable_delay = 600, - }, - }, - { - .name = "AUX D_XELPD", - .domains = XELPD_AUX_IO_D_XELPD_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_AUX_D, - .hsw.fixed_enable_delay = 600, - }, - }, - { - .name = "AUX E_XELPD", - .domains = XELPD_AUX_IO_E_XELPD_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = XELPD_PW_CTL_IDX_AUX_E, - }, - }, - { - .name = "AUX USBC1", - .domains = XELPD_AUX_IO_USBC1_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1, - .hsw.fixed_enable_delay = 600, - }, - }, - { - .name = "AUX USBC2", - .domains = XELPD_AUX_IO_USBC2_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2, - }, - }, - { - .name = "AUX USBC3", - .domains = XELPD_AUX_IO_USBC3_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC3, - }, - }, - { - .name = "AUX USBC4", - .domains = XELPD_AUX_IO_USBC4_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TC4, - }, - }, - { - .name = "AUX TBT1", - .domains = XELPD_AUX_IO_TBT1_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT1, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT2", - .domains = XELPD_AUX_IO_TBT2_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT2, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT3", - .domains = XELPD_AUX_IO_TBT3_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT3, - .hsw.is_tc_tbt = true, - }, - }, - { - .name = "AUX TBT4", - .domains = XELPD_AUX_IO_TBT4_POWER_DOMAINS, - .ops = &icl_aux_power_well_ops, - .id = DISP_PW_ID_NONE, - { - .hsw.regs = &icl_aux_power_well_regs, - .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT4, - .hsw.is_tc_tbt = true, - }, - }, -}; - static int sanitize_disable_power_well_option(const struct drm_i915_private *dev_priv, int disable_power_well) @@ -5142,57 +965,6 @@ static u32 get_allowed_dc_mask(const struct drm_i915_private *dev_priv, return mask; } -static int -__set_power_wells(struct i915_power_domains *power_domains, - const struct i915_power_well_desc *power_well_descs, - int power_well_descs_sz, u64 skip_mask) -{ - struct drm_i915_private *i915 = container_of(power_domains, - struct drm_i915_private, - power_domains); - u64 power_well_ids = 0; - int power_well_count = 0; - int i, plt_idx = 0; - - for (i = 0; i < power_well_descs_sz; i++) - if (!(BIT_ULL(power_well_descs[i].id) & skip_mask)) - power_well_count++; - - power_domains->power_well_count = power_well_count; - power_domains->power_wells = - kcalloc(power_well_count, - sizeof(*power_domains->power_wells), - GFP_KERNEL); - if (!power_domains->power_wells) - return -ENOMEM; - - for (i = 0; i < power_well_descs_sz; i++) { - enum i915_power_well_id id = power_well_descs[i].id; - - if (BIT_ULL(id) & skip_mask) - continue; - - power_domains->power_wells[plt_idx++].desc = - &power_well_descs[i]; - - if (id == DISP_PW_ID_NONE) - continue; - - drm_WARN_ON(&i915->drm, id >= sizeof(power_well_ids) * 8); - drm_WARN_ON(&i915->drm, power_well_ids & BIT_ULL(id)); - power_well_ids |= BIT_ULL(id); - } - - return 0; -} - -#define set_power_wells_mask(power_domains, __power_well_descs, skip_mask) \ - __set_power_wells(power_domains, __power_well_descs, \ - ARRAY_SIZE(__power_well_descs), skip_mask) - -#define set_power_wells(power_domains, __power_well_descs) \ - set_power_wells_mask(power_domains, __power_well_descs, 0) - /** * intel_power_domains_init - initializes the power domain structures * @dev_priv: i915 device instance @@ -5203,7 +975,6 @@ __set_power_wells(struct i915_power_domains *power_domains, int intel_power_domains_init(struct drm_i915_private *dev_priv) { struct i915_power_domains *power_domains = &dev_priv->power_domains; - int err; dev_priv->params.disable_power_well = sanitize_disable_power_well_option(dev_priv, @@ -5214,54 +985,12 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) dev_priv->dmc.target_dc_state = sanitize_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); - BUILD_BUG_ON(POWER_DOMAIN_NUM > 64); - mutex_init(&power_domains->lock); INIT_DELAYED_WORK(&power_domains->async_put_work, intel_display_power_put_async_work); - /* - * The enabling order will be from lower to higher indexed wells, - * the disabling order is reversed. - */ - if (!HAS_DISPLAY(dev_priv)) { - power_domains->power_well_count = 0; - err = 0; - } else if (DISPLAY_VER(dev_priv) >= 13) { - err = set_power_wells(power_domains, xelpd_power_wells); - } else if (IS_DG1(dev_priv)) { - err = set_power_wells(power_domains, dg1_power_wells); - } else if (IS_ALDERLAKE_S(dev_priv)) { - err = set_power_wells_mask(power_domains, tgl_power_wells, - BIT_ULL(TGL_DISP_PW_TC_COLD_OFF)); - } else if (IS_ROCKETLAKE(dev_priv)) { - err = set_power_wells(power_domains, rkl_power_wells); - } else if (DISPLAY_VER(dev_priv) == 12) { - err = set_power_wells(power_domains, tgl_power_wells); - } else if (DISPLAY_VER(dev_priv) == 11) { - err = set_power_wells(power_domains, icl_power_wells); - } else if (IS_GEMINILAKE(dev_priv)) { - err = set_power_wells(power_domains, glk_power_wells); - } else if (IS_BROXTON(dev_priv)) { - err = set_power_wells(power_domains, bxt_power_wells); - } else if (DISPLAY_VER(dev_priv) == 9) { - err = set_power_wells(power_domains, skl_power_wells); - } else if (IS_CHERRYVIEW(dev_priv)) { - err = set_power_wells(power_domains, chv_power_wells); - } else if (IS_BROADWELL(dev_priv)) { - err = set_power_wells(power_domains, bdw_power_wells); - } else if (IS_HASWELL(dev_priv)) { - err = set_power_wells(power_domains, hsw_power_wells); - } else if (IS_VALLEYVIEW(dev_priv)) { - err = set_power_wells(power_domains, vlv_power_wells); - } else if (IS_I830(dev_priv)) { - err = set_power_wells(power_domains, i830_power_wells); - } else { - err = set_power_wells(power_domains, i9xx_always_on_power_well); - } - - return err; + return intel_display_power_map_init(power_domains); } /** @@ -5272,7 +1001,7 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) */ void intel_power_domains_cleanup(struct drm_i915_private *dev_priv) { - kfree(dev_priv->power_domains.power_wells); + intel_display_power_map_cleanup(&dev_priv->power_domains); } static void intel_power_domains_sync_hw(struct drm_i915_private *dev_priv) @@ -5281,11 +1010,8 @@ static void intel_power_domains_sync_hw(struct drm_i915_private *dev_priv) struct i915_power_well *power_well; mutex_lock(&power_domains->lock); - for_each_power_well(dev_priv, power_well) { - power_well->desc->ops->sync_hw(dev_priv, power_well); - power_well->hw_enabled = - power_well->desc->ops->is_enabled(dev_priv, power_well); - } + for_each_power_well(dev_priv, power_well) + intel_power_well_sync_hw(dev_priv, power_well); mutex_unlock(&power_domains->lock); } @@ -5303,7 +1029,7 @@ static void gen9_dbuf_slice_set(struct drm_i915_private *dev_priv, state = intel_de_read(dev_priv, reg) & DBUF_POWER_STATE; drm_WARN(&dev_priv->drm, enable != state, "DBuf slice %d power %s timeout!\n", - slice, enabledisable(enable)); + slice, str_enable_disable(enable)); } void gen9_dbuf_slices_update(struct drm_i915_private *dev_priv, @@ -5692,7 +1418,7 @@ static void skl_display_core_init(struct drm_i915_private *dev_priv, gen9_dbuf_enable(dev_priv); - if (resume && intel_dmc_has_payload(dev_priv)) + if (resume) intel_dmc_load_program(dev_priv); } @@ -5759,7 +1485,7 @@ static void bxt_display_core_init(struct drm_i915_private *dev_priv, bool resume gen9_dbuf_enable(dev_priv); - if (resume && intel_dmc_has_payload(dev_priv)) + if (resume) intel_dmc_load_program(dev_priv); } @@ -5923,7 +1649,7 @@ static void icl_display_core_init(struct drm_i915_private *dev_priv, if (IS_DG2(dev_priv)) intel_snps_phy_wait_for_calibration(dev_priv); - if (resume && intel_dmc_has_payload(dev_priv)) + if (resume) intel_dmc_load_program(dev_priv); /* Wa_14011508470:tgl,dg1,rkl,adl-s,adl-p */ @@ -5998,7 +1724,7 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv) * override and set the lane powerdown bits accding to the * current lane status. */ - if (cmn_bc->desc->ops->is_enabled(dev_priv, cmn_bc)) { + if (intel_power_well_is_enabled(dev_priv, cmn_bc)) { u32 status = intel_de_read(dev_priv, DPLL(PIPE_A)); unsigned int mask; @@ -6029,7 +1755,7 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv) dev_priv->chv_phy_assert[DPIO_PHY0] = true; } - if (cmn_d->desc->ops->is_enabled(dev_priv, cmn_d)) { + if (intel_power_well_is_enabled(dev_priv, cmn_d)) { u32 status = intel_de_read(dev_priv, DPIO_PHY_STATUS); unsigned int mask; @@ -6065,15 +1791,15 @@ static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) lookup_power_well(dev_priv, VLV_DISP_PW_DISP2D); /* If the display might be already active skip this */ - if (cmn->desc->ops->is_enabled(dev_priv, cmn) && - disp2d->desc->ops->is_enabled(dev_priv, disp2d) && + if (intel_power_well_is_enabled(dev_priv, cmn) && + intel_power_well_is_enabled(dev_priv, disp2d) && intel_de_read(dev_priv, DPIO_CTL) & DPIO_CMNRST) return; drm_dbg_kms(&dev_priv->drm, "toggling display PHY side reset\n"); /* cmnlane needs DPLL registers */ - disp2d->desc->ops->enable(dev_priv, disp2d); + intel_power_well_enable(dev_priv, disp2d); /* * From VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_11.docx: @@ -6082,7 +1808,7 @@ static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) * Simply ungating isn't enough to reset the PHY enough to get * ports and lanes running. */ - cmn->desc->ops->disable(dev_priv, cmn); + intel_power_well_disable(dev_priv, cmn); } static bool vlv_punit_is_power_gated(struct drm_i915_private *dev_priv, u32 reg0) @@ -6233,12 +1959,12 @@ void intel_power_domains_sanitize_state(struct drm_i915_private *i915) for_each_power_well_reverse(i915, power_well) { if (power_well->desc->always_on || power_well->count || - !power_well->desc->ops->is_enabled(i915, power_well)) + !intel_power_well_is_enabled(i915, power_well)) continue; drm_dbg_kms(&i915->drm, "BIOS left unused %s power well enabled, disabling it\n", - power_well->desc->name); + intel_power_well_name(power_well)); intel_power_well_disable(i915, power_well); } @@ -6377,9 +2103,9 @@ static void intel_power_domains_dump_info(struct drm_i915_private *i915) enum intel_display_power_domain domain; drm_dbg(&i915->drm, "%-25s %d\n", - power_well->desc->name, power_well->count); + intel_power_well_name(power_well), intel_power_well_refcount(power_well)); - for_each_power_domain(domain, power_well->desc->domains) + for_each_power_domain(domain, intel_power_well_domains(power_well)) drm_dbg(&i915->drm, " %-23s %d\n", intel_display_power_domain_str(domain), power_domains->domain_use_count[domain]); @@ -6412,23 +2138,25 @@ static void intel_power_domains_verify_state(struct drm_i915_private *i915) int domains_count; bool enabled; - enabled = power_well->desc->ops->is_enabled(i915, power_well); - if ((power_well->count || power_well->desc->always_on) != + enabled = intel_power_well_is_enabled(i915, power_well); + if ((intel_power_well_refcount(power_well) || + intel_power_well_is_always_on(power_well)) != enabled) drm_err(&i915->drm, "power well %s state mismatch (refcount %d/enabled %d)", - power_well->desc->name, - power_well->count, enabled); + intel_power_well_name(power_well), + intel_power_well_refcount(power_well), enabled); domains_count = 0; - for_each_power_domain(domain, power_well->desc->domains) + for_each_power_domain(domain, intel_power_well_domains(power_well)) domains_count += power_domains->domain_use_count[domain]; - if (power_well->count != domains_count) { + if (intel_power_well_refcount(power_well) != domains_count) { drm_err(&i915->drm, "power well %s refcount/domain refcount mismatch " "(refcount %d/domains refcount %d)\n", - power_well->desc->name, power_well->count, + intel_power_well_name(power_well), + intel_power_well_refcount(power_well), domains_count); dump_domain_info = true; } @@ -6533,10 +2261,10 @@ void intel_display_power_debug(struct drm_i915_private *i915, struct seq_file *m enum intel_display_power_domain power_domain; power_well = &power_domains->power_wells[i]; - seq_printf(m, "%-25s %d\n", power_well->desc->name, - power_well->count); + seq_printf(m, "%-25s %d\n", intel_power_well_name(power_well), + intel_power_well_refcount(power_well)); - for_each_power_domain(power_domain, power_well->desc->domains) + for_each_power_domain(power_domain, intel_power_well_domains(power_well)) seq_printf(m, " %-23s %d\n", intel_display_power_domain_str(power_domain), power_domains->domain_use_count[power_domain]); @@ -6544,3 +2272,209 @@ void intel_display_power_debug(struct drm_i915_private *i915, struct seq_file *m mutex_unlock(&power_domains->lock); } + +struct intel_ddi_port_domains { + enum port port_start; + enum port port_end; + enum aux_ch aux_ch_start; + enum aux_ch aux_ch_end; + + enum intel_display_power_domain ddi_lanes; + enum intel_display_power_domain ddi_io; + enum intel_display_power_domain aux_legacy_usbc; + enum intel_display_power_domain aux_tbt; +}; + +static const struct intel_ddi_port_domains +i9xx_port_domains[] = { + { + .port_start = PORT_A, + .port_end = PORT_F, + .aux_ch_start = AUX_CH_A, + .aux_ch_end = AUX_CH_F, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_A, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_A, + .aux_legacy_usbc = POWER_DOMAIN_AUX_A, + .aux_tbt = POWER_DOMAIN_INVALID, + }, +}; + +static const struct intel_ddi_port_domains +d11_port_domains[] = { + { + .port_start = PORT_A, + .port_end = PORT_B, + .aux_ch_start = AUX_CH_A, + .aux_ch_end = AUX_CH_B, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_A, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_A, + .aux_legacy_usbc = POWER_DOMAIN_AUX_A, + .aux_tbt = POWER_DOMAIN_INVALID, + }, { + .port_start = PORT_C, + .port_end = PORT_F, + .aux_ch_start = AUX_CH_C, + .aux_ch_end = AUX_CH_F, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_C, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_C, + .aux_legacy_usbc = POWER_DOMAIN_AUX_C, + .aux_tbt = POWER_DOMAIN_AUX_TBT1, + }, +}; + +static const struct intel_ddi_port_domains +d12_port_domains[] = { + { + .port_start = PORT_A, + .port_end = PORT_C, + .aux_ch_start = AUX_CH_A, + .aux_ch_end = AUX_CH_C, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_A, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_A, + .aux_legacy_usbc = POWER_DOMAIN_AUX_A, + .aux_tbt = POWER_DOMAIN_INVALID, + }, { + .port_start = PORT_TC1, + .port_end = PORT_TC6, + .aux_ch_start = AUX_CH_USBC1, + .aux_ch_end = AUX_CH_USBC6, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_TC1, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_TC1, + .aux_legacy_usbc = POWER_DOMAIN_AUX_USBC1, + .aux_tbt = POWER_DOMAIN_AUX_TBT1, + }, +}; + +static const struct intel_ddi_port_domains +d13_port_domains[] = { + { + .port_start = PORT_A, + .port_end = PORT_C, + .aux_ch_start = AUX_CH_A, + .aux_ch_end = AUX_CH_C, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_A, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_A, + .aux_legacy_usbc = POWER_DOMAIN_AUX_A, + .aux_tbt = POWER_DOMAIN_INVALID, + }, { + .port_start = PORT_TC1, + .port_end = PORT_TC4, + .aux_ch_start = AUX_CH_USBC1, + .aux_ch_end = AUX_CH_USBC4, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_TC1, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_TC1, + .aux_legacy_usbc = POWER_DOMAIN_AUX_USBC1, + .aux_tbt = POWER_DOMAIN_AUX_TBT1, + }, { + .port_start = PORT_D_XELPD, + .port_end = PORT_E_XELPD, + .aux_ch_start = AUX_CH_D_XELPD, + .aux_ch_end = AUX_CH_E_XELPD, + + .ddi_lanes = POWER_DOMAIN_PORT_DDI_LANES_D, + .ddi_io = POWER_DOMAIN_PORT_DDI_IO_D, + .aux_legacy_usbc = POWER_DOMAIN_AUX_D, + .aux_tbt = POWER_DOMAIN_INVALID, + }, +}; + +static void +intel_port_domains_for_platform(struct drm_i915_private *i915, + const struct intel_ddi_port_domains **domains, + int *domains_size) +{ + if (DISPLAY_VER(i915) >= 13) { + *domains = d13_port_domains; + *domains_size = ARRAY_SIZE(d13_port_domains); + } else if (DISPLAY_VER(i915) >= 12) { + *domains = d12_port_domains; + *domains_size = ARRAY_SIZE(d12_port_domains); + } else if (DISPLAY_VER(i915) >= 11) { + *domains = d11_port_domains; + *domains_size = ARRAY_SIZE(d11_port_domains); + } else { + *domains = i9xx_port_domains; + *domains_size = ARRAY_SIZE(i9xx_port_domains); + } +} + +static const struct intel_ddi_port_domains * +intel_port_domains_for_port(struct drm_i915_private *i915, enum port port) +{ + const struct intel_ddi_port_domains *domains; + int domains_size; + int i; + + intel_port_domains_for_platform(i915, &domains, &domains_size); + for (i = 0; i < domains_size; i++) + if (port >= domains[i].port_start && port <= domains[i].port_end) + return &domains[i]; + + return NULL; +} + +enum intel_display_power_domain +intel_display_power_ddi_io_domain(struct drm_i915_private *i915, enum port port) +{ + const struct intel_ddi_port_domains *domains = intel_port_domains_for_port(i915, port); + + if (drm_WARN_ON(&i915->drm, !domains) || domains->ddi_io == POWER_DOMAIN_INVALID) + return POWER_DOMAIN_PORT_DDI_IO_A; + + return domains->ddi_io + (int)(port - domains->port_start); +} + +enum intel_display_power_domain +intel_display_power_ddi_lanes_domain(struct drm_i915_private *i915, enum port port) +{ + const struct intel_ddi_port_domains *domains = intel_port_domains_for_port(i915, port); + + if (drm_WARN_ON(&i915->drm, !domains) || domains->ddi_lanes == POWER_DOMAIN_INVALID) + return POWER_DOMAIN_PORT_DDI_LANES_A; + + return domains->ddi_lanes + (int)(port - domains->port_start); +} + +static const struct intel_ddi_port_domains * +intel_port_domains_for_aux_ch(struct drm_i915_private *i915, enum aux_ch aux_ch) +{ + const struct intel_ddi_port_domains *domains; + int domains_size; + int i; + + intel_port_domains_for_platform(i915, &domains, &domains_size); + for (i = 0; i < domains_size; i++) + if (aux_ch >= domains[i].aux_ch_start && aux_ch <= domains[i].aux_ch_end) + return &domains[i]; + + return NULL; +} + +enum intel_display_power_domain +intel_display_power_legacy_aux_domain(struct drm_i915_private *i915, enum aux_ch aux_ch) +{ + const struct intel_ddi_port_domains *domains = intel_port_domains_for_aux_ch(i915, aux_ch); + + if (drm_WARN_ON(&i915->drm, !domains) || domains->aux_legacy_usbc == POWER_DOMAIN_INVALID) + return POWER_DOMAIN_AUX_A; + + return domains->aux_legacy_usbc + (int)(aux_ch - domains->aux_ch_start); +} + +enum intel_display_power_domain +intel_display_power_tbt_aux_domain(struct drm_i915_private *i915, enum aux_ch aux_ch) +{ + const struct intel_ddi_port_domains *domains = intel_port_domains_for_aux_ch(i915, aux_ch); + + if (drm_WARN_ON(&i915->drm, !domains) || domains->aux_tbt == POWER_DOMAIN_INVALID) + return POWER_DOMAIN_AUX_TBT1; + + return domains->aux_tbt + (int)(aux_ch - domains->aux_ch_start); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index f6d0e6e73c6d..7136ea3f233e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -8,71 +8,68 @@ #include "intel_runtime_pm.h" +enum aux_ch; enum dpio_channel; enum dpio_phy; +enum port; struct drm_i915_private; struct i915_power_well; struct intel_encoder; +/* + * Keep the pipe, transcoder, port (DDI_LANES,DDI_IO,AUX) domain instances + * consecutive, so that the pipe,transcoder,port -> power domain macros + * work correctly. + */ enum intel_display_power_domain { POWER_DOMAIN_DISPLAY_CORE, POWER_DOMAIN_PIPE_A, POWER_DOMAIN_PIPE_B, POWER_DOMAIN_PIPE_C, POWER_DOMAIN_PIPE_D, - POWER_DOMAIN_PIPE_A_PANEL_FITTER, - POWER_DOMAIN_PIPE_B_PANEL_FITTER, - POWER_DOMAIN_PIPE_C_PANEL_FITTER, - POWER_DOMAIN_PIPE_D_PANEL_FITTER, + POWER_DOMAIN_PIPE_PANEL_FITTER_A, + POWER_DOMAIN_PIPE_PANEL_FITTER_B, + POWER_DOMAIN_PIPE_PANEL_FITTER_C, + POWER_DOMAIN_PIPE_PANEL_FITTER_D, POWER_DOMAIN_TRANSCODER_A, POWER_DOMAIN_TRANSCODER_B, POWER_DOMAIN_TRANSCODER_C, POWER_DOMAIN_TRANSCODER_D, POWER_DOMAIN_TRANSCODER_EDP, - /* VDSC/joining for eDP/DSI transcoder (ICL) or pipe A (TGL) */ - POWER_DOMAIN_TRANSCODER_VDSC_PW2, POWER_DOMAIN_TRANSCODER_DSI_A, POWER_DOMAIN_TRANSCODER_DSI_C, - POWER_DOMAIN_PORT_DDI_A_LANES, - POWER_DOMAIN_PORT_DDI_B_LANES, - POWER_DOMAIN_PORT_DDI_C_LANES, - POWER_DOMAIN_PORT_DDI_D_LANES, - POWER_DOMAIN_PORT_DDI_E_LANES, - POWER_DOMAIN_PORT_DDI_F_LANES, - POWER_DOMAIN_PORT_DDI_G_LANES, - POWER_DOMAIN_PORT_DDI_H_LANES, - POWER_DOMAIN_PORT_DDI_I_LANES, - - POWER_DOMAIN_PORT_DDI_LANES_TC1 = POWER_DOMAIN_PORT_DDI_D_LANES, /* tgl+ */ + + /* VDSC/joining for eDP/DSI transcoder (ICL) or pipe A (TGL) */ + POWER_DOMAIN_TRANSCODER_VDSC_PW2, + + POWER_DOMAIN_PORT_DDI_LANES_A, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_PORT_DDI_LANES_D, + POWER_DOMAIN_PORT_DDI_LANES_E, + POWER_DOMAIN_PORT_DDI_LANES_F, + + POWER_DOMAIN_PORT_DDI_LANES_TC1, POWER_DOMAIN_PORT_DDI_LANES_TC2, POWER_DOMAIN_PORT_DDI_LANES_TC3, POWER_DOMAIN_PORT_DDI_LANES_TC4, POWER_DOMAIN_PORT_DDI_LANES_TC5, POWER_DOMAIN_PORT_DDI_LANES_TC6, - POWER_DOMAIN_PORT_DDI_LANES_D_XELPD = POWER_DOMAIN_PORT_DDI_LANES_TC5, /* XELPD */ - POWER_DOMAIN_PORT_DDI_LANES_E_XELPD, - - POWER_DOMAIN_PORT_DDI_A_IO, - POWER_DOMAIN_PORT_DDI_B_IO, - POWER_DOMAIN_PORT_DDI_C_IO, - POWER_DOMAIN_PORT_DDI_D_IO, - POWER_DOMAIN_PORT_DDI_E_IO, - POWER_DOMAIN_PORT_DDI_F_IO, - POWER_DOMAIN_PORT_DDI_G_IO, - POWER_DOMAIN_PORT_DDI_H_IO, - POWER_DOMAIN_PORT_DDI_I_IO, + POWER_DOMAIN_PORT_DDI_IO_A, + POWER_DOMAIN_PORT_DDI_IO_B, + POWER_DOMAIN_PORT_DDI_IO_C, + POWER_DOMAIN_PORT_DDI_IO_D, + POWER_DOMAIN_PORT_DDI_IO_E, + POWER_DOMAIN_PORT_DDI_IO_F, - POWER_DOMAIN_PORT_DDI_IO_TC1 = POWER_DOMAIN_PORT_DDI_D_IO, /* tgl+ */ + POWER_DOMAIN_PORT_DDI_IO_TC1, POWER_DOMAIN_PORT_DDI_IO_TC2, POWER_DOMAIN_PORT_DDI_IO_TC3, POWER_DOMAIN_PORT_DDI_IO_TC4, POWER_DOMAIN_PORT_DDI_IO_TC5, POWER_DOMAIN_PORT_DDI_IO_TC6, - POWER_DOMAIN_PORT_DDI_IO_D_XELPD = POWER_DOMAIN_PORT_DDI_IO_TC5, /* XELPD */ - POWER_DOMAIN_PORT_DDI_IO_E_XELPD, - POWER_DOMAIN_PORT_DSI, POWER_DOMAIN_PORT_CRT, POWER_DOMAIN_PORT_OTHER, @@ -85,30 +82,17 @@ enum intel_display_power_domain { POWER_DOMAIN_AUX_D, POWER_DOMAIN_AUX_E, POWER_DOMAIN_AUX_F, - POWER_DOMAIN_AUX_G, - POWER_DOMAIN_AUX_H, - POWER_DOMAIN_AUX_I, - POWER_DOMAIN_AUX_USBC1 = POWER_DOMAIN_AUX_D, /* tgl+ */ + POWER_DOMAIN_AUX_USBC1, POWER_DOMAIN_AUX_USBC2, POWER_DOMAIN_AUX_USBC3, POWER_DOMAIN_AUX_USBC4, POWER_DOMAIN_AUX_USBC5, POWER_DOMAIN_AUX_USBC6, - POWER_DOMAIN_AUX_D_XELPD = POWER_DOMAIN_AUX_USBC5, /* XELPD */ - POWER_DOMAIN_AUX_E_XELPD, - POWER_DOMAIN_AUX_IO_A, - POWER_DOMAIN_AUX_C_TBT, - POWER_DOMAIN_AUX_D_TBT, - POWER_DOMAIN_AUX_E_TBT, - POWER_DOMAIN_AUX_F_TBT, - POWER_DOMAIN_AUX_G_TBT, - POWER_DOMAIN_AUX_H_TBT, - POWER_DOMAIN_AUX_I_TBT, - - POWER_DOMAIN_AUX_TBT1 = POWER_DOMAIN_AUX_D_TBT, /* tgl+ */ + + POWER_DOMAIN_AUX_TBT1, POWER_DOMAIN_AUX_TBT2, POWER_DOMAIN_AUX_TBT3, POWER_DOMAIN_AUX_TBT4, @@ -123,39 +107,20 @@ enum intel_display_power_domain { POWER_DOMAIN_INIT, POWER_DOMAIN_NUM, -}; - -/* - * i915_power_well_id: - * - * IDs used to look up power wells. Power wells accessed directly bypassing - * the power domains framework must be assigned a unique ID. The rest of power - * wells must be assigned DISP_PW_ID_NONE. - */ -enum i915_power_well_id { - DISP_PW_ID_NONE, - - VLV_DISP_PW_DISP2D, - BXT_DISP_PW_DPIO_CMN_A, - VLV_DISP_PW_DPIO_CMN_BC, - GLK_DISP_PW_DPIO_CMN_C, - CHV_DISP_PW_DPIO_CMN_D, - HSW_DISP_PW_GLOBAL, - SKL_DISP_PW_MISC_IO, - SKL_DISP_PW_1, - SKL_DISP_PW_2, - ICL_DISP_PW_3, - SKL_DISP_DC_OFF, - TGL_DISP_PW_TC_COLD_OFF, + POWER_DOMAIN_INVALID = POWER_DOMAIN_NUM, }; #define POWER_DOMAIN_PIPE(pipe) ((pipe) + POWER_DOMAIN_PIPE_A) #define POWER_DOMAIN_PIPE_PANEL_FITTER(pipe) \ - ((pipe) + POWER_DOMAIN_PIPE_A_PANEL_FITTER) + ((pipe) + POWER_DOMAIN_PIPE_PANEL_FITTER_A) #define POWER_DOMAIN_TRANSCODER(tran) \ ((tran) == TRANSCODER_EDP ? POWER_DOMAIN_TRANSCODER_EDP : \ (tran) + POWER_DOMAIN_TRANSCODER_A) +struct intel_power_domain_mask { + DECLARE_BITMAP(bits, POWER_DOMAIN_NUM); +}; + struct i915_power_domains { /* * Power wells needed for initialization at driver init and suspend @@ -173,41 +138,21 @@ struct i915_power_domains { struct delayed_work async_put_work; intel_wakeref_t async_put_wakeref; - u64 async_put_domains[2]; + struct intel_power_domain_mask async_put_domains[2]; struct i915_power_well *power_wells; }; struct intel_display_power_domain_set { - u64 mask; + struct intel_power_domain_mask mask; #ifdef CONFIG_DRM_I915_DEBUG_RUNTIME_PM intel_wakeref_t wakerefs[POWER_DOMAIN_NUM]; #endif }; -#define for_each_power_domain(domain, mask) \ - for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \ - for_each_if(BIT_ULL(domain) & (mask)) - -#define for_each_power_well(__dev_priv, __power_well) \ - for ((__power_well) = (__dev_priv)->power_domains.power_wells; \ - (__power_well) - (__dev_priv)->power_domains.power_wells < \ - (__dev_priv)->power_domains.power_well_count; \ - (__power_well)++) - -#define for_each_power_well_reverse(__dev_priv, __power_well) \ - for ((__power_well) = (__dev_priv)->power_domains.power_wells + \ - (__dev_priv)->power_domains.power_well_count - 1; \ - (__power_well) - (__dev_priv)->power_domains.power_wells >= 0; \ - (__power_well)--) - -#define for_each_power_domain_well(__dev_priv, __power_well, __domain_mask) \ - for_each_power_well(__dev_priv, __power_well) \ - for_each_if((__power_well)->desc->domains & (__domain_mask)) - -#define for_each_power_domain_well_reverse(__dev_priv, __power_well, __domain_mask) \ - for_each_power_well_reverse(__dev_priv, __power_well) \ - for_each_if((__power_well)->desc->domains & (__domain_mask)) +#define for_each_power_domain(__domain, __mask) \ + for ((__domain) = 0; (__domain) < POWER_DOMAIN_NUM; (__domain)++) \ + for_each_if(test_bit((__domain), (__mask)->bits)) int intel_power_domains_init(struct drm_i915_private *dev_priv); void intel_power_domains_cleanup(struct drm_i915_private *dev_priv); @@ -232,8 +177,6 @@ intel_display_power_domain_str(enum intel_display_power_domain domain); bool intel_display_power_is_enabled(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); -bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, - enum i915_power_well_id power_well_id); bool __intel_display_power_is_enabled(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); intel_wakeref_t intel_display_power_get(struct drm_i915_private *dev_priv, @@ -290,17 +233,26 @@ intel_display_power_get_in_set_if_enabled(struct drm_i915_private *i915, void intel_display_power_put_mask_in_set(struct drm_i915_private *i915, struct intel_display_power_domain_set *power_domain_set, - u64 mask); + struct intel_power_domain_mask *mask); static inline void intel_display_power_put_all_in_set(struct drm_i915_private *i915, struct intel_display_power_domain_set *power_domain_set) { - intel_display_power_put_mask_in_set(i915, power_domain_set, power_domain_set->mask); + intel_display_power_put_mask_in_set(i915, power_domain_set, &power_domain_set->mask); } void intel_display_power_debug(struct drm_i915_private *i915, struct seq_file *m); +enum intel_display_power_domain +intel_display_power_ddi_lanes_domain(struct drm_i915_private *i915, enum port port); +enum intel_display_power_domain +intel_display_power_ddi_io_domain(struct drm_i915_private *i915, enum port port); +enum intel_display_power_domain +intel_display_power_legacy_aux_domain(struct drm_i915_private *i915, enum aux_ch aux_ch); +enum intel_display_power_domain +intel_display_power_tbt_aux_domain(struct drm_i915_private *i915, enum aux_ch aux_ch); + /* * FIXME: We should probably switch this to a 0-based scheme to be consistent * with how we now name/number DBUF_CTL instances. @@ -324,9 +276,4 @@ void gen9_dbuf_slices_update(struct drm_i915_private *dev_priv, for ((wf) = intel_display_power_get_if_enabled((i915), (domain)); (wf); \ intel_display_power_put_async((i915), (domain), (wf)), (wf) = 0) -void chv_phy_powergate_lanes(struct intel_encoder *encoder, - bool override, unsigned int mask); -bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, - enum dpio_channel ch, bool override); - #endif /* __INTEL_DISPLAY_POWER_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display_power_map.c b/drivers/gpu/drm/i915/display/intel_display_power_map.c new file mode 100644 index 000000000000..97b367f39f35 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_power_map.c @@ -0,0 +1,1501 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 Intel Corporation + */ + +#include "i915_drv.h" +#include "i915_reg.h" + +#include "vlv_sideband_reg.h" + +#include "intel_display_power_map.h" +#include "intel_display_power_well.h" + +#define __LIST_INLINE_ELEMS(__elem_type, ...) \ + ((__elem_type[]) { __VA_ARGS__ }) + +#define __LIST(__elems) { \ + .list = __elems, \ + .count = ARRAY_SIZE(__elems), \ +} + +#define I915_PW_DOMAINS(...) \ + (const struct i915_power_domain_list) \ + __LIST(__LIST_INLINE_ELEMS(const enum intel_display_power_domain, __VA_ARGS__)) + +#define I915_DECL_PW_DOMAINS(__name, ...) \ + static const struct i915_power_domain_list __name = I915_PW_DOMAINS(__VA_ARGS__) + +/* Zero-length list assigns all power domains, a NULL list assigns none. */ +#define I915_PW_DOMAINS_NONE NULL +#define I915_PW_DOMAINS_ALL /* zero-length list */ + +#define I915_PW_INSTANCES(...) \ + (const struct i915_power_well_instance_list) \ + __LIST(__LIST_INLINE_ELEMS(const struct i915_power_well_instance, __VA_ARGS__)) + +#define I915_PW(_name, _domain_list, ...) \ + { .name = _name, .domain_list = _domain_list, ## __VA_ARGS__ } + + +struct i915_power_well_desc_list { + const struct i915_power_well_desc *list; + u8 count; +}; + +#define I915_PW_DESCRIPTORS(x) __LIST(x) + + +I915_DECL_PW_DOMAINS(i9xx_pwdoms_always_on, I915_PW_DOMAINS_ALL); + +static const struct i915_power_well_desc i9xx_power_wells_always_on[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("always-on", &i9xx_pwdoms_always_on), + ), + .ops = &i9xx_always_on_power_well_ops, + .always_on = true, + }, +}; + +static const struct i915_power_well_desc_list i9xx_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), +}; + +I915_DECL_PW_DOMAINS(i830_pwdoms_pipes, + POWER_DOMAIN_PIPE_A, + POWER_DOMAIN_PIPE_B, + POWER_DOMAIN_PIPE_PANEL_FITTER_A, + POWER_DOMAIN_PIPE_PANEL_FITTER_B, + POWER_DOMAIN_TRANSCODER_A, + POWER_DOMAIN_TRANSCODER_B, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc i830_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("pipes", &i830_pwdoms_pipes), + ), + .ops = &i830_pipes_power_well_ops, + }, +}; + +static const struct i915_power_well_desc_list i830_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(i830_power_wells_main), +}; + +I915_DECL_PW_DOMAINS(hsw_pwdoms_display, + POWER_DOMAIN_PIPE_B, + POWER_DOMAIN_PIPE_C, + POWER_DOMAIN_PIPE_PANEL_FITTER_A, + POWER_DOMAIN_PIPE_PANEL_FITTER_B, + POWER_DOMAIN_PIPE_PANEL_FITTER_C, + POWER_DOMAIN_TRANSCODER_A, + POWER_DOMAIN_TRANSCODER_B, + POWER_DOMAIN_TRANSCODER_C, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_PORT_DDI_LANES_D, + POWER_DOMAIN_PORT_CRT, /* DDI E */ + POWER_DOMAIN_VGA, + POWER_DOMAIN_AUDIO_MMIO, + POWER_DOMAIN_AUDIO_PLAYBACK, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc hsw_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("display", &hsw_pwdoms_display, + .hsw.idx = HSW_PW_CTL_IDX_GLOBAL, + .id = HSW_DISP_PW_GLOBAL), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + }, +}; + +static const struct i915_power_well_desc_list hsw_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(hsw_power_wells_main), +}; + +I915_DECL_PW_DOMAINS(bdw_pwdoms_display, + POWER_DOMAIN_PIPE_B, + POWER_DOMAIN_PIPE_C, + POWER_DOMAIN_PIPE_PANEL_FITTER_B, + POWER_DOMAIN_PIPE_PANEL_FITTER_C, + POWER_DOMAIN_TRANSCODER_A, + POWER_DOMAIN_TRANSCODER_B, + POWER_DOMAIN_TRANSCODER_C, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_PORT_DDI_LANES_D, + POWER_DOMAIN_PORT_CRT, /* DDI E */ + POWER_DOMAIN_VGA, + POWER_DOMAIN_AUDIO_MMIO, + POWER_DOMAIN_AUDIO_PLAYBACK, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc bdw_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("display", &bdw_pwdoms_display, + .hsw.idx = HSW_PW_CTL_IDX_GLOBAL, + .id = HSW_DISP_PW_GLOBAL), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), + }, +}; + +static const struct i915_power_well_desc_list bdw_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(bdw_power_wells_main), +}; + +I915_DECL_PW_DOMAINS(vlv_pwdoms_display, + POWER_DOMAIN_DISPLAY_CORE, + POWER_DOMAIN_PIPE_A, + POWER_DOMAIN_PIPE_B, + POWER_DOMAIN_PIPE_PANEL_FITTER_A, + POWER_DOMAIN_PIPE_PANEL_FITTER_B, + POWER_DOMAIN_TRANSCODER_A, + POWER_DOMAIN_TRANSCODER_B, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_PORT_DSI, + POWER_DOMAIN_PORT_CRT, + POWER_DOMAIN_VGA, + POWER_DOMAIN_AUDIO_MMIO, + POWER_DOMAIN_AUDIO_PLAYBACK, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_GMBUS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(vlv_pwdoms_dpio_cmn_bc, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_PORT_CRT, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(vlv_pwdoms_dpio_tx_bc_lanes, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc vlv_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("display", &vlv_pwdoms_display, + .vlv.idx = PUNIT_PWGT_IDX_DISP2D, + .id = VLV_DISP_PW_DISP2D), + ), + .ops = &vlv_display_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("dpio-tx-b-01", &vlv_pwdoms_dpio_tx_bc_lanes, + .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_B_LANES_01), + I915_PW("dpio-tx-b-23", &vlv_pwdoms_dpio_tx_bc_lanes, + .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_B_LANES_23), + I915_PW("dpio-tx-c-01", &vlv_pwdoms_dpio_tx_bc_lanes, + .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_C_LANES_01), + I915_PW("dpio-tx-c-23", &vlv_pwdoms_dpio_tx_bc_lanes, + .vlv.idx = PUNIT_PWGT_IDX_DPIO_TX_C_LANES_23), + ), + .ops = &vlv_dpio_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("dpio-common", &vlv_pwdoms_dpio_cmn_bc, + .vlv.idx = PUNIT_PWGT_IDX_DPIO_CMN_BC, + .id = VLV_DISP_PW_DPIO_CMN_BC), + ), + .ops = &vlv_dpio_cmn_power_well_ops, + }, +}; + +static const struct i915_power_well_desc_list vlv_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(vlv_power_wells_main), +}; + +I915_DECL_PW_DOMAINS(chv_pwdoms_display, + POWER_DOMAIN_DISPLAY_CORE, + POWER_DOMAIN_PIPE_A, + POWER_DOMAIN_PIPE_B, + POWER_DOMAIN_PIPE_C, + POWER_DOMAIN_PIPE_PANEL_FITTER_A, + POWER_DOMAIN_PIPE_PANEL_FITTER_B, + POWER_DOMAIN_PIPE_PANEL_FITTER_C, + POWER_DOMAIN_TRANSCODER_A, + POWER_DOMAIN_TRANSCODER_B, + POWER_DOMAIN_TRANSCODER_C, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_PORT_DDI_LANES_D, + POWER_DOMAIN_PORT_DSI, + POWER_DOMAIN_VGA, + POWER_DOMAIN_AUDIO_MMIO, + POWER_DOMAIN_AUDIO_PLAYBACK, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_AUX_D, + POWER_DOMAIN_GMBUS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(chv_pwdoms_dpio_cmn_bc, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(chv_pwdoms_dpio_cmn_d, + POWER_DOMAIN_PORT_DDI_LANES_D, + POWER_DOMAIN_AUX_D, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc chv_power_wells_main[] = { + { + /* + * Pipe A power well is the new disp2d well. Pipe B and C + * power wells don't actually exist. Pipe A power well is + * required for any pipe to work. + */ + .instances = &I915_PW_INSTANCES( + I915_PW("display", &chv_pwdoms_display), + ), + .ops = &chv_pipe_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("dpio-common-bc", &chv_pwdoms_dpio_cmn_bc, + .vlv.idx = PUNIT_PWGT_IDX_DPIO_CMN_BC, + .id = VLV_DISP_PW_DPIO_CMN_BC), + I915_PW("dpio-common-d", &chv_pwdoms_dpio_cmn_d, + .vlv.idx = PUNIT_PWGT_IDX_DPIO_CMN_D, + .id = CHV_DISP_PW_DPIO_CMN_D), + ), + .ops = &chv_dpio_cmn_power_well_ops, + }, +}; + +static const struct i915_power_well_desc_list chv_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(chv_power_wells_main), +}; + +#define SKL_PW_2_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_C, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_C, \ + POWER_DOMAIN_TRANSCODER_A, \ + POWER_DOMAIN_TRANSCODER_B, \ + POWER_DOMAIN_TRANSCODER_C, \ + POWER_DOMAIN_PORT_DDI_LANES_B, \ + POWER_DOMAIN_PORT_DDI_LANES_C, \ + POWER_DOMAIN_PORT_DDI_LANES_D, \ + POWER_DOMAIN_PORT_DDI_LANES_E, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_MMIO, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_B, \ + POWER_DOMAIN_AUX_C, \ + POWER_DOMAIN_AUX_D + +I915_DECL_PW_DOMAINS(skl_pwdoms_pw_2, + SKL_PW_2_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(skl_pwdoms_dc_off, + SKL_PW_2_POWER_DOMAINS, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_GT_IRQ, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(skl_pwdoms_ddi_io_a_e, + POWER_DOMAIN_PORT_DDI_IO_A, + POWER_DOMAIN_PORT_DDI_IO_E, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(skl_pwdoms_ddi_io_b, + POWER_DOMAIN_PORT_DDI_IO_B, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(skl_pwdoms_ddi_io_c, + POWER_DOMAIN_PORT_DDI_IO_C, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(skl_pwdoms_ddi_io_d, + POWER_DOMAIN_PORT_DDI_IO_D, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc skl_power_wells_pw_1[] = { + { + /* Handled by the DMC firmware */ + .instances = &I915_PW_INSTANCES( + I915_PW("PW_1", I915_PW_DOMAINS_NONE, + .hsw.idx = SKL_PW_CTL_IDX_PW_1, + .id = SKL_DISP_PW_1), + ), + .ops = &hsw_power_well_ops, + .always_on = true, + .has_fuses = true, + }, +}; + +static const struct i915_power_well_desc skl_power_wells_main[] = { + { + /* Handled by the DMC firmware */ + .instances = &I915_PW_INSTANCES( + I915_PW("MISC_IO", I915_PW_DOMAINS_NONE, + .hsw.idx = SKL_PW_CTL_IDX_MISC_IO, + .id = SKL_DISP_PW_MISC_IO), + ), + .ops = &hsw_power_well_ops, + .always_on = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &skl_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_2", &skl_pwdoms_pw_2, + .hsw.idx = SKL_PW_CTL_IDX_PW_2, + .id = SKL_DISP_PW_2), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("DDI_IO_A_E", &skl_pwdoms_ddi_io_a_e, .hsw.idx = SKL_PW_CTL_IDX_DDI_A_E), + I915_PW("DDI_IO_B", &skl_pwdoms_ddi_io_b, .hsw.idx = SKL_PW_CTL_IDX_DDI_B), + I915_PW("DDI_IO_C", &skl_pwdoms_ddi_io_c, .hsw.idx = SKL_PW_CTL_IDX_DDI_C), + I915_PW("DDI_IO_D", &skl_pwdoms_ddi_io_d, .hsw.idx = SKL_PW_CTL_IDX_DDI_D), + ), + .ops = &hsw_power_well_ops, + }, +}; + +static const struct i915_power_well_desc_list skl_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(skl_power_wells_pw_1), + I915_PW_DESCRIPTORS(skl_power_wells_main), +}; + +#define BXT_PW_2_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_C, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_C, \ + POWER_DOMAIN_TRANSCODER_A, \ + POWER_DOMAIN_TRANSCODER_B, \ + POWER_DOMAIN_TRANSCODER_C, \ + POWER_DOMAIN_PORT_DDI_LANES_B, \ + POWER_DOMAIN_PORT_DDI_LANES_C, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_MMIO, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_B, \ + POWER_DOMAIN_AUX_C + +I915_DECL_PW_DOMAINS(bxt_pwdoms_pw_2, + BXT_PW_2_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(bxt_pwdoms_dc_off, + BXT_PW_2_POWER_DOMAINS, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_GMBUS, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_GT_IRQ, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(bxt_pwdoms_dpio_cmn_a, + POWER_DOMAIN_PORT_DDI_LANES_A, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(bxt_pwdoms_dpio_cmn_bc, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc bxt_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &bxt_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_2", &bxt_pwdoms_pw_2, + .hsw.idx = SKL_PW_CTL_IDX_PW_2, + .id = SKL_DISP_PW_2), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("dpio-common-a", &bxt_pwdoms_dpio_cmn_a, + .bxt.phy = DPIO_PHY1, + .id = BXT_DISP_PW_DPIO_CMN_A), + I915_PW("dpio-common-bc", &bxt_pwdoms_dpio_cmn_bc, + .bxt.phy = DPIO_PHY0, + .id = VLV_DISP_PW_DPIO_CMN_BC), + ), + .ops = &bxt_dpio_cmn_power_well_ops, + }, +}; + +static const struct i915_power_well_desc_list bxt_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(skl_power_wells_pw_1), + I915_PW_DESCRIPTORS(bxt_power_wells_main), +}; + +#define GLK_PW_2_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_C, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_C, \ + POWER_DOMAIN_TRANSCODER_A, \ + POWER_DOMAIN_TRANSCODER_B, \ + POWER_DOMAIN_TRANSCODER_C, \ + POWER_DOMAIN_PORT_DDI_LANES_B, \ + POWER_DOMAIN_PORT_DDI_LANES_C, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_MMIO, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_B, \ + POWER_DOMAIN_AUX_C + +I915_DECL_PW_DOMAINS(glk_pwdoms_pw_2, + GLK_PW_2_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(glk_pwdoms_dc_off, + GLK_PW_2_POWER_DOMAINS, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_GMBUS, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_GT_IRQ, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(glk_pwdoms_ddi_io_a, POWER_DOMAIN_PORT_DDI_IO_A); +I915_DECL_PW_DOMAINS(glk_pwdoms_ddi_io_b, POWER_DOMAIN_PORT_DDI_IO_B); +I915_DECL_PW_DOMAINS(glk_pwdoms_ddi_io_c, POWER_DOMAIN_PORT_DDI_IO_C); + +I915_DECL_PW_DOMAINS(glk_pwdoms_dpio_cmn_a, + POWER_DOMAIN_PORT_DDI_LANES_A, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(glk_pwdoms_dpio_cmn_b, + POWER_DOMAIN_PORT_DDI_LANES_B, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(glk_pwdoms_dpio_cmn_c, + POWER_DOMAIN_PORT_DDI_LANES_C, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(glk_pwdoms_aux_a, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_AUX_IO_A, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(glk_pwdoms_aux_b, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(glk_pwdoms_aux_c, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc glk_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &glk_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_2", &glk_pwdoms_pw_2, + .hsw.idx = SKL_PW_CTL_IDX_PW_2, + .id = SKL_DISP_PW_2), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + .irq_pipe_mask = BIT(PIPE_B) | BIT(PIPE_C), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("dpio-common-a", &glk_pwdoms_dpio_cmn_a, + .bxt.phy = DPIO_PHY1, + .id = BXT_DISP_PW_DPIO_CMN_A), + I915_PW("dpio-common-b", &glk_pwdoms_dpio_cmn_b, + .bxt.phy = DPIO_PHY0, + .id = VLV_DISP_PW_DPIO_CMN_BC), + I915_PW("dpio-common-c", &glk_pwdoms_dpio_cmn_c, + .bxt.phy = DPIO_PHY2, + .id = GLK_DISP_PW_DPIO_CMN_C), + ), + .ops = &bxt_dpio_cmn_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_A", &glk_pwdoms_aux_a, .hsw.idx = GLK_PW_CTL_IDX_AUX_A), + I915_PW("AUX_B", &glk_pwdoms_aux_b, .hsw.idx = GLK_PW_CTL_IDX_AUX_B), + I915_PW("AUX_C", &glk_pwdoms_aux_c, .hsw.idx = GLK_PW_CTL_IDX_AUX_C), + I915_PW("DDI_IO_A", &glk_pwdoms_ddi_io_a, .hsw.idx = GLK_PW_CTL_IDX_DDI_A), + I915_PW("DDI_IO_B", &glk_pwdoms_ddi_io_b, .hsw.idx = SKL_PW_CTL_IDX_DDI_B), + I915_PW("DDI_IO_C", &glk_pwdoms_ddi_io_c, .hsw.idx = SKL_PW_CTL_IDX_DDI_C), + ), + .ops = &hsw_power_well_ops, + }, +}; + +static const struct i915_power_well_desc_list glk_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(skl_power_wells_pw_1), + I915_PW_DESCRIPTORS(glk_power_wells_main), +}; + +/* + * ICL PW_0/PG_0 domains (HW/DMC control): + * - PCI + * - clocks except port PLL + * - central power except FBC + * - shared functions except pipe interrupts, pipe MBUS, DBUF registers + * ICL PW_1/PG_1 domains (HW/DMC control): + * - DBUF function + * - PIPE_A and its planes, except VGA + * - transcoder EDP + PSR + * - transcoder DSI + * - DDI_A + * - FBC + */ +#define ICL_PW_4_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_C, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_C + +I915_DECL_PW_DOMAINS(icl_pwdoms_pw_4, + ICL_PW_4_POWER_DOMAINS, + POWER_DOMAIN_INIT); + /* VDSC/joining */ + +#define ICL_PW_3_POWER_DOMAINS \ + ICL_PW_4_POWER_DOMAINS, \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_TRANSCODER_A, \ + POWER_DOMAIN_TRANSCODER_B, \ + POWER_DOMAIN_TRANSCODER_C, \ + POWER_DOMAIN_PORT_DDI_LANES_B, \ + POWER_DOMAIN_PORT_DDI_LANES_C, \ + POWER_DOMAIN_PORT_DDI_LANES_D, \ + POWER_DOMAIN_PORT_DDI_LANES_E, \ + POWER_DOMAIN_PORT_DDI_LANES_F, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_MMIO, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_B, \ + POWER_DOMAIN_AUX_C, \ + POWER_DOMAIN_AUX_D, \ + POWER_DOMAIN_AUX_E, \ + POWER_DOMAIN_AUX_F, \ + POWER_DOMAIN_AUX_TBT1, \ + POWER_DOMAIN_AUX_TBT2, \ + POWER_DOMAIN_AUX_TBT3, \ + POWER_DOMAIN_AUX_TBT4 + +I915_DECL_PW_DOMAINS(icl_pwdoms_pw_3, + ICL_PW_3_POWER_DOMAINS, + POWER_DOMAIN_INIT); + /* + * - transcoder WD + * - KVMR (HW control) + */ + +#define ICL_PW_2_POWER_DOMAINS \ + ICL_PW_3_POWER_DOMAINS, \ + POWER_DOMAIN_TRANSCODER_VDSC_PW2 + +I915_DECL_PW_DOMAINS(icl_pwdoms_pw_2, + ICL_PW_2_POWER_DOMAINS, + POWER_DOMAIN_INIT); + /* + * - KVMR (HW control) + */ + +I915_DECL_PW_DOMAINS(icl_pwdoms_dc_off, + ICL_PW_2_POWER_DOMAINS, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_DC_OFF, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(icl_pwdoms_ddi_io_d, POWER_DOMAIN_PORT_DDI_IO_D); +I915_DECL_PW_DOMAINS(icl_pwdoms_ddi_io_e, POWER_DOMAIN_PORT_DDI_IO_E); +I915_DECL_PW_DOMAINS(icl_pwdoms_ddi_io_f, POWER_DOMAIN_PORT_DDI_IO_F); + +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_a, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_AUX_IO_A); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_b, POWER_DOMAIN_AUX_B); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_c, POWER_DOMAIN_AUX_C); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_d, POWER_DOMAIN_AUX_D); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_e, POWER_DOMAIN_AUX_E); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_f, POWER_DOMAIN_AUX_F); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_tbt1, POWER_DOMAIN_AUX_TBT1); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_tbt2, POWER_DOMAIN_AUX_TBT2); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_tbt3, POWER_DOMAIN_AUX_TBT3); +I915_DECL_PW_DOMAINS(icl_pwdoms_aux_tbt4, POWER_DOMAIN_AUX_TBT4); + +static const struct i915_power_well_desc icl_power_wells_pw_1[] = { + { + /* Handled by the DMC firmware */ + .instances = &I915_PW_INSTANCES( + I915_PW("PW_1", I915_PW_DOMAINS_NONE, + .hsw.idx = ICL_PW_CTL_IDX_PW_1, + .id = SKL_DISP_PW_1), + ), + .ops = &hsw_power_well_ops, + .always_on = true, + .has_fuses = true, + }, +}; + +static const struct i915_power_well_desc icl_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &icl_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_2", &icl_pwdoms_pw_2, + .hsw.idx = ICL_PW_CTL_IDX_PW_2, + .id = SKL_DISP_PW_2), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_3", &icl_pwdoms_pw_3, + .hsw.idx = ICL_PW_CTL_IDX_PW_3, + .id = ICL_DISP_PW_3), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + .irq_pipe_mask = BIT(PIPE_B), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("DDI_IO_A", &glk_pwdoms_ddi_io_a, .hsw.idx = ICL_PW_CTL_IDX_DDI_A), + I915_PW("DDI_IO_B", &glk_pwdoms_ddi_io_b, .hsw.idx = ICL_PW_CTL_IDX_DDI_B), + I915_PW("DDI_IO_C", &glk_pwdoms_ddi_io_c, .hsw.idx = ICL_PW_CTL_IDX_DDI_C), + I915_PW("DDI_IO_D", &icl_pwdoms_ddi_io_d, .hsw.idx = ICL_PW_CTL_IDX_DDI_D), + I915_PW("DDI_IO_E", &icl_pwdoms_ddi_io_e, .hsw.idx = ICL_PW_CTL_IDX_DDI_E), + I915_PW("DDI_IO_F", &icl_pwdoms_ddi_io_f, .hsw.idx = ICL_PW_CTL_IDX_DDI_F), + ), + .ops = &icl_ddi_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_A", &icl_pwdoms_aux_a, .hsw.idx = ICL_PW_CTL_IDX_AUX_A), + I915_PW("AUX_B", &icl_pwdoms_aux_b, .hsw.idx = ICL_PW_CTL_IDX_AUX_B), + I915_PW("AUX_C", &icl_pwdoms_aux_c, .hsw.idx = ICL_PW_CTL_IDX_AUX_C), + I915_PW("AUX_D", &icl_pwdoms_aux_d, .hsw.idx = ICL_PW_CTL_IDX_AUX_D), + I915_PW("AUX_E", &icl_pwdoms_aux_e, .hsw.idx = ICL_PW_CTL_IDX_AUX_E), + I915_PW("AUX_F", &icl_pwdoms_aux_f, .hsw.idx = ICL_PW_CTL_IDX_AUX_F), + ), + .ops = &icl_aux_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_TBT1", &icl_pwdoms_aux_tbt1, .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT1), + I915_PW("AUX_TBT2", &icl_pwdoms_aux_tbt2, .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT2), + I915_PW("AUX_TBT3", &icl_pwdoms_aux_tbt3, .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT3), + I915_PW("AUX_TBT4", &icl_pwdoms_aux_tbt4, .hsw.idx = ICL_PW_CTL_IDX_AUX_TBT4), + ), + .ops = &icl_aux_power_well_ops, + .is_tc_tbt = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_4", &icl_pwdoms_pw_4, + .hsw.idx = ICL_PW_CTL_IDX_PW_4), + ), + .ops = &hsw_power_well_ops, + .irq_pipe_mask = BIT(PIPE_C), + .has_fuses = true, + }, +}; + +static const struct i915_power_well_desc_list icl_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(icl_power_wells_pw_1), + I915_PW_DESCRIPTORS(icl_power_wells_main), +}; + +#define TGL_PW_5_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_D, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_D, \ + POWER_DOMAIN_TRANSCODER_D + +I915_DECL_PW_DOMAINS(tgl_pwdoms_pw_5, + TGL_PW_5_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +#define TGL_PW_4_POWER_DOMAINS \ + TGL_PW_5_POWER_DOMAINS, \ + POWER_DOMAIN_PIPE_C, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_C, \ + POWER_DOMAIN_TRANSCODER_C + +I915_DECL_PW_DOMAINS(tgl_pwdoms_pw_4, + TGL_PW_4_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +#define TGL_PW_3_POWER_DOMAINS \ + TGL_PW_4_POWER_DOMAINS, \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_TRANSCODER_B, \ + POWER_DOMAIN_PORT_DDI_LANES_TC1, \ + POWER_DOMAIN_PORT_DDI_LANES_TC2, \ + POWER_DOMAIN_PORT_DDI_LANES_TC3, \ + POWER_DOMAIN_PORT_DDI_LANES_TC4, \ + POWER_DOMAIN_PORT_DDI_LANES_TC5, \ + POWER_DOMAIN_PORT_DDI_LANES_TC6, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_MMIO, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_USBC1, \ + POWER_DOMAIN_AUX_USBC2, \ + POWER_DOMAIN_AUX_USBC3, \ + POWER_DOMAIN_AUX_USBC4, \ + POWER_DOMAIN_AUX_USBC5, \ + POWER_DOMAIN_AUX_USBC6, \ + POWER_DOMAIN_AUX_TBT1, \ + POWER_DOMAIN_AUX_TBT2, \ + POWER_DOMAIN_AUX_TBT3, \ + POWER_DOMAIN_AUX_TBT4, \ + POWER_DOMAIN_AUX_TBT5, \ + POWER_DOMAIN_AUX_TBT6 + +I915_DECL_PW_DOMAINS(tgl_pwdoms_pw_3, + TGL_PW_3_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(tgl_pwdoms_pw_2, + TGL_PW_3_POWER_DOMAINS, + POWER_DOMAIN_TRANSCODER_VDSC_PW2, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(tgl_pwdoms_dc_off, + TGL_PW_3_POWER_DOMAINS, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_AUX_C, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(tgl_pwdoms_ddi_io_tc1, POWER_DOMAIN_PORT_DDI_IO_TC1); +I915_DECL_PW_DOMAINS(tgl_pwdoms_ddi_io_tc2, POWER_DOMAIN_PORT_DDI_IO_TC2); +I915_DECL_PW_DOMAINS(tgl_pwdoms_ddi_io_tc3, POWER_DOMAIN_PORT_DDI_IO_TC3); +I915_DECL_PW_DOMAINS(tgl_pwdoms_ddi_io_tc4, POWER_DOMAIN_PORT_DDI_IO_TC4); +I915_DECL_PW_DOMAINS(tgl_pwdoms_ddi_io_tc5, POWER_DOMAIN_PORT_DDI_IO_TC5); +I915_DECL_PW_DOMAINS(tgl_pwdoms_ddi_io_tc6, POWER_DOMAIN_PORT_DDI_IO_TC6); + +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_usbc1, POWER_DOMAIN_AUX_USBC1); +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_usbc2, POWER_DOMAIN_AUX_USBC2); +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_usbc3, POWER_DOMAIN_AUX_USBC3); +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_usbc4, POWER_DOMAIN_AUX_USBC4); +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_usbc5, POWER_DOMAIN_AUX_USBC5); +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_usbc6, POWER_DOMAIN_AUX_USBC6); + +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_tbt5, POWER_DOMAIN_AUX_TBT5); +I915_DECL_PW_DOMAINS(tgl_pwdoms_aux_tbt6, POWER_DOMAIN_AUX_TBT6); + +I915_DECL_PW_DOMAINS(tgl_pwdoms_tc_cold_off, + POWER_DOMAIN_AUX_USBC1, + POWER_DOMAIN_AUX_USBC2, + POWER_DOMAIN_AUX_USBC3, + POWER_DOMAIN_AUX_USBC4, + POWER_DOMAIN_AUX_USBC5, + POWER_DOMAIN_AUX_USBC6, + POWER_DOMAIN_AUX_TBT1, + POWER_DOMAIN_AUX_TBT2, + POWER_DOMAIN_AUX_TBT3, + POWER_DOMAIN_AUX_TBT4, + POWER_DOMAIN_AUX_TBT5, + POWER_DOMAIN_AUX_TBT6, + POWER_DOMAIN_TC_COLD_OFF); + +static const struct i915_power_well_desc tgl_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &tgl_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_2", &tgl_pwdoms_pw_2, + .hsw.idx = ICL_PW_CTL_IDX_PW_2, + .id = SKL_DISP_PW_2), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_3", &tgl_pwdoms_pw_3, + .hsw.idx = ICL_PW_CTL_IDX_PW_3, + .id = ICL_DISP_PW_3), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + .irq_pipe_mask = BIT(PIPE_B), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("DDI_IO_A", &glk_pwdoms_ddi_io_a, .hsw.idx = ICL_PW_CTL_IDX_DDI_A), + I915_PW("DDI_IO_B", &glk_pwdoms_ddi_io_b, .hsw.idx = ICL_PW_CTL_IDX_DDI_B), + I915_PW("DDI_IO_C", &glk_pwdoms_ddi_io_c, .hsw.idx = ICL_PW_CTL_IDX_DDI_C), + I915_PW("DDI_IO_TC1", &tgl_pwdoms_ddi_io_tc1, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1), + I915_PW("DDI_IO_TC2", &tgl_pwdoms_ddi_io_tc2, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2), + I915_PW("DDI_IO_TC3", &tgl_pwdoms_ddi_io_tc3, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC3), + I915_PW("DDI_IO_TC4", &tgl_pwdoms_ddi_io_tc4, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC4), + I915_PW("DDI_IO_TC5", &tgl_pwdoms_ddi_io_tc5, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC5), + I915_PW("DDI_IO_TC6", &tgl_pwdoms_ddi_io_tc6, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC6), + ), + .ops = &icl_ddi_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_4", &tgl_pwdoms_pw_4, + .hsw.idx = ICL_PW_CTL_IDX_PW_4), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + .irq_pipe_mask = BIT(PIPE_C), + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_5", &tgl_pwdoms_pw_5, + .hsw.idx = TGL_PW_CTL_IDX_PW_5), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + .irq_pipe_mask = BIT(PIPE_D), + }, +}; + +static const struct i915_power_well_desc tgl_power_wells_tc_cold_off[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("TC_cold_off", &tgl_pwdoms_tc_cold_off, + .id = TGL_DISP_PW_TC_COLD_OFF), + ), + .ops = &tgl_tc_cold_off_ops, + }, +}; + +static const struct i915_power_well_desc tgl_power_wells_aux[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_A", &icl_pwdoms_aux_a, .hsw.idx = ICL_PW_CTL_IDX_AUX_A), + I915_PW("AUX_B", &icl_pwdoms_aux_b, .hsw.idx = ICL_PW_CTL_IDX_AUX_B), + I915_PW("AUX_C", &icl_pwdoms_aux_c, .hsw.idx = ICL_PW_CTL_IDX_AUX_C), + I915_PW("AUX_USBC1", &tgl_pwdoms_aux_usbc1, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1), + I915_PW("AUX_USBC2", &tgl_pwdoms_aux_usbc2, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2), + I915_PW("AUX_USBC3", &tgl_pwdoms_aux_usbc3, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC3), + I915_PW("AUX_USBC4", &tgl_pwdoms_aux_usbc4, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC4), + I915_PW("AUX_USBC5", &tgl_pwdoms_aux_usbc5, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC5), + I915_PW("AUX_USBC6", &tgl_pwdoms_aux_usbc6, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC6), + ), + .ops = &icl_aux_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_TBT1", &icl_pwdoms_aux_tbt1, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT1), + I915_PW("AUX_TBT2", &icl_pwdoms_aux_tbt2, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT2), + I915_PW("AUX_TBT3", &icl_pwdoms_aux_tbt3, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT3), + I915_PW("AUX_TBT4", &icl_pwdoms_aux_tbt4, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT4), + I915_PW("AUX_TBT5", &tgl_pwdoms_aux_tbt5, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT5), + I915_PW("AUX_TBT6", &tgl_pwdoms_aux_tbt6, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT6), + ), + .ops = &icl_aux_power_well_ops, + .is_tc_tbt = true, + }, +}; + +static const struct i915_power_well_desc_list tgl_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(icl_power_wells_pw_1), + I915_PW_DESCRIPTORS(tgl_power_wells_main), + I915_PW_DESCRIPTORS(tgl_power_wells_tc_cold_off), + I915_PW_DESCRIPTORS(tgl_power_wells_aux), +}; + +static const struct i915_power_well_desc_list adls_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(icl_power_wells_pw_1), + I915_PW_DESCRIPTORS(tgl_power_wells_main), + I915_PW_DESCRIPTORS(tgl_power_wells_aux), +}; + +#define RKL_PW_4_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_C, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_C, \ + POWER_DOMAIN_TRANSCODER_C + +I915_DECL_PW_DOMAINS(rkl_pwdoms_pw_4, + RKL_PW_4_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +#define RKL_PW_3_POWER_DOMAINS \ + RKL_PW_4_POWER_DOMAINS, \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_TRANSCODER_B, \ + POWER_DOMAIN_PORT_DDI_LANES_TC1, \ + POWER_DOMAIN_PORT_DDI_LANES_TC2, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_MMIO, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_USBC1, \ + POWER_DOMAIN_AUX_USBC2 + +I915_DECL_PW_DOMAINS(rkl_pwdoms_pw_3, + RKL_PW_3_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +/* + * There is no PW_2/PG_2 on RKL. + * + * RKL PW_1/PG_1 domains (under HW/DMC control): + * - DBUF function (note: registers are in PW0) + * - PIPE_A and its planes and VDSC/joining, except VGA + * - transcoder A + * - DDI_A and DDI_B + * - FBC + * + * RKL PW_0/PG_0 domains (under HW/DMC control): + * - PCI + * - clocks except port PLL + * - shared functions: + * * interrupts except pipe interrupts + * * MBus except PIPE_MBUS_DBOX_CTL + * * DBUF registers + * - central power except FBC + * - top-level GTC (DDI-level GTC is in the well associated with the DDI) + */ + +I915_DECL_PW_DOMAINS(rkl_pwdoms_dc_off, + RKL_PW_3_POWER_DOMAINS, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc rkl_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &rkl_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_3", &rkl_pwdoms_pw_3, + .hsw.idx = ICL_PW_CTL_IDX_PW_3, + .id = ICL_DISP_PW_3), + ), + .ops = &hsw_power_well_ops, + .irq_pipe_mask = BIT(PIPE_B), + .has_vga = true, + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_4", &rkl_pwdoms_pw_4, + .hsw.idx = ICL_PW_CTL_IDX_PW_4), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + .irq_pipe_mask = BIT(PIPE_C), + }, +}; + +static const struct i915_power_well_desc rkl_power_wells_ddi_aux[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DDI_IO_A", &glk_pwdoms_ddi_io_a, .hsw.idx = ICL_PW_CTL_IDX_DDI_A), + I915_PW("DDI_IO_B", &glk_pwdoms_ddi_io_b, .hsw.idx = ICL_PW_CTL_IDX_DDI_B), + I915_PW("DDI_IO_TC1", &tgl_pwdoms_ddi_io_tc1, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1), + I915_PW("DDI_IO_TC2", &tgl_pwdoms_ddi_io_tc2, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2), + ), + .ops = &icl_ddi_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_A", &icl_pwdoms_aux_a, .hsw.idx = ICL_PW_CTL_IDX_AUX_A), + I915_PW("AUX_B", &icl_pwdoms_aux_b, .hsw.idx = ICL_PW_CTL_IDX_AUX_B), + I915_PW("AUX_USBC1", &tgl_pwdoms_aux_usbc1, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1), + I915_PW("AUX_USBC2", &tgl_pwdoms_aux_usbc2, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2), + ), + .ops = &icl_aux_power_well_ops, + }, +}; + +static const struct i915_power_well_desc_list rkl_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(icl_power_wells_pw_1), + I915_PW_DESCRIPTORS(rkl_power_wells_main), + I915_PW_DESCRIPTORS(rkl_power_wells_ddi_aux), +}; + +/* + * DG1 onwards Audio MMIO/VERBS lies in PG0 power well. + */ +#define DG1_PW_3_POWER_DOMAINS \ + TGL_PW_4_POWER_DOMAINS, \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_TRANSCODER_B, \ + POWER_DOMAIN_PORT_DDI_LANES_TC1, \ + POWER_DOMAIN_PORT_DDI_LANES_TC2, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_USBC1, \ + POWER_DOMAIN_AUX_USBC2 + +I915_DECL_PW_DOMAINS(dg1_pwdoms_pw_3, + DG1_PW_3_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(dg1_pwdoms_dc_off, + DG1_PW_3_POWER_DOMAINS, + POWER_DOMAIN_AUDIO_MMIO, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(dg1_pwdoms_pw_2, + DG1_PW_3_POWER_DOMAINS, + POWER_DOMAIN_TRANSCODER_VDSC_PW2, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc dg1_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &dg1_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_2", &dg1_pwdoms_pw_2, + .hsw.idx = ICL_PW_CTL_IDX_PW_2, + .id = SKL_DISP_PW_2), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_3", &dg1_pwdoms_pw_3, + .hsw.idx = ICL_PW_CTL_IDX_PW_3, + .id = ICL_DISP_PW_3), + ), + .ops = &hsw_power_well_ops, + .irq_pipe_mask = BIT(PIPE_B), + .has_vga = true, + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_4", &tgl_pwdoms_pw_4, + .hsw.idx = ICL_PW_CTL_IDX_PW_4), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + .irq_pipe_mask = BIT(PIPE_C), + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_5", &tgl_pwdoms_pw_5, + .hsw.idx = TGL_PW_CTL_IDX_PW_5), + ), + .ops = &hsw_power_well_ops, + .has_fuses = true, + .irq_pipe_mask = BIT(PIPE_D), + }, +}; + +static const struct i915_power_well_desc_list dg1_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(icl_power_wells_pw_1), + I915_PW_DESCRIPTORS(dg1_power_wells_main), + I915_PW_DESCRIPTORS(rkl_power_wells_ddi_aux), +}; + +/* + * XE_LPD Power Domains + * + * Previous platforms required that PG(n-1) be enabled before PG(n). That + * dependency chain turns into a dependency tree on XE_LPD: + * + * PG0 + * | + * --PG1-- + * / \ + * PGA --PG2-- + * / | \ + * PGB PGC PGD + * + * Power wells must be enabled from top to bottom and disabled from bottom + * to top. This allows pipes to be power gated independently. + */ + +#define XELPD_PW_D_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_D, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_D, \ + POWER_DOMAIN_TRANSCODER_D + +I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_d, + XELPD_PW_D_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +#define XELPD_PW_C_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_C, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_C, \ + POWER_DOMAIN_TRANSCODER_C + +I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_c, + XELPD_PW_C_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +#define XELPD_PW_B_POWER_DOMAINS \ + POWER_DOMAIN_PIPE_B, \ + POWER_DOMAIN_PIPE_PANEL_FITTER_B, \ + POWER_DOMAIN_TRANSCODER_B + +I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_b, + XELPD_PW_B_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_a, + POWER_DOMAIN_PIPE_A, + POWER_DOMAIN_PIPE_PANEL_FITTER_A, + POWER_DOMAIN_INIT); + +#define XELPD_PW_2_POWER_DOMAINS \ + XELPD_PW_B_POWER_DOMAINS, \ + XELPD_PW_C_POWER_DOMAINS, \ + XELPD_PW_D_POWER_DOMAINS, \ + POWER_DOMAIN_PORT_DDI_LANES_C, \ + POWER_DOMAIN_PORT_DDI_LANES_D, \ + POWER_DOMAIN_PORT_DDI_LANES_E, \ + POWER_DOMAIN_PORT_DDI_LANES_TC1, \ + POWER_DOMAIN_PORT_DDI_LANES_TC2, \ + POWER_DOMAIN_PORT_DDI_LANES_TC3, \ + POWER_DOMAIN_PORT_DDI_LANES_TC4, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_C, \ + POWER_DOMAIN_AUX_D, \ + POWER_DOMAIN_AUX_E, \ + POWER_DOMAIN_AUX_USBC1, \ + POWER_DOMAIN_AUX_USBC2, \ + POWER_DOMAIN_AUX_USBC3, \ + POWER_DOMAIN_AUX_USBC4, \ + POWER_DOMAIN_AUX_TBT1, \ + POWER_DOMAIN_AUX_TBT2, \ + POWER_DOMAIN_AUX_TBT3, \ + POWER_DOMAIN_AUX_TBT4 + +I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_2, + XELPD_PW_2_POWER_DOMAINS, + POWER_DOMAIN_INIT); + +/* + * XELPD PW_1/PG_1 domains (under HW/DMC control): + * - DBUF function (registers are in PW0) + * - Transcoder A + * - DDI_A and DDI_B + * + * XELPD PW_0/PW_1 domains (under HW/DMC control): + * - PCI + * - Clocks except port PLL + * - Shared functions: + * * interrupts except pipe interrupts + * * MBus except PIPE_MBUS_DBOX_CTL + * * DBUF registers + * - Central power except FBC + * - Top-level GTC (DDI-level GTC is in the well associated with the DDI) + */ + +I915_DECL_PW_DOMAINS(xelpd_pwdoms_dc_off, + XELPD_PW_2_POWER_DOMAINS, + POWER_DOMAIN_PORT_DSI, + POWER_DOMAIN_AUDIO_MMIO, + POWER_DOMAIN_AUX_A, + POWER_DOMAIN_AUX_B, + POWER_DOMAIN_MODESET, + POWER_DOMAIN_INIT); + +static const struct i915_power_well_desc xelpd_power_wells_main[] = { + { + .instances = &I915_PW_INSTANCES( + I915_PW("DC_off", &xelpd_pwdoms_dc_off, + .id = SKL_DISP_DC_OFF), + ), + .ops = &gen9_dc_off_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_2", &xelpd_pwdoms_pw_2, + .hsw.idx = ICL_PW_CTL_IDX_PW_2, + .id = SKL_DISP_PW_2), + ), + .ops = &hsw_power_well_ops, + .has_vga = true, + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_A", &xelpd_pwdoms_pw_a, + .hsw.idx = XELPD_PW_CTL_IDX_PW_A), + ), + .ops = &hsw_power_well_ops, + .irq_pipe_mask = BIT(PIPE_A), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_B", &xelpd_pwdoms_pw_b, + .hsw.idx = XELPD_PW_CTL_IDX_PW_B), + ), + .ops = &hsw_power_well_ops, + .irq_pipe_mask = BIT(PIPE_B), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_C", &xelpd_pwdoms_pw_c, + .hsw.idx = XELPD_PW_CTL_IDX_PW_C), + ), + .ops = &hsw_power_well_ops, + .irq_pipe_mask = BIT(PIPE_C), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("PW_D", &xelpd_pwdoms_pw_d, + .hsw.idx = XELPD_PW_CTL_IDX_PW_D), + ), + .ops = &hsw_power_well_ops, + .irq_pipe_mask = BIT(PIPE_D), + .has_fuses = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("DDI_IO_A", &glk_pwdoms_ddi_io_a, .hsw.idx = ICL_PW_CTL_IDX_DDI_A), + I915_PW("DDI_IO_B", &glk_pwdoms_ddi_io_b, .hsw.idx = ICL_PW_CTL_IDX_DDI_B), + I915_PW("DDI_IO_C", &glk_pwdoms_ddi_io_c, .hsw.idx = ICL_PW_CTL_IDX_DDI_C), + I915_PW("DDI_IO_D", &icl_pwdoms_ddi_io_d, .hsw.idx = XELPD_PW_CTL_IDX_DDI_D), + I915_PW("DDI_IO_E", &icl_pwdoms_ddi_io_e, .hsw.idx = XELPD_PW_CTL_IDX_DDI_E), + I915_PW("DDI_IO_TC1", &tgl_pwdoms_ddi_io_tc1, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1), + I915_PW("DDI_IO_TC2", &tgl_pwdoms_ddi_io_tc2, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2), + I915_PW("DDI_IO_TC3", &tgl_pwdoms_ddi_io_tc3, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC3), + I915_PW("DDI_IO_TC4", &tgl_pwdoms_ddi_io_tc4, .hsw.idx = TGL_PW_CTL_IDX_DDI_TC4), + ), + .ops = &icl_ddi_power_well_ops, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_A", &icl_pwdoms_aux_a, .hsw.idx = ICL_PW_CTL_IDX_AUX_A), + I915_PW("AUX_B", &icl_pwdoms_aux_b, .hsw.idx = ICL_PW_CTL_IDX_AUX_B), + I915_PW("AUX_C", &icl_pwdoms_aux_c, .hsw.idx = ICL_PW_CTL_IDX_AUX_C), + I915_PW("AUX_D", &icl_pwdoms_aux_d, .hsw.idx = XELPD_PW_CTL_IDX_AUX_D), + I915_PW("AUX_E", &icl_pwdoms_aux_e, .hsw.idx = XELPD_PW_CTL_IDX_AUX_E), + I915_PW("AUX_USBC1", &tgl_pwdoms_aux_usbc1, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1), + I915_PW("AUX_USBC2", &tgl_pwdoms_aux_usbc2, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2), + I915_PW("AUX_USBC3", &tgl_pwdoms_aux_usbc3, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC3), + I915_PW("AUX_USBC4", &tgl_pwdoms_aux_usbc4, .hsw.idx = TGL_PW_CTL_IDX_AUX_TC4), + ), + .ops = &icl_aux_power_well_ops, + .fixed_enable_delay = true, + }, { + .instances = &I915_PW_INSTANCES( + I915_PW("AUX_TBT1", &icl_pwdoms_aux_tbt1, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT1), + I915_PW("AUX_TBT2", &icl_pwdoms_aux_tbt2, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT2), + I915_PW("AUX_TBT3", &icl_pwdoms_aux_tbt3, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT3), + I915_PW("AUX_TBT4", &icl_pwdoms_aux_tbt4, .hsw.idx = TGL_PW_CTL_IDX_AUX_TBT4), + ), + .ops = &icl_aux_power_well_ops, + .is_tc_tbt = true, + }, +}; + +static const struct i915_power_well_desc_list xelpd_power_wells[] = { + I915_PW_DESCRIPTORS(i9xx_power_wells_always_on), + I915_PW_DESCRIPTORS(icl_power_wells_pw_1), + I915_PW_DESCRIPTORS(xelpd_power_wells_main), +}; + +static void init_power_well_domains(const struct i915_power_well_instance *inst, + struct i915_power_well *power_well) +{ + int j; + + if (!inst->domain_list) + return; + + if (inst->domain_list->count == 0) { + bitmap_fill(power_well->domains.bits, POWER_DOMAIN_NUM); + + return; + } + + for (j = 0; j < inst->domain_list->count; j++) + set_bit(inst->domain_list->list[j], power_well->domains.bits); +} + +#define for_each_power_well_instance_in_desc_list(_desc_list, _desc_count, _desc, _inst) \ + for ((_desc) = (_desc_list); (_desc) - (_desc_list) < (_desc_count); (_desc)++) \ + for ((_inst) = (_desc)->instances->list; \ + (_inst) - (_desc)->instances->list < (_desc)->instances->count; \ + (_inst)++) + +#define for_each_power_well_instance(_desc_list, _desc_count, _descs, _desc, _inst) \ + for ((_descs) = (_desc_list); \ + (_descs) - (_desc_list) < (_desc_count); \ + (_descs)++) \ + for_each_power_well_instance_in_desc_list((_descs)->list, (_descs)->count, \ + (_desc), (_inst)) + +static int +__set_power_wells(struct i915_power_domains *power_domains, + const struct i915_power_well_desc_list *power_well_descs, + int power_well_descs_sz) +{ + struct drm_i915_private *i915 = container_of(power_domains, + struct drm_i915_private, + power_domains); + u64 power_well_ids = 0; + const struct i915_power_well_desc_list *desc_list; + const struct i915_power_well_desc *desc; + const struct i915_power_well_instance *inst; + int power_well_count = 0; + int plt_idx = 0; + + for_each_power_well_instance(power_well_descs, power_well_descs_sz, desc_list, desc, inst) + power_well_count++; + + power_domains->power_well_count = power_well_count; + power_domains->power_wells = + kcalloc(power_well_count, + sizeof(*power_domains->power_wells), + GFP_KERNEL); + if (!power_domains->power_wells) + return -ENOMEM; + + for_each_power_well_instance(power_well_descs, power_well_descs_sz, desc_list, desc, inst) { + struct i915_power_well *pw = &power_domains->power_wells[plt_idx]; + enum i915_power_well_id id = inst->id; + + pw->desc = desc; + drm_WARN_ON(&i915->drm, + overflows_type(inst - desc->instances->list, pw->instance_idx)); + pw->instance_idx = inst - desc->instances->list; + + init_power_well_domains(inst, pw); + + plt_idx++; + + if (id == DISP_PW_ID_NONE) + continue; + + drm_WARN_ON(&i915->drm, id >= sizeof(power_well_ids) * 8); + drm_WARN_ON(&i915->drm, power_well_ids & BIT_ULL(id)); + power_well_ids |= BIT_ULL(id); + } + + return 0; +} + +#define set_power_wells(power_domains, __power_well_descs) \ + __set_power_wells(power_domains, __power_well_descs, \ + ARRAY_SIZE(__power_well_descs)) + +/** + * intel_display_power_map_init - initialize power domain -> power well mappings + * @power_domains: power domain state + * + * Creates all the power wells for the current platform, initializes the + * dynamic state for them and initializes the mapping of each power well to + * all the power domains the power well belongs to. + */ +int intel_display_power_map_init(struct i915_power_domains *power_domains) +{ + struct drm_i915_private *i915 = container_of(power_domains, + struct drm_i915_private, + power_domains); + /* + * The enabling order will be from lower to higher indexed wells, + * the disabling order is reversed. + */ + if (!HAS_DISPLAY(i915)) { + power_domains->power_well_count = 0; + return 0; + } + + if (DISPLAY_VER(i915) >= 13) + return set_power_wells(power_domains, xelpd_power_wells); + else if (IS_DG1(i915)) + return set_power_wells(power_domains, dg1_power_wells); + else if (IS_ALDERLAKE_S(i915)) + return set_power_wells(power_domains, adls_power_wells); + else if (IS_ROCKETLAKE(i915)) + return set_power_wells(power_domains, rkl_power_wells); + else if (DISPLAY_VER(i915) == 12) + return set_power_wells(power_domains, tgl_power_wells); + else if (DISPLAY_VER(i915) == 11) + return set_power_wells(power_domains, icl_power_wells); + else if (IS_GEMINILAKE(i915)) + return set_power_wells(power_domains, glk_power_wells); + else if (IS_BROXTON(i915)) + return set_power_wells(power_domains, bxt_power_wells); + else if (DISPLAY_VER(i915) == 9) + return set_power_wells(power_domains, skl_power_wells); + else if (IS_CHERRYVIEW(i915)) + return set_power_wells(power_domains, chv_power_wells); + else if (IS_BROADWELL(i915)) + return set_power_wells(power_domains, bdw_power_wells); + else if (IS_HASWELL(i915)) + return set_power_wells(power_domains, hsw_power_wells); + else if (IS_VALLEYVIEW(i915)) + return set_power_wells(power_domains, vlv_power_wells); + else if (IS_I830(i915)) + return set_power_wells(power_domains, i830_power_wells); + else + return set_power_wells(power_domains, i9xx_power_wells); +} + +/** + * intel_display_power_map_cleanup - clean up power domain -> power well mappings + * @power_domains: power domain state + * + * Cleans up all the state that was initialized by intel_display_power_map_init(). + */ +void intel_display_power_map_cleanup(struct i915_power_domains *power_domains) +{ + kfree(power_domains->power_wells); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_power_map.h b/drivers/gpu/drm/i915/display/intel_display_power_map.h new file mode 100644 index 000000000000..da8f7055a44c --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_power_map.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2022 Intel Corporation + */ + +#ifndef __INTEL_DISPLAY_POWER_MAP_H__ +#define __INTEL_DISPLAY_POWER_MAP_H__ + +struct i915_power_domains; + +int intel_display_power_map_init(struct i915_power_domains *power_domains); +void intel_display_power_map_cleanup(struct i915_power_domains *power_domains); + +#endif diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c new file mode 100644 index 000000000000..5be18eb94042 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -0,0 +1,1912 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2022 Intel Corporation + */ + +#include "i915_drv.h" +#include "i915_irq.h" +#include "intel_combo_phy.h" +#include "intel_combo_phy_regs.h" +#include "intel_crt.h" +#include "intel_de.h" +#include "intel_display_power_well.h" +#include "intel_display_types.h" +#include "intel_dmc.h" +#include "intel_dpio_phy.h" +#include "intel_dpll.h" +#include "intel_hotplug.h" +#include "intel_pcode.h" +#include "intel_pm.h" +#include "intel_pps.h" +#include "intel_tc.h" +#include "intel_vga.h" +#include "vlv_sideband.h" +#include "vlv_sideband_reg.h" + +struct i915_power_well_regs { + i915_reg_t bios; + i915_reg_t driver; + i915_reg_t kvmr; + i915_reg_t debug; +}; + +struct i915_power_well_ops { + const struct i915_power_well_regs *regs; + /* + * Synchronize the well's hw state to match the current sw state, for + * example enable/disable it based on the current refcount. Called + * during driver init and resume time, possibly after first calling + * the enable/disable handlers. + */ + void (*sync_hw)(struct drm_i915_private *i915, + struct i915_power_well *power_well); + /* + * Enable the well and resources that depend on it (for example + * interrupts located on the well). Called after the 0->1 refcount + * transition. + */ + void (*enable)(struct drm_i915_private *i915, + struct i915_power_well *power_well); + /* + * Disable the well and resources that depend on it. Called after + * the 1->0 refcount transition. + */ + void (*disable)(struct drm_i915_private *i915, + struct i915_power_well *power_well); + /* Returns the hw enabled state. */ + bool (*is_enabled)(struct drm_i915_private *i915, + struct i915_power_well *power_well); +}; + +static const struct i915_power_well_instance * +i915_power_well_instance(const struct i915_power_well *power_well) +{ + return &power_well->desc->instances->list[power_well->instance_idx]; +} + +struct i915_power_well * +lookup_power_well(struct drm_i915_private *i915, + enum i915_power_well_id power_well_id) +{ + struct i915_power_well *power_well; + + for_each_power_well(i915, power_well) + if (i915_power_well_instance(power_well)->id == power_well_id) + return power_well; + + /* + * It's not feasible to add error checking code to the callers since + * this condition really shouldn't happen and it doesn't even make sense + * to abort things like display initialization sequences. Just return + * the first power well and hope the WARN gets reported so we can fix + * our driver. + */ + drm_WARN(&i915->drm, 1, + "Power well %d not defined for this platform\n", + power_well_id); + return &i915->power_domains.power_wells[0]; +} + +void intel_power_well_enable(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + drm_dbg_kms(&i915->drm, "enabling %s\n", intel_power_well_name(power_well)); + power_well->desc->ops->enable(i915, power_well); + power_well->hw_enabled = true; +} + +void intel_power_well_disable(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + drm_dbg_kms(&i915->drm, "disabling %s\n", intel_power_well_name(power_well)); + power_well->hw_enabled = false; + power_well->desc->ops->disable(i915, power_well); +} + +void intel_power_well_sync_hw(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + power_well->desc->ops->sync_hw(i915, power_well); + power_well->hw_enabled = + power_well->desc->ops->is_enabled(i915, power_well); +} + +void intel_power_well_get(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + if (!power_well->count++) + intel_power_well_enable(i915, power_well); +} + +void intel_power_well_put(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + drm_WARN(&i915->drm, !power_well->count, + "Use count on power well %s is already zero", + i915_power_well_instance(power_well)->name); + + if (!--power_well->count) + intel_power_well_disable(i915, power_well); +} + +bool intel_power_well_is_enabled(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + return power_well->desc->ops->is_enabled(i915, power_well); +} + +bool intel_power_well_is_enabled_cached(struct i915_power_well *power_well) +{ + return power_well->hw_enabled; +} + +bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, + enum i915_power_well_id power_well_id) +{ + struct i915_power_well *power_well; + + power_well = lookup_power_well(dev_priv, power_well_id); + + return intel_power_well_is_enabled(dev_priv, power_well); +} + +bool intel_power_well_is_always_on(struct i915_power_well *power_well) +{ + return power_well->desc->always_on; +} + +const char *intel_power_well_name(struct i915_power_well *power_well) +{ + return i915_power_well_instance(power_well)->name; +} + +struct intel_power_domain_mask *intel_power_well_domains(struct i915_power_well *power_well) +{ + return &power_well->domains; +} + +int intel_power_well_refcount(struct i915_power_well *power_well) +{ + return power_well->count; +} + +/* + * Starting with Haswell, we have a "Power Down Well" that can be turned off + * when not needed anymore. We have 4 registers that can request the power well + * to be enabled, and it will only be disabled if none of the registers is + * requesting it to be enabled. + */ +static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv, + u8 irq_pipe_mask, bool has_vga) +{ + if (has_vga) + intel_vga_reset_io_mem(dev_priv); + + if (irq_pipe_mask) + gen8_irq_power_well_post_enable(dev_priv, irq_pipe_mask); +} + +static void hsw_power_well_pre_disable(struct drm_i915_private *dev_priv, + u8 irq_pipe_mask) +{ + if (irq_pipe_mask) + gen8_irq_power_well_pre_disable(dev_priv, irq_pipe_mask); +} + +#define ICL_AUX_PW_TO_CH(pw_idx) \ + ((pw_idx) - ICL_PW_CTL_IDX_AUX_A + AUX_CH_A) + +#define ICL_TBT_AUX_PW_TO_CH(pw_idx) \ + ((pw_idx) - ICL_PW_CTL_IDX_AUX_TBT1 + AUX_CH_C) + +static enum aux_ch icl_aux_pw_to_ch(const struct i915_power_well *power_well) +{ + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + + return power_well->desc->is_tc_tbt ? ICL_TBT_AUX_PW_TO_CH(pw_idx) : + ICL_AUX_PW_TO_CH(pw_idx); +} + +static struct intel_digital_port * +aux_ch_to_digital_port(struct drm_i915_private *dev_priv, + enum aux_ch aux_ch) +{ + struct intel_digital_port *dig_port = NULL; + struct intel_encoder *encoder; + + for_each_intel_encoder(&dev_priv->drm, encoder) { + /* We'll check the MST primary port */ + if (encoder->type == INTEL_OUTPUT_DP_MST) + continue; + + dig_port = enc_to_dig_port(encoder); + if (!dig_port) + continue; + + if (dig_port->aux_ch != aux_ch) { + dig_port = NULL; + continue; + } + + break; + } + + return dig_port; +} + +static enum phy icl_aux_pw_to_phy(struct drm_i915_private *i915, + const struct i915_power_well *power_well) +{ + enum aux_ch aux_ch = icl_aux_pw_to_ch(power_well); + struct intel_digital_port *dig_port = aux_ch_to_digital_port(i915, aux_ch); + + return intel_port_to_phy(i915, dig_port->base.port); +} + +static void hsw_wait_for_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, + bool timeout_expected) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + + /* + * For some power wells we're not supposed to watch the status bit for + * an ack, but rather just wait a fixed amount of time and then + * proceed. This is only used on DG2. + */ + if (IS_DG2(dev_priv) && power_well->desc->fixed_enable_delay) { + usleep_range(600, 1200); + return; + } + + /* Timeout for PW1:10 us, AUX:not specified, other PWs:20 us. */ + if (intel_de_wait_for_set(dev_priv, regs->driver, + HSW_PWR_WELL_CTL_STATE(pw_idx), 1)) { + drm_dbg_kms(&dev_priv->drm, "%s power well enable timeout\n", + intel_power_well_name(power_well)); + + drm_WARN_ON(&dev_priv->drm, !timeout_expected); + + } +} + +static u32 hsw_power_well_requesters(struct drm_i915_private *dev_priv, + const struct i915_power_well_regs *regs, + int pw_idx) +{ + u32 req_mask = HSW_PWR_WELL_CTL_REQ(pw_idx); + u32 ret; + + ret = intel_de_read(dev_priv, regs->bios) & req_mask ? 1 : 0; + ret |= intel_de_read(dev_priv, regs->driver) & req_mask ? 2 : 0; + if (regs->kvmr.reg) + ret |= intel_de_read(dev_priv, regs->kvmr) & req_mask ? 4 : 0; + ret |= intel_de_read(dev_priv, regs->debug) & req_mask ? 8 : 0; + + return ret; +} + +static void hsw_wait_for_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + bool disabled; + u32 reqs; + + /* + * Bspec doesn't require waiting for PWs to get disabled, but still do + * this for paranoia. The known cases where a PW will be forced on: + * - a KVMR request on any power well via the KVMR request register + * - a DMC request on PW1 and MISC_IO power wells via the BIOS and + * DEBUG request registers + * Skip the wait in case any of the request bits are set and print a + * diagnostic message. + */ + wait_for((disabled = !(intel_de_read(dev_priv, regs->driver) & + HSW_PWR_WELL_CTL_STATE(pw_idx))) || + (reqs = hsw_power_well_requesters(dev_priv, regs, pw_idx)), 1); + if (disabled) + return; + + drm_dbg_kms(&dev_priv->drm, + "%s forced on (bios:%d driver:%d kvmr:%d debug:%d)\n", + intel_power_well_name(power_well), + !!(reqs & 1), !!(reqs & 2), !!(reqs & 4), !!(reqs & 8)); +} + +static void gen9_wait_for_power_well_fuses(struct drm_i915_private *dev_priv, + enum skl_power_gate pg) +{ + /* Timeout 5us for PG#0, for other PGs 1us */ + drm_WARN_ON(&dev_priv->drm, + intel_de_wait_for_set(dev_priv, SKL_FUSE_STATUS, + SKL_FUSE_PG_DIST_STATUS(pg), 1)); +} + +static void hsw_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + u32 val; + + if (power_well->desc->has_fuses) { + enum skl_power_gate pg; + + pg = DISPLAY_VER(dev_priv) >= 11 ? ICL_PW_CTL_IDX_TO_PG(pw_idx) : + SKL_PW_CTL_IDX_TO_PG(pw_idx); + + /* Wa_16013190616:adlp */ + if (IS_ALDERLAKE_P(dev_priv) && pg == SKL_PG1) + intel_de_rmw(dev_priv, GEN8_CHICKEN_DCPR_1, 0, DISABLE_FLR_SRC); + + /* + * For PW1 we have to wait both for the PW0/PG0 fuse state + * before enabling the power well and PW1/PG1's own fuse + * state after the enabling. For all other power wells with + * fuses we only have to wait for that PW/PG's fuse state + * after the enabling. + */ + if (pg == SKL_PG1) + gen9_wait_for_power_well_fuses(dev_priv, SKL_PG0); + } + + val = intel_de_read(dev_priv, regs->driver); + intel_de_write(dev_priv, regs->driver, + val | HSW_PWR_WELL_CTL_REQ(pw_idx)); + + hsw_wait_for_power_well_enable(dev_priv, power_well, false); + + if (power_well->desc->has_fuses) { + enum skl_power_gate pg; + + pg = DISPLAY_VER(dev_priv) >= 11 ? ICL_PW_CTL_IDX_TO_PG(pw_idx) : + SKL_PW_CTL_IDX_TO_PG(pw_idx); + gen9_wait_for_power_well_fuses(dev_priv, pg); + } + + hsw_power_well_post_enable(dev_priv, + power_well->desc->irq_pipe_mask, + power_well->desc->has_vga); +} + +static void hsw_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + u32 val; + + hsw_power_well_pre_disable(dev_priv, + power_well->desc->irq_pipe_mask); + + val = intel_de_read(dev_priv, regs->driver); + intel_de_write(dev_priv, regs->driver, + val & ~HSW_PWR_WELL_CTL_REQ(pw_idx)); + hsw_wait_for_power_well_disable(dev_priv, power_well); +} + +static void +icl_combo_phy_aux_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); + u32 val; + + drm_WARN_ON(&dev_priv->drm, !IS_ICELAKE(dev_priv)); + + val = intel_de_read(dev_priv, regs->driver); + intel_de_write(dev_priv, regs->driver, + val | HSW_PWR_WELL_CTL_REQ(pw_idx)); + + if (DISPLAY_VER(dev_priv) < 12) { + val = intel_de_read(dev_priv, ICL_PORT_CL_DW12(phy)); + intel_de_write(dev_priv, ICL_PORT_CL_DW12(phy), + val | ICL_LANE_ENABLE_AUX); + } + + hsw_wait_for_power_well_enable(dev_priv, power_well, false); + + /* Display WA #1178: icl */ + if (pw_idx >= ICL_PW_CTL_IDX_AUX_A && pw_idx <= ICL_PW_CTL_IDX_AUX_B && + !intel_bios_is_port_edp(dev_priv, (enum port)phy)) { + val = intel_de_read(dev_priv, ICL_AUX_ANAOVRD1(pw_idx)); + val |= ICL_AUX_ANAOVRD1_ENABLE | ICL_AUX_ANAOVRD1_LDO_BYPASS; + intel_de_write(dev_priv, ICL_AUX_ANAOVRD1(pw_idx), val); + } +} + +static void +icl_combo_phy_aux_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); + u32 val; + + drm_WARN_ON(&dev_priv->drm, !IS_ICELAKE(dev_priv)); + + val = intel_de_read(dev_priv, ICL_PORT_CL_DW12(phy)); + intel_de_write(dev_priv, ICL_PORT_CL_DW12(phy), + val & ~ICL_LANE_ENABLE_AUX); + + val = intel_de_read(dev_priv, regs->driver); + intel_de_write(dev_priv, regs->driver, + val & ~HSW_PWR_WELL_CTL_REQ(pw_idx)); + + hsw_wait_for_power_well_disable(dev_priv, power_well); +} + +#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) + +static void icl_tc_port_assert_ref_held(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, + struct intel_digital_port *dig_port) +{ + if (drm_WARN_ON(&dev_priv->drm, !dig_port)) + return; + + if (DISPLAY_VER(dev_priv) == 11 && intel_tc_cold_requires_aux_pw(dig_port)) + return; + + drm_WARN_ON(&dev_priv->drm, !intel_tc_port_ref_held(dig_port)); +} + +#else + +static void icl_tc_port_assert_ref_held(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, + struct intel_digital_port *dig_port) +{ +} + +#endif + +#define TGL_AUX_PW_TO_TC_PORT(pw_idx) ((pw_idx) - TGL_PW_CTL_IDX_AUX_TC1) + +static void icl_tc_cold_exit(struct drm_i915_private *i915) +{ + int ret, tries = 0; + + while (1) { + ret = snb_pcode_write_timeout(i915, ICL_PCODE_EXIT_TCCOLD, 0, + 250, 1); + if (ret != -EAGAIN || ++tries == 3) + break; + msleep(1); + } + + /* Spec states that TC cold exit can take up to 1ms to complete */ + if (!ret) + msleep(1); + + /* TODO: turn failure into a error as soon i915 CI updates ICL IFWI */ + drm_dbg_kms(&i915->drm, "TC cold block %s\n", ret ? "failed" : + "succeeded"); +} + +static void +icl_tc_phy_aux_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum aux_ch aux_ch = icl_aux_pw_to_ch(power_well); + struct intel_digital_port *dig_port = aux_ch_to_digital_port(dev_priv, aux_ch); + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + bool is_tbt = power_well->desc->is_tc_tbt; + bool timeout_expected; + u32 val; + + icl_tc_port_assert_ref_held(dev_priv, power_well, dig_port); + + val = intel_de_read(dev_priv, DP_AUX_CH_CTL(aux_ch)); + val &= ~DP_AUX_CH_CTL_TBT_IO; + if (is_tbt) + val |= DP_AUX_CH_CTL_TBT_IO; + intel_de_write(dev_priv, DP_AUX_CH_CTL(aux_ch), val); + + val = intel_de_read(dev_priv, regs->driver); + intel_de_write(dev_priv, regs->driver, + val | HSW_PWR_WELL_CTL_REQ(i915_power_well_instance(power_well)->hsw.idx)); + + /* + * An AUX timeout is expected if the TBT DP tunnel is down, + * or need to enable AUX on a legacy TypeC port as part of the TC-cold + * exit sequence. + */ + timeout_expected = is_tbt || intel_tc_cold_requires_aux_pw(dig_port); + if (DISPLAY_VER(dev_priv) == 11 && intel_tc_cold_requires_aux_pw(dig_port)) + icl_tc_cold_exit(dev_priv); + + hsw_wait_for_power_well_enable(dev_priv, power_well, timeout_expected); + + if (DISPLAY_VER(dev_priv) >= 12 && !is_tbt) { + enum tc_port tc_port; + + tc_port = TGL_AUX_PW_TO_TC_PORT(i915_power_well_instance(power_well)->hsw.idx); + intel_de_write(dev_priv, HIP_INDEX_REG(tc_port), + HIP_INDEX_VAL(tc_port, 0x2)); + + if (intel_de_wait_for_set(dev_priv, DKL_CMN_UC_DW_27(tc_port), + DKL_CMN_UC_DW27_UC_HEALTH, 1)) + drm_warn(&dev_priv->drm, + "Timeout waiting TC uC health\n"); + } +} + +static void +icl_aux_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); + + if (intel_phy_is_tc(dev_priv, phy)) + return icl_tc_phy_aux_power_well_enable(dev_priv, power_well); + else if (IS_ICELAKE(dev_priv)) + return icl_combo_phy_aux_power_well_enable(dev_priv, + power_well); + else + return hsw_power_well_enable(dev_priv, power_well); +} + +static void +icl_aux_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum phy phy = icl_aux_pw_to_phy(dev_priv, power_well); + + if (intel_phy_is_tc(dev_priv, phy)) + return hsw_power_well_disable(dev_priv, power_well); + else if (IS_ICELAKE(dev_priv)) + return icl_combo_phy_aux_power_well_disable(dev_priv, + power_well); + else + return hsw_power_well_disable(dev_priv, power_well); +} + +/* + * We should only use the power well if we explicitly asked the hardware to + * enable it, so check if it's enabled and also check if we've requested it to + * be enabled. + */ +static bool hsw_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + enum i915_power_well_id id = i915_power_well_instance(power_well)->id; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + u32 mask = HSW_PWR_WELL_CTL_REQ(pw_idx) | + HSW_PWR_WELL_CTL_STATE(pw_idx); + u32 val; + + val = intel_de_read(dev_priv, regs->driver); + + /* + * On GEN9 big core due to a DMC bug the driver's request bits for PW1 + * and the MISC_IO PW will be not restored, so check instead for the + * BIOS's own request bits, which are forced-on for these power wells + * when exiting DC5/6. + */ + if (DISPLAY_VER(dev_priv) == 9 && !IS_BROXTON(dev_priv) && + (id == SKL_DISP_PW_1 || id == SKL_DISP_PW_MISC_IO)) + val |= intel_de_read(dev_priv, regs->bios); + + return (val & mask) == mask; +} + +static void assert_can_enable_dc9(struct drm_i915_private *dev_priv) +{ + drm_WARN_ONCE(&dev_priv->drm, + (intel_de_read(dev_priv, DC_STATE_EN) & DC_STATE_EN_DC9), + "DC9 already programmed to be enabled.\n"); + drm_WARN_ONCE(&dev_priv->drm, + intel_de_read(dev_priv, DC_STATE_EN) & + DC_STATE_EN_UPTO_DC5, + "DC5 still not disabled to enable DC9.\n"); + drm_WARN_ONCE(&dev_priv->drm, + intel_de_read(dev_priv, HSW_PWR_WELL_CTL2) & + HSW_PWR_WELL_CTL_REQ(SKL_PW_CTL_IDX_PW_2), + "Power well 2 on.\n"); + drm_WARN_ONCE(&dev_priv->drm, intel_irqs_enabled(dev_priv), + "Interrupts not disabled yet.\n"); + + /* + * TODO: check for the following to verify the conditions to enter DC9 + * state are satisfied: + * 1] Check relevant display engine registers to verify if mode set + * disable sequence was followed. + * 2] Check if display uninitialize sequence is initialized. + */ +} + +static void assert_can_disable_dc9(struct drm_i915_private *dev_priv) +{ + drm_WARN_ONCE(&dev_priv->drm, intel_irqs_enabled(dev_priv), + "Interrupts not disabled yet.\n"); + drm_WARN_ONCE(&dev_priv->drm, + intel_de_read(dev_priv, DC_STATE_EN) & + DC_STATE_EN_UPTO_DC5, + "DC5 still not disabled.\n"); + + /* + * TODO: check for the following to verify DC9 state was indeed + * entered before programming to disable it: + * 1] Check relevant display engine registers to verify if mode + * set disable sequence was followed. + * 2] Check if display uninitialize sequence is initialized. + */ +} + +static void gen9_write_dc_state(struct drm_i915_private *dev_priv, + u32 state) +{ + int rewrites = 0; + int rereads = 0; + u32 v; + + intel_de_write(dev_priv, DC_STATE_EN, state); + + /* It has been observed that disabling the dc6 state sometimes + * doesn't stick and dmc keeps returning old value. Make sure + * the write really sticks enough times and also force rewrite until + * we are confident that state is exactly what we want. + */ + do { + v = intel_de_read(dev_priv, DC_STATE_EN); + + if (v != state) { + intel_de_write(dev_priv, DC_STATE_EN, state); + rewrites++; + rereads = 0; + } else if (rereads++ > 5) { + break; + } + + } while (rewrites < 100); + + if (v != state) + drm_err(&dev_priv->drm, + "Writing dc state to 0x%x failed, now 0x%x\n", + state, v); + + /* Most of the times we need one retry, avoid spam */ + if (rewrites > 1) + drm_dbg_kms(&dev_priv->drm, + "Rewrote dc state to 0x%x %d times\n", + state, rewrites); +} + +static u32 gen9_dc_mask(struct drm_i915_private *dev_priv) +{ + u32 mask; + + mask = DC_STATE_EN_UPTO_DC5; + + if (DISPLAY_VER(dev_priv) >= 12) + mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6 + | DC_STATE_EN_DC9; + else if (DISPLAY_VER(dev_priv) == 11) + mask |= DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; + else if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) + mask |= DC_STATE_EN_DC9; + else + mask |= DC_STATE_EN_UPTO_DC6; + + return mask; +} + +void gen9_sanitize_dc_state(struct drm_i915_private *dev_priv) +{ + u32 val; + + if (!HAS_DISPLAY(dev_priv)) + return; + + val = intel_de_read(dev_priv, DC_STATE_EN) & gen9_dc_mask(dev_priv); + + drm_dbg_kms(&dev_priv->drm, + "Resetting DC state tracking from %02x to %02x\n", + dev_priv->dmc.dc_state, val); + dev_priv->dmc.dc_state = val; +} + +/** + * gen9_set_dc_state - set target display C power state + * @dev_priv: i915 device instance + * @state: target DC power state + * - DC_STATE_DISABLE + * - DC_STATE_EN_UPTO_DC5 + * - DC_STATE_EN_UPTO_DC6 + * - DC_STATE_EN_DC9 + * + * Signal to DMC firmware/HW the target DC power state passed in @state. + * DMC/HW can turn off individual display clocks and power rails when entering + * a deeper DC power state (higher in number) and turns these back when exiting + * that state to a shallower power state (lower in number). The HW will decide + * when to actually enter a given state on an on-demand basis, for instance + * depending on the active state of display pipes. The state of display + * registers backed by affected power rails are saved/restored as needed. + * + * Based on the above enabling a deeper DC power state is asynchronous wrt. + * enabling it. Disabling a deeper power state is synchronous: for instance + * setting %DC_STATE_DISABLE won't complete until all HW resources are turned + * back on and register state is restored. This is guaranteed by the MMIO write + * to DC_STATE_EN blocking until the state is restored. + */ +void gen9_set_dc_state(struct drm_i915_private *dev_priv, u32 state) +{ + u32 val; + u32 mask; + + if (!HAS_DISPLAY(dev_priv)) + return; + + if (drm_WARN_ON_ONCE(&dev_priv->drm, + state & ~dev_priv->dmc.allowed_dc_mask)) + state &= dev_priv->dmc.allowed_dc_mask; + + val = intel_de_read(dev_priv, DC_STATE_EN); + mask = gen9_dc_mask(dev_priv); + drm_dbg_kms(&dev_priv->drm, "Setting DC state from %02x to %02x\n", + val & mask, state); + + /* Check if DMC is ignoring our DC state requests */ + if ((val & mask) != dev_priv->dmc.dc_state) + drm_err(&dev_priv->drm, "DC state mismatch (0x%x -> 0x%x)\n", + dev_priv->dmc.dc_state, val & mask); + + val &= ~mask; + val |= state; + + gen9_write_dc_state(dev_priv, val); + + dev_priv->dmc.dc_state = val & mask; +} + +static void tgl_enable_dc3co(struct drm_i915_private *dev_priv) +{ + drm_dbg_kms(&dev_priv->drm, "Enabling DC3CO\n"); + gen9_set_dc_state(dev_priv, DC_STATE_EN_DC3CO); +} + +static void tgl_disable_dc3co(struct drm_i915_private *dev_priv) +{ + u32 val; + + drm_dbg_kms(&dev_priv->drm, "Disabling DC3CO\n"); + val = intel_de_read(dev_priv, DC_STATE_EN); + val &= ~DC_STATE_DC3CO_STATUS; + intel_de_write(dev_priv, DC_STATE_EN, val); + gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + /* + * Delay of 200us DC3CO Exit time B.Spec 49196 + */ + usleep_range(200, 210); +} + +static void assert_can_enable_dc5(struct drm_i915_private *dev_priv) +{ + enum i915_power_well_id high_pg; + + /* Power wells at this level and above must be disabled for DC5 entry */ + if (DISPLAY_VER(dev_priv) == 12) + high_pg = ICL_DISP_PW_3; + else + high_pg = SKL_DISP_PW_2; + + drm_WARN_ONCE(&dev_priv->drm, + intel_display_power_well_is_enabled(dev_priv, high_pg), + "Power wells above platform's DC5 limit still enabled.\n"); + + drm_WARN_ONCE(&dev_priv->drm, + (intel_de_read(dev_priv, DC_STATE_EN) & + DC_STATE_EN_UPTO_DC5), + "DC5 already programmed to be enabled.\n"); + assert_rpm_wakelock_held(&dev_priv->runtime_pm); + + assert_dmc_loaded(dev_priv); +} + +void gen9_enable_dc5(struct drm_i915_private *dev_priv) +{ + assert_can_enable_dc5(dev_priv); + + drm_dbg_kms(&dev_priv->drm, "Enabling DC5\n"); + + /* Wa Display #1183: skl,kbl,cfl */ + if (DISPLAY_VER(dev_priv) == 9 && !IS_BROXTON(dev_priv)) + intel_de_write(dev_priv, GEN8_CHICKEN_DCPR_1, + intel_de_read(dev_priv, GEN8_CHICKEN_DCPR_1) | SKL_SELECT_ALTERNATE_DC_EXIT); + + gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC5); +} + +static void assert_can_enable_dc6(struct drm_i915_private *dev_priv) +{ + drm_WARN_ONCE(&dev_priv->drm, + intel_de_read(dev_priv, UTIL_PIN_CTL) & UTIL_PIN_ENABLE, + "Backlight is not disabled.\n"); + drm_WARN_ONCE(&dev_priv->drm, + (intel_de_read(dev_priv, DC_STATE_EN) & + DC_STATE_EN_UPTO_DC6), + "DC6 already programmed to be enabled.\n"); + + assert_dmc_loaded(dev_priv); +} + +void skl_enable_dc6(struct drm_i915_private *dev_priv) +{ + assert_can_enable_dc6(dev_priv); + + drm_dbg_kms(&dev_priv->drm, "Enabling DC6\n"); + + /* Wa Display #1183: skl,kbl,cfl */ + if (DISPLAY_VER(dev_priv) == 9 && !IS_BROXTON(dev_priv)) + intel_de_write(dev_priv, GEN8_CHICKEN_DCPR_1, + intel_de_read(dev_priv, GEN8_CHICKEN_DCPR_1) | SKL_SELECT_ALTERNATE_DC_EXIT); + + gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); +} + +void bxt_enable_dc9(struct drm_i915_private *dev_priv) +{ + assert_can_enable_dc9(dev_priv); + + drm_dbg_kms(&dev_priv->drm, "Enabling DC9\n"); + /* + * Power sequencer reset is not needed on + * platforms with South Display Engine on PCH, + * because PPS registers are always on. + */ + if (!HAS_PCH_SPLIT(dev_priv)) + intel_pps_reset_all(dev_priv); + gen9_set_dc_state(dev_priv, DC_STATE_EN_DC9); +} + +void bxt_disable_dc9(struct drm_i915_private *dev_priv) +{ + assert_can_disable_dc9(dev_priv); + + drm_dbg_kms(&dev_priv->drm, "Disabling DC9\n"); + + gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + + intel_pps_unlock_regs_wa(dev_priv); +} + +static void hsw_power_well_sync_hw(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + const struct i915_power_well_regs *regs = power_well->desc->ops->regs; + int pw_idx = i915_power_well_instance(power_well)->hsw.idx; + u32 mask = HSW_PWR_WELL_CTL_REQ(pw_idx); + u32 bios_req = intel_de_read(dev_priv, regs->bios); + + /* Take over the request bit if set by BIOS. */ + if (bios_req & mask) { + u32 drv_req = intel_de_read(dev_priv, regs->driver); + + if (!(drv_req & mask)) + intel_de_write(dev_priv, regs->driver, drv_req | mask); + intel_de_write(dev_priv, regs->bios, bios_req & ~mask); + } +} + +static void bxt_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + bxt_ddi_phy_init(dev_priv, i915_power_well_instance(power_well)->bxt.phy); +} + +static void bxt_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + bxt_ddi_phy_uninit(dev_priv, i915_power_well_instance(power_well)->bxt.phy); +} + +static bool bxt_dpio_cmn_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + return bxt_ddi_phy_is_enabled(dev_priv, i915_power_well_instance(power_well)->bxt.phy); +} + +static void bxt_verify_ddi_phy_power_wells(struct drm_i915_private *dev_priv) +{ + struct i915_power_well *power_well; + + power_well = lookup_power_well(dev_priv, BXT_DISP_PW_DPIO_CMN_A); + if (intel_power_well_refcount(power_well) > 0) + bxt_ddi_phy_verify_state(dev_priv, i915_power_well_instance(power_well)->bxt.phy); + + power_well = lookup_power_well(dev_priv, VLV_DISP_PW_DPIO_CMN_BC); + if (intel_power_well_refcount(power_well) > 0) + bxt_ddi_phy_verify_state(dev_priv, i915_power_well_instance(power_well)->bxt.phy); + + if (IS_GEMINILAKE(dev_priv)) { + power_well = lookup_power_well(dev_priv, + GLK_DISP_PW_DPIO_CMN_C); + if (intel_power_well_refcount(power_well) > 0) + bxt_ddi_phy_verify_state(dev_priv, + i915_power_well_instance(power_well)->bxt.phy); + } +} + +static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + return ((intel_de_read(dev_priv, DC_STATE_EN) & DC_STATE_EN_DC3CO) == 0 && + (intel_de_read(dev_priv, DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0); +} + +static void gen9_assert_dbuf_enabled(struct drm_i915_private *dev_priv) +{ + u8 hw_enabled_dbuf_slices = intel_enabled_dbuf_slices_mask(dev_priv); + u8 enabled_dbuf_slices = dev_priv->dbuf.enabled_slices; + + drm_WARN(&dev_priv->drm, + hw_enabled_dbuf_slices != enabled_dbuf_slices, + "Unexpected DBuf power power state (0x%08x, expected 0x%08x)\n", + hw_enabled_dbuf_slices, + enabled_dbuf_slices); +} + +void gen9_disable_dc_states(struct drm_i915_private *dev_priv) +{ + struct intel_cdclk_config cdclk_config = {}; + + if (dev_priv->dmc.target_dc_state == DC_STATE_EN_DC3CO) { + tgl_disable_dc3co(dev_priv); + return; + } + + gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + + if (!HAS_DISPLAY(dev_priv)) + return; + + intel_cdclk_get_cdclk(dev_priv, &cdclk_config); + /* Can't read out voltage_level so can't use intel_cdclk_changed() */ + drm_WARN_ON(&dev_priv->drm, + intel_cdclk_needs_modeset(&dev_priv->cdclk.hw, + &cdclk_config)); + + gen9_assert_dbuf_enabled(dev_priv); + + if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) + bxt_verify_ddi_phy_power_wells(dev_priv); + + if (DISPLAY_VER(dev_priv) >= 11) + /* + * DMC retains HW context only for port A, the other combo + * PHY's HW context for port B is lost after DC transitions, + * so we need to restore it manually. + */ + intel_combo_phy_init(dev_priv); +} + +static void gen9_dc_off_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + gen9_disable_dc_states(dev_priv); +} + +static void gen9_dc_off_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + if (!intel_dmc_has_payload(dev_priv)) + return; + + switch (dev_priv->dmc.target_dc_state) { + case DC_STATE_EN_DC3CO: + tgl_enable_dc3co(dev_priv); + break; + case DC_STATE_EN_UPTO_DC6: + skl_enable_dc6(dev_priv); + break; + case DC_STATE_EN_UPTO_DC5: + gen9_enable_dc5(dev_priv); + break; + } +} + +static void i9xx_power_well_sync_hw_noop(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ +} + +static void i9xx_always_on_power_well_noop(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ +} + +static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + return true; +} + +static void i830_pipes_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + if ((intel_de_read(dev_priv, PIPECONF(PIPE_A)) & PIPECONF_ENABLE) == 0) + i830_enable_pipe(dev_priv, PIPE_A); + if ((intel_de_read(dev_priv, PIPECONF(PIPE_B)) & PIPECONF_ENABLE) == 0) + i830_enable_pipe(dev_priv, PIPE_B); +} + +static void i830_pipes_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + i830_disable_pipe(dev_priv, PIPE_B); + i830_disable_pipe(dev_priv, PIPE_A); +} + +static bool i830_pipes_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + return intel_de_read(dev_priv, PIPECONF(PIPE_A)) & PIPECONF_ENABLE && + intel_de_read(dev_priv, PIPECONF(PIPE_B)) & PIPECONF_ENABLE; +} + +static void i830_pipes_power_well_sync_hw(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + if (intel_power_well_refcount(power_well) > 0) + i830_pipes_power_well_enable(dev_priv, power_well); + else + i830_pipes_power_well_disable(dev_priv, power_well); +} + +static void vlv_set_power_well(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, bool enable) +{ + int pw_idx = i915_power_well_instance(power_well)->vlv.idx; + u32 mask; + u32 state; + u32 ctrl; + + mask = PUNIT_PWRGT_MASK(pw_idx); + state = enable ? PUNIT_PWRGT_PWR_ON(pw_idx) : + PUNIT_PWRGT_PWR_GATE(pw_idx); + + vlv_punit_get(dev_priv); + +#define COND \ + ((vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & mask) == state) + + if (COND) + goto out; + + ctrl = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL); + ctrl &= ~mask; + ctrl |= state; + vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, ctrl); + + if (wait_for(COND, 100)) + drm_err(&dev_priv->drm, + "timeout setting power well state %08x (%08x)\n", + state, + vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL)); + +#undef COND + +out: + vlv_punit_put(dev_priv); +} + +static void vlv_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + vlv_set_power_well(dev_priv, power_well, true); +} + +static void vlv_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + vlv_set_power_well(dev_priv, power_well, false); +} + +static bool vlv_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + int pw_idx = i915_power_well_instance(power_well)->vlv.idx; + bool enabled = false; + u32 mask; + u32 state; + u32 ctrl; + + mask = PUNIT_PWRGT_MASK(pw_idx); + ctrl = PUNIT_PWRGT_PWR_ON(pw_idx); + + vlv_punit_get(dev_priv); + + state = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & mask; + /* + * We only ever set the power-on and power-gate states, anything + * else is unexpected. + */ + drm_WARN_ON(&dev_priv->drm, state != PUNIT_PWRGT_PWR_ON(pw_idx) && + state != PUNIT_PWRGT_PWR_GATE(pw_idx)); + if (state == ctrl) + enabled = true; + + /* + * A transient state at this point would mean some unexpected party + * is poking at the power controls too. + */ + ctrl = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_CTRL) & mask; + drm_WARN_ON(&dev_priv->drm, ctrl != state); + + vlv_punit_put(dev_priv); + + return enabled; +} + +static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv) +{ + u32 val; + + /* + * On driver load, a pipe may be active and driving a DSI display. + * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck + * (and never recovering) in this case. intel_dsi_post_disable() will + * clear it when we turn off the display. + */ + val = intel_de_read(dev_priv, DSPCLK_GATE_D); + val &= DPOUNIT_CLOCK_GATE_DISABLE; + val |= VRHUNIT_CLOCK_GATE_DISABLE; + intel_de_write(dev_priv, DSPCLK_GATE_D, val); + + /* + * Disable trickle feed and enable pnd deadline calculation + */ + intel_de_write(dev_priv, MI_ARB_VLV, + MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); + intel_de_write(dev_priv, CBR1_VLV, 0); + + drm_WARN_ON(&dev_priv->drm, RUNTIME_INFO(dev_priv)->rawclk_freq == 0); + intel_de_write(dev_priv, RAWCLK_FREQ_VLV, + DIV_ROUND_CLOSEST(RUNTIME_INFO(dev_priv)->rawclk_freq, + 1000)); +} + +static void vlv_display_power_well_init(struct drm_i915_private *dev_priv) +{ + struct intel_encoder *encoder; + enum pipe pipe; + + /* + * Enable the CRI clock source so we can get at the + * display and the reference clock for VGA + * hotplug / manual detection. Supposedly DSI also + * needs the ref clock up and running. + * + * CHV DPLL B/C have some issues if VGA mode is enabled. + */ + for_each_pipe(dev_priv, pipe) { + u32 val = intel_de_read(dev_priv, DPLL(pipe)); + + val |= DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; + if (pipe != PIPE_A) + val |= DPLL_INTEGRATED_CRI_CLK_VLV; + + intel_de_write(dev_priv, DPLL(pipe), val); + } + + vlv_init_display_clock_gating(dev_priv); + + spin_lock_irq(&dev_priv->irq_lock); + valleyview_enable_display_irqs(dev_priv); + spin_unlock_irq(&dev_priv->irq_lock); + + /* + * During driver initialization/resume we can avoid restoring the + * part of the HW/SW state that will be inited anyway explicitly. + */ + if (dev_priv->power_domains.initializing) + return; + + intel_hpd_init(dev_priv); + intel_hpd_poll_disable(dev_priv); + + /* Re-enable the ADPA, if we have one */ + for_each_intel_encoder(&dev_priv->drm, encoder) { + if (encoder->type == INTEL_OUTPUT_ANALOG) + intel_crt_reset(&encoder->base); + } + + intel_vga_redisable_power_on(dev_priv); + + intel_pps_unlock_regs_wa(dev_priv); +} + +static void vlv_display_power_well_deinit(struct drm_i915_private *dev_priv) +{ + spin_lock_irq(&dev_priv->irq_lock); + valleyview_disable_display_irqs(dev_priv); + spin_unlock_irq(&dev_priv->irq_lock); + + /* make sure we're done processing display irqs */ + intel_synchronize_irq(dev_priv); + + intel_pps_reset_all(dev_priv); + + /* Prevent us from re-enabling polling on accident in late suspend */ + if (!dev_priv->drm.dev->power.is_suspended) + intel_hpd_poll_enable(dev_priv); +} + +static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + vlv_set_power_well(dev_priv, power_well, true); + + vlv_display_power_well_init(dev_priv); +} + +static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + vlv_display_power_well_deinit(dev_priv); + + vlv_set_power_well(dev_priv, power_well, false); +} + +static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + /* since ref/cri clock was enabled */ + udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ + + vlv_set_power_well(dev_priv, power_well, true); + + /* + * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx - + * 6. De-assert cmn_reset/side_reset. Same as VLV X0. + * a. GUnit 0x2110 bit[0] set to 1 (def 0) + * b. The other bits such as sfr settings / modesel may all + * be set to 0. + * + * This should only be done on init and resume from S3 with + * both PLLs disabled, or we risk losing DPIO and PLL + * synchronization. + */ + intel_de_write(dev_priv, DPIO_CTL, + intel_de_read(dev_priv, DPIO_CTL) | DPIO_CMNRST); +} + +static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) + assert_pll_disabled(dev_priv, pipe); + + /* Assert common reset */ + intel_de_write(dev_priv, DPIO_CTL, + intel_de_read(dev_priv, DPIO_CTL) & ~DPIO_CMNRST); + + vlv_set_power_well(dev_priv, power_well, false); +} + +#define BITS_SET(val, bits) (((val) & (bits)) == (bits)) + +static void assert_chv_phy_status(struct drm_i915_private *dev_priv) +{ + struct i915_power_well *cmn_bc = + lookup_power_well(dev_priv, VLV_DISP_PW_DPIO_CMN_BC); + struct i915_power_well *cmn_d = + lookup_power_well(dev_priv, CHV_DISP_PW_DPIO_CMN_D); + u32 phy_control = dev_priv->chv_phy_control; + u32 phy_status = 0; + u32 phy_status_mask = 0xffffffff; + + /* + * The BIOS can leave the PHY is some weird state + * where it doesn't fully power down some parts. + * Disable the asserts until the PHY has been fully + * reset (ie. the power well has been disabled at + * least once). + */ + if (!dev_priv->chv_phy_assert[DPIO_PHY0]) + phy_status_mask &= ~(PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH0) | + PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 0) | + PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 1) | + PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH1) | + PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 0) | + PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 1)); + + if (!dev_priv->chv_phy_assert[DPIO_PHY1]) + phy_status_mask &= ~(PHY_STATUS_CMN_LDO(DPIO_PHY1, DPIO_CH0) | + PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 0) | + PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 1)); + + if (intel_power_well_is_enabled(dev_priv, cmn_bc)) { + phy_status |= PHY_POWERGOOD(DPIO_PHY0); + + /* this assumes override is only used to enable lanes */ + if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0)) == 0) + phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0); + + if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1)) == 0) + phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1); + + /* CL1 is on whenever anything is on in either channel */ + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0) | + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1))) + phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH0); + + /* + * The DPLLB check accounts for the pipe B + port A usage + * with CL2 powered up but all the lanes in the second channel + * powered down. + */ + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1)) && + (intel_de_read(dev_priv, DPLL(PIPE_B)) & DPLL_VCO_ENABLE) == 0) + phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH1); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 0); + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 1); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH1))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 0); + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH1))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 1); + } + + if (intel_power_well_is_enabled(dev_priv, cmn_d)) { + phy_status |= PHY_POWERGOOD(DPIO_PHY1); + + /* this assumes override is only used to enable lanes */ + if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0)) == 0) + phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0))) + phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY1, DPIO_CH0); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY1, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 0); + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY1, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 1); + } + + phy_status &= phy_status_mask; + + /* + * The PHY may be busy with some initial calibration and whatnot, + * so the power state can take a while to actually change. + */ + if (intel_de_wait_for_register(dev_priv, DISPLAY_PHY_STATUS, + phy_status_mask, phy_status, 10)) + drm_err(&dev_priv->drm, + "Unexpected PHY_STATUS 0x%08x, expected 0x%08x (PHY_CONTROL=0x%08x)\n", + intel_de_read(dev_priv, DISPLAY_PHY_STATUS) & phy_status_mask, + phy_status, dev_priv->chv_phy_control); +} + +#undef BITS_SET + +static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum i915_power_well_id id = i915_power_well_instance(power_well)->id; + enum dpio_phy phy; + enum pipe pipe; + u32 tmp; + + drm_WARN_ON_ONCE(&dev_priv->drm, + id != VLV_DISP_PW_DPIO_CMN_BC && + id != CHV_DISP_PW_DPIO_CMN_D); + + if (id == VLV_DISP_PW_DPIO_CMN_BC) { + pipe = PIPE_A; + phy = DPIO_PHY0; + } else { + pipe = PIPE_C; + phy = DPIO_PHY1; + } + + /* since ref/cri clock was enabled */ + udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ + vlv_set_power_well(dev_priv, power_well, true); + + /* Poll for phypwrgood signal */ + if (intel_de_wait_for_set(dev_priv, DISPLAY_PHY_STATUS, + PHY_POWERGOOD(phy), 1)) + drm_err(&dev_priv->drm, "Display PHY %d is not power up\n", + phy); + + vlv_dpio_get(dev_priv); + + /* Enable dynamic power down */ + tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); + tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN | + DPIO_SUS_CLK_CONFIG_GATE_CLKREQ; + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); + + if (id == VLV_DISP_PW_DPIO_CMN_BC) { + tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); + tmp |= DPIO_DYNPWRDOWNEN_CH1; + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); + } else { + /* + * Force the non-existing CL2 off. BXT does this + * too, so maybe it saves some power even though + * CL2 doesn't exist? + */ + tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); + tmp |= DPIO_CL2_LDOFUSE_PWRENB; + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, tmp); + } + + vlv_dpio_put(dev_priv); + + dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy); + intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, + dev_priv->chv_phy_control); + + drm_dbg_kms(&dev_priv->drm, + "Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", + phy, dev_priv->chv_phy_control); + + assert_chv_phy_status(dev_priv); +} + +static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum i915_power_well_id id = i915_power_well_instance(power_well)->id; + enum dpio_phy phy; + + drm_WARN_ON_ONCE(&dev_priv->drm, + id != VLV_DISP_PW_DPIO_CMN_BC && + id != CHV_DISP_PW_DPIO_CMN_D); + + if (id == VLV_DISP_PW_DPIO_CMN_BC) { + phy = DPIO_PHY0; + assert_pll_disabled(dev_priv, PIPE_A); + assert_pll_disabled(dev_priv, PIPE_B); + } else { + phy = DPIO_PHY1; + assert_pll_disabled(dev_priv, PIPE_C); + } + + dev_priv->chv_phy_control &= ~PHY_COM_LANE_RESET_DEASSERT(phy); + intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, + dev_priv->chv_phy_control); + + vlv_set_power_well(dev_priv, power_well, false); + + drm_dbg_kms(&dev_priv->drm, + "Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", + phy, dev_priv->chv_phy_control); + + /* PHY is fully reset now, so we can enable the PHY state asserts */ + dev_priv->chv_phy_assert[phy] = true; + + assert_chv_phy_status(dev_priv); +} + +static void assert_chv_phy_powergate(struct drm_i915_private *dev_priv, enum dpio_phy phy, + enum dpio_channel ch, bool override, unsigned int mask) +{ + enum pipe pipe = phy == DPIO_PHY0 ? PIPE_A : PIPE_C; + u32 reg, val, expected, actual; + + /* + * The BIOS can leave the PHY is some weird state + * where it doesn't fully power down some parts. + * Disable the asserts until the PHY has been fully + * reset (ie. the power well has been disabled at + * least once). + */ + if (!dev_priv->chv_phy_assert[phy]) + return; + + if (ch == DPIO_CH0) + reg = _CHV_CMN_DW0_CH0; + else + reg = _CHV_CMN_DW6_CH1; + + vlv_dpio_get(dev_priv); + val = vlv_dpio_read(dev_priv, pipe, reg); + vlv_dpio_put(dev_priv); + + /* + * This assumes !override is only used when the port is disabled. + * All lanes should power down even without the override when + * the port is disabled. + */ + if (!override || mask == 0xf) { + expected = DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; + /* + * If CH1 common lane is not active anymore + * (eg. for pipe B DPLL) the entire channel will + * shut down, which causes the common lane registers + * to read as 0. That means we can't actually check + * the lane power down status bits, but as the entire + * register reads as 0 it's a good indication that the + * channel is indeed entirely powered down. + */ + if (ch == DPIO_CH1 && val == 0) + expected = 0; + } else if (mask != 0x0) { + expected = DPIO_ANYDL_POWERDOWN; + } else { + expected = 0; + } + + if (ch == DPIO_CH0) + actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH0; + else + actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH1; + actual &= DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; + + drm_WARN(&dev_priv->drm, actual != expected, + "Unexpected DPIO lane power down: all %d, any %d. Expected: all %d, any %d. (0x%x = 0x%08x)\n", + !!(actual & DPIO_ALLDL_POWERDOWN), + !!(actual & DPIO_ANYDL_POWERDOWN), + !!(expected & DPIO_ALLDL_POWERDOWN), + !!(expected & DPIO_ANYDL_POWERDOWN), + reg, val); +} + +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, + enum dpio_channel ch, bool override) +{ + struct i915_power_domains *power_domains = &dev_priv->power_domains; + bool was_override; + + mutex_lock(&power_domains->lock); + + was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + + if (override == was_override) + goto out; + + if (override) + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + else + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + + intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, + dev_priv->chv_phy_control); + + drm_dbg_kms(&dev_priv->drm, + "Power gating DPIO PHY%d CH%d (DPIO_PHY_CONTROL=0x%08x)\n", + phy, ch, dev_priv->chv_phy_control); + + assert_chv_phy_status(dev_priv); + +out: + mutex_unlock(&power_domains->lock); + + return was_override; +} + +void chv_phy_powergate_lanes(struct intel_encoder *encoder, + bool override, unsigned int mask) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct i915_power_domains *power_domains = &dev_priv->power_domains; + enum dpio_phy phy = vlv_dig_port_to_phy(enc_to_dig_port(encoder)); + enum dpio_channel ch = vlv_dig_port_to_channel(enc_to_dig_port(encoder)); + + mutex_lock(&power_domains->lock); + + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch); + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch); + + if (override) + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + else + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + + intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, + dev_priv->chv_phy_control); + + drm_dbg_kms(&dev_priv->drm, + "Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", + phy, ch, mask, dev_priv->chv_phy_control); + + assert_chv_phy_status(dev_priv); + + assert_chv_phy_powergate(dev_priv, phy, ch, override, mask); + + mutex_unlock(&power_domains->lock); +} + +static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum pipe pipe = PIPE_A; + bool enabled; + u32 state, ctrl; + + vlv_punit_get(dev_priv); + + state = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM) & DP_SSS_MASK(pipe); + /* + * We only ever set the power-on and power-gate states, anything + * else is unexpected. + */ + drm_WARN_ON(&dev_priv->drm, state != DP_SSS_PWR_ON(pipe) && + state != DP_SSS_PWR_GATE(pipe)); + enabled = state == DP_SSS_PWR_ON(pipe); + + /* + * A transient state at this point would mean some unexpected party + * is poking at the power controls too. + */ + ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM) & DP_SSC_MASK(pipe); + drm_WARN_ON(&dev_priv->drm, ctrl << 16 != state); + + vlv_punit_put(dev_priv); + + return enabled; +} + +static void chv_set_pipe_power_well(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, + bool enable) +{ + enum pipe pipe = PIPE_A; + u32 state; + u32 ctrl; + + state = enable ? DP_SSS_PWR_ON(pipe) : DP_SSS_PWR_GATE(pipe); + + vlv_punit_get(dev_priv); + +#define COND \ + ((vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM) & DP_SSS_MASK(pipe)) == state) + + if (COND) + goto out; + + ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM); + ctrl &= ~DP_SSC_MASK(pipe); + ctrl |= enable ? DP_SSC_PWR_ON(pipe) : DP_SSC_PWR_GATE(pipe); + vlv_punit_write(dev_priv, PUNIT_REG_DSPSSPM, ctrl); + + if (wait_for(COND, 100)) + drm_err(&dev_priv->drm, + "timeout setting power well state %08x (%08x)\n", + state, + vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM)); + +#undef COND + +out: + vlv_punit_put(dev_priv); +} + +static void chv_pipe_power_well_sync_hw(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + intel_de_write(dev_priv, DISPLAY_PHY_CONTROL, + dev_priv->chv_phy_control); +} + +static void chv_pipe_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + chv_set_pipe_power_well(dev_priv, power_well, true); + + vlv_display_power_well_init(dev_priv); +} + +static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + vlv_display_power_well_deinit(dev_priv); + + chv_set_pipe_power_well(dev_priv, power_well, false); +} + +static void +tgl_tc_cold_request(struct drm_i915_private *i915, bool block) +{ + u8 tries = 0; + int ret; + + while (1) { + u32 low_val; + u32 high_val = 0; + + if (block) + low_val = TGL_PCODE_EXIT_TCCOLD_DATA_L_BLOCK_REQ; + else + low_val = TGL_PCODE_EXIT_TCCOLD_DATA_L_UNBLOCK_REQ; + + /* + * Spec states that we should timeout the request after 200us + * but the function below will timeout after 500us + */ + ret = snb_pcode_read(i915, TGL_PCODE_TCCOLD, &low_val, &high_val); + if (ret == 0) { + if (block && + (low_val & TGL_PCODE_EXIT_TCCOLD_DATA_L_EXIT_FAILED)) + ret = -EIO; + else + break; + } + + if (++tries == 3) + break; + + msleep(1); + } + + if (ret) + drm_err(&i915->drm, "TC cold %sblock failed\n", + block ? "" : "un"); + else + drm_dbg_kms(&i915->drm, "TC cold %sblock succeeded\n", + block ? "" : "un"); +} + +static void +tgl_tc_cold_off_power_well_enable(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + tgl_tc_cold_request(i915, true); +} + +static void +tgl_tc_cold_off_power_well_disable(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + tgl_tc_cold_request(i915, false); +} + +static void +tgl_tc_cold_off_power_well_sync_hw(struct drm_i915_private *i915, + struct i915_power_well *power_well) +{ + if (intel_power_well_refcount(power_well) > 0) + tgl_tc_cold_off_power_well_enable(i915, power_well); + else + tgl_tc_cold_off_power_well_disable(i915, power_well); +} + +static bool +tgl_tc_cold_off_power_well_is_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + /* + * Not the correctly implementation but there is no way to just read it + * from PCODE, so returning count to avoid state mismatch errors + */ + return intel_power_well_refcount(power_well); +} + + +const struct i915_power_well_ops i9xx_always_on_power_well_ops = { + .sync_hw = i9xx_power_well_sync_hw_noop, + .enable = i9xx_always_on_power_well_noop, + .disable = i9xx_always_on_power_well_noop, + .is_enabled = i9xx_always_on_power_well_enabled, +}; + +const struct i915_power_well_ops chv_pipe_power_well_ops = { + .sync_hw = chv_pipe_power_well_sync_hw, + .enable = chv_pipe_power_well_enable, + .disable = chv_pipe_power_well_disable, + .is_enabled = chv_pipe_power_well_enabled, +}; + +const struct i915_power_well_ops chv_dpio_cmn_power_well_ops = { + .sync_hw = i9xx_power_well_sync_hw_noop, + .enable = chv_dpio_cmn_power_well_enable, + .disable = chv_dpio_cmn_power_well_disable, + .is_enabled = vlv_power_well_enabled, +}; + +const struct i915_power_well_ops i830_pipes_power_well_ops = { + .sync_hw = i830_pipes_power_well_sync_hw, + .enable = i830_pipes_power_well_enable, + .disable = i830_pipes_power_well_disable, + .is_enabled = i830_pipes_power_well_enabled, +}; + +static const struct i915_power_well_regs hsw_power_well_regs = { + .bios = HSW_PWR_WELL_CTL1, + .driver = HSW_PWR_WELL_CTL2, + .kvmr = HSW_PWR_WELL_CTL3, + .debug = HSW_PWR_WELL_CTL4, +}; + +const struct i915_power_well_ops hsw_power_well_ops = { + .regs = &hsw_power_well_regs, + .sync_hw = hsw_power_well_sync_hw, + .enable = hsw_power_well_enable, + .disable = hsw_power_well_disable, + .is_enabled = hsw_power_well_enabled, +}; + +const struct i915_power_well_ops gen9_dc_off_power_well_ops = { + .sync_hw = i9xx_power_well_sync_hw_noop, + .enable = gen9_dc_off_power_well_enable, + .disable = gen9_dc_off_power_well_disable, + .is_enabled = gen9_dc_off_power_well_enabled, +}; + +const struct i915_power_well_ops bxt_dpio_cmn_power_well_ops = { + .sync_hw = i9xx_power_well_sync_hw_noop, + .enable = bxt_dpio_cmn_power_well_enable, + .disable = bxt_dpio_cmn_power_well_disable, + .is_enabled = bxt_dpio_cmn_power_well_enabled, +}; + +const struct i915_power_well_ops vlv_display_power_well_ops = { + .sync_hw = i9xx_power_well_sync_hw_noop, + .enable = vlv_display_power_well_enable, + .disable = vlv_display_power_well_disable, + .is_enabled = vlv_power_well_enabled, +}; + +const struct i915_power_well_ops vlv_dpio_cmn_power_well_ops = { + .sync_hw = i9xx_power_well_sync_hw_noop, + .enable = vlv_dpio_cmn_power_well_enable, + .disable = vlv_dpio_cmn_power_well_disable, + .is_enabled = vlv_power_well_enabled, +}; + +const struct i915_power_well_ops vlv_dpio_power_well_ops = { + .sync_hw = i9xx_power_well_sync_hw_noop, + .enable = vlv_power_well_enable, + .disable = vlv_power_well_disable, + .is_enabled = vlv_power_well_enabled, +}; + +static const struct i915_power_well_regs icl_aux_power_well_regs = { + .bios = ICL_PWR_WELL_CTL_AUX1, + .driver = ICL_PWR_WELL_CTL_AUX2, + .debug = ICL_PWR_WELL_CTL_AUX4, +}; + +const struct i915_power_well_ops icl_aux_power_well_ops = { + .regs = &icl_aux_power_well_regs, + .sync_hw = hsw_power_well_sync_hw, + .enable = icl_aux_power_well_enable, + .disable = icl_aux_power_well_disable, + .is_enabled = hsw_power_well_enabled, +}; + +static const struct i915_power_well_regs icl_ddi_power_well_regs = { + .bios = ICL_PWR_WELL_CTL_DDI1, + .driver = ICL_PWR_WELL_CTL_DDI2, + .debug = ICL_PWR_WELL_CTL_DDI4, +}; + +const struct i915_power_well_ops icl_ddi_power_well_ops = { + .regs = &icl_ddi_power_well_regs, + .sync_hw = hsw_power_well_sync_hw, + .enable = hsw_power_well_enable, + .disable = hsw_power_well_disable, + .is_enabled = hsw_power_well_enabled, +}; + +const struct i915_power_well_ops tgl_tc_cold_off_ops = { + .sync_hw = tgl_tc_cold_off_power_well_sync_hw, + .enable = tgl_tc_cold_off_power_well_enable, + .disable = tgl_tc_cold_off_power_well_disable, + .is_enabled = tgl_tc_cold_off_power_well_is_enabled, +}; diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.h b/drivers/gpu/drm/i915/display/intel_display_power_well.h new file mode 100644 index 000000000000..d0624642dcb6 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2022 Intel Corporation + */ +#ifndef __INTEL_DISPLAY_POWER_WELL_H__ +#define __INTEL_DISPLAY_POWER_WELL_H__ + +#include <linux/types.h> + +#include "intel_display.h" +#include "intel_display_power.h" + +struct drm_i915_private; +struct i915_power_well; + +#define for_each_power_well(__dev_priv, __power_well) \ + for ((__power_well) = (__dev_priv)->power_domains.power_wells; \ + (__power_well) - (__dev_priv)->power_domains.power_wells < \ + (__dev_priv)->power_domains.power_well_count; \ + (__power_well)++) + +#define for_each_power_well_reverse(__dev_priv, __power_well) \ + for ((__power_well) = (__dev_priv)->power_domains.power_wells + \ + (__dev_priv)->power_domains.power_well_count - 1; \ + (__power_well) - (__dev_priv)->power_domains.power_wells >= 0; \ + (__power_well)--) + +/* + * i915_power_well_id: + * + * IDs used to look up power wells. Power wells accessed directly bypassing + * the power domains framework must be assigned a unique ID. The rest of power + * wells must be assigned DISP_PW_ID_NONE. + */ +enum i915_power_well_id { + DISP_PW_ID_NONE = 0, /* must be kept zero */ + + VLV_DISP_PW_DISP2D, + BXT_DISP_PW_DPIO_CMN_A, + VLV_DISP_PW_DPIO_CMN_BC, + GLK_DISP_PW_DPIO_CMN_C, + CHV_DISP_PW_DPIO_CMN_D, + HSW_DISP_PW_GLOBAL, + SKL_DISP_PW_MISC_IO, + SKL_DISP_PW_1, + SKL_DISP_PW_2, + ICL_DISP_PW_3, + SKL_DISP_DC_OFF, + TGL_DISP_PW_TC_COLD_OFF, +}; + +struct i915_power_well_instance { + const char *name; + const struct i915_power_domain_list { + const enum intel_display_power_domain *list; + u8 count; + } *domain_list; + + /* unique identifier for this power well */ + enum i915_power_well_id id; + /* + * Arbitraty data associated with this power well. Platform and power + * well specific. + */ + union { + struct { + /* + * request/status flag index in the PUNIT power well + * control/status registers. + */ + u8 idx; + } vlv; + struct { + enum dpio_phy phy; + } bxt; + struct { + /* + * request/status flag index in the power well + * constrol/status registers. + */ + u8 idx; + } hsw; + }; +}; + +struct i915_power_well_desc { + const struct i915_power_well_ops *ops; + const struct i915_power_well_instance_list { + const struct i915_power_well_instance *list; + u8 count; + } *instances; + + /* Mask of pipes whose IRQ logic is backed by the pw */ + u16 irq_pipe_mask:4; + u16 always_on:1; + /* + * Instead of waiting for the status bit to ack enables, + * just wait a specific amount of time and then consider + * the well enabled. + */ + u16 fixed_enable_delay:1; + /* The pw is backing the VGA functionality */ + u16 has_vga:1; + u16 has_fuses:1; + /* + * The pw is for an ICL+ TypeC PHY port in + * Thunderbolt mode. + */ + u16 is_tc_tbt:1; +}; + +struct i915_power_well { + const struct i915_power_well_desc *desc; + struct intel_power_domain_mask domains; + /* power well enable/disable usage count */ + int count; + /* cached hw enabled state */ + bool hw_enabled; + /* index into desc->instances->list */ + u8 instance_idx; +}; + +struct i915_power_well *lookup_power_well(struct drm_i915_private *i915, + enum i915_power_well_id id); + +void intel_power_well_enable(struct drm_i915_private *i915, + struct i915_power_well *power_well); +void intel_power_well_disable(struct drm_i915_private *i915, + struct i915_power_well *power_well); +void intel_power_well_sync_hw(struct drm_i915_private *i915, + struct i915_power_well *power_well); +void intel_power_well_get(struct drm_i915_private *i915, + struct i915_power_well *power_well); +void intel_power_well_put(struct drm_i915_private *i915, + struct i915_power_well *power_well); +bool intel_power_well_is_enabled(struct drm_i915_private *i915, + struct i915_power_well *power_well); +bool intel_power_well_is_enabled_cached(struct i915_power_well *power_well); +bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, + enum i915_power_well_id power_well_id); +bool intel_power_well_is_always_on(struct i915_power_well *power_well); +const char *intel_power_well_name(struct i915_power_well *power_well); +struct intel_power_domain_mask *intel_power_well_domains(struct i915_power_well *power_well); +int intel_power_well_refcount(struct i915_power_well *power_well); + +void chv_phy_powergate_lanes(struct intel_encoder *encoder, + bool override, unsigned int mask); +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, + enum dpio_channel ch, bool override); + +void gen9_enable_dc5(struct drm_i915_private *dev_priv); +void skl_enable_dc6(struct drm_i915_private *dev_priv); +void gen9_sanitize_dc_state(struct drm_i915_private *dev_priv); +void gen9_set_dc_state(struct drm_i915_private *dev_priv, u32 state); +void gen9_disable_dc_states(struct drm_i915_private *dev_priv); +void bxt_enable_dc9(struct drm_i915_private *dev_priv); +void bxt_disable_dc9(struct drm_i915_private *dev_priv); + +extern const struct i915_power_well_ops i9xx_always_on_power_well_ops; +extern const struct i915_power_well_ops chv_pipe_power_well_ops; +extern const struct i915_power_well_ops chv_dpio_cmn_power_well_ops; +extern const struct i915_power_well_ops i830_pipes_power_well_ops; +extern const struct i915_power_well_ops hsw_power_well_ops; +extern const struct i915_power_well_ops gen9_dc_off_power_well_ops; +extern const struct i915_power_well_ops bxt_dpio_cmn_power_well_ops; +extern const struct i915_power_well_ops vlv_display_power_well_ops; +extern const struct i915_power_well_ops vlv_dpio_cmn_power_well_ops; +extern const struct i915_power_well_ops vlv_dpio_power_well_ops; +extern const struct i915_power_well_ops icl_aux_power_well_ops; +extern const struct i915_power_well_ops icl_ddi_power_well_ops; +extern const struct i915_power_well_ops tgl_tc_cold_off_ops; + +#endif diff --git a/drivers/gpu/drm/i915/display/intel_display_trace.h b/drivers/gpu/drm/i915/display/intel_display_trace.h index f05f0f9b5103..2dd5a4b7f5d8 100644 --- a/drivers/gpu/drm/i915/display/intel_display_trace.h +++ b/drivers/gpu/drm/i915/display/intel_display_trace.h @@ -9,6 +9,7 @@ #if !defined(__INTEL_DISPLAY_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) #define __INTEL_DISPLAY_TRACE_H__ +#include <linux/string_helpers.h> #include <linux/types.h> #include <linux/tracepoint.h> @@ -161,7 +162,7 @@ TRACE_EVENT(intel_memory_cxsr, ), TP_printk("%s->%s, pipe A: frame=%u, scanline=%u, pipe B: frame=%u, scanline=%u, pipe C: frame=%u, scanline=%u", - onoff(__entry->old), onoff(__entry->new), + str_on_off(__entry->old), str_on_off(__entry->new), __entry->frame[PIPE_A], __entry->scanline[PIPE_A], __entry->frame[PIPE_B], __entry->scanline[PIPE_B], __entry->frame[PIPE_C], __entry->scanline[PIPE_C]) @@ -210,9 +211,9 @@ TRACE_EVENT(g4x_wm, TP_printk("pipe %c, frame=%u, scanline=%u, wm %d/%d/%d, sr %s/%d/%d/%d, hpll %s/%d/%d/%d, fbc %s", pipe_name(__entry->pipe), __entry->frame, __entry->scanline, __entry->primary, __entry->sprite, __entry->cursor, - yesno(__entry->cxsr), __entry->sr_plane, __entry->sr_cursor, __entry->sr_fbc, - yesno(__entry->hpll), __entry->hpll_plane, __entry->hpll_cursor, __entry->hpll_fbc, - yesno(__entry->fbc)) + str_yes_no(__entry->cxsr), __entry->sr_plane, __entry->sr_cursor, __entry->sr_fbc, + str_yes_no(__entry->hpll), __entry->hpll_plane, __entry->hpll_cursor, __entry->hpll_fbc, + str_yes_no(__entry->fbc)) ); TRACE_EVENT(vlv_wm, diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 776b3e6662f2..408152f9f46a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -31,11 +31,11 @@ #include <linux/pwm.h> #include <linux/sched/clock.h> -#include <drm/dp/drm_dp_dual_mode_helper.h> -#include <drm/dp/drm_dp_mst_helper.h> +#include <drm/display/drm_dp_dual_mode_helper.h> +#include <drm/display/drm_dp_mst_helper.h> +#include <drm/display/drm_dsc.h> #include <drm/drm_atomic.h> #include <drm/drm_crtc.h> -#include <drm/drm_dsc.h> #include <drm/drm_encoder.h> #include <drm/drm_fourcc.h> #include <drm/drm_probe_helper.h> @@ -280,8 +280,7 @@ struct intel_panel_bl_funcs { }; struct intel_panel { - struct drm_display_mode *fixed_mode; - struct drm_display_mode *downclock_mode; + struct list_head fixed_modes; /* backlight */ struct { @@ -847,8 +846,13 @@ struct intel_crtc_wm_state { /* gen9+ only needs 1-step wm programming */ struct skl_pipe_wm optimal; struct skl_ddb_entry ddb; + /* + * pre-icl: for packed/planar CbCr + * icl+: for everything + */ + struct skl_ddb_entry plane_ddb[I915_MAX_PLANES]; + /* pre-icl: for planar Y */ struct skl_ddb_entry plane_ddb_y[I915_MAX_PLANES]; - struct skl_ddb_entry plane_ddb_uv[I915_MAX_PLANES]; } skl; struct { @@ -954,7 +958,7 @@ struct intel_crtc_state { /* Pipe source size (ie. panel fitter input size) * All planes will be positioned inside this space, * and get clipped at the edges. */ - int pipe_src_w, pipe_src_h; + struct drm_rect pipe_src; /* * Pipe pixel rate, adjusted for @@ -1125,11 +1129,14 @@ struct intel_crtc_state { int min_cdclk[I915_MAX_PLANES]; + /* for packed/planar CbCr */ u32 data_rate[I915_MAX_PLANES]; + /* for planar Y */ + u32 data_rate_y[I915_MAX_PLANES]; - /* FIXME unify with data_rate[] */ - u64 plane_data_rate[I915_MAX_PLANES]; - u64 uv_plane_data_rate[I915_MAX_PLANES]; + /* FIXME unify with data_rate[]? */ + u64 rel_data_rate[I915_MAX_PLANES]; + u64 rel_data_rate_y[I915_MAX_PLANES]; /* Gamma mode programmed on the pipe */ u32 gamma_mode; @@ -1154,6 +1161,9 @@ struct intel_crtc_state { /* bitmask of planes that will be updated during the commit */ u8 update_planes; + u8 framestart_delay; /* 1-4 */ + u8 msa_timing_delay; /* 0-3 */ + struct { u32 enable; u32 gcp; @@ -1179,9 +1189,6 @@ struct intel_crtc_state { /* enable pipe csc? */ bool csc_enable; - /* enable pipe big joiner? */ - bool bigjoiner; - /* big joiner pipe bitmask */ u8 bigjoiner_pipes; @@ -1252,6 +1259,11 @@ enum intel_pipe_crc_source { INTEL_PIPE_CRC_SOURCE_MAX, }; +enum drrs_refresh_rate { + DRRS_REFRESH_RATE_HIGH, + DRRS_REFRESH_RATE_LOW, +}; + #define INTEL_PIPE_CRC_ENTRIES_NR 128 struct intel_pipe_crc { spinlock_t lock; @@ -1294,6 +1306,16 @@ struct intel_crtc { } active; } wm; + struct { + struct mutex mutex; + struct delayed_work work; + enum drrs_refresh_rate refresh_rate; + unsigned int frontbuffer_bits; + unsigned int busy_frontbuffer_bits; + enum transcoder cpu_transcoder; + struct intel_link_m_n m_n, m2_n2; + } drrs; + int scanline_offset; struct { @@ -1503,6 +1525,7 @@ struct intel_psr { bool colorimetry_support; bool psr2_enabled; bool psr2_sel_fetch_enabled; + bool psr2_sel_fetch_cff_enabled; bool req_psr2_sdp_prior_scanline; u8 sink_sync_latency; ktime_t last_entry_attempt; diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 1b774dcfb281..a171d42a5c5b 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -28,6 +28,7 @@ #include "i915_reg.h" #include "intel_de.h" #include "intel_dmc.h" +#include "intel_dmc_regs.h" /** * DOC: DMC Firmware Support @@ -37,6 +38,10 @@ * low-power state and comes back to normal. */ +#define DMC_VERSION(major, minor) ((major) << 16 | (minor)) +#define DMC_VERSION_MAJOR(version) ((version) >> 16) +#define DMC_VERSION_MINOR(version) ((version) & 0xffff) + #define DMC_PATH(platform, major, minor) \ "i915/" \ __stringify(platform) "_dmc_ver" \ @@ -47,8 +52,8 @@ #define DISPLAY_VER12_DMC_MAX_FW_SIZE ICL_DMC_MAX_FW_SIZE -#define ADLP_DMC_PATH DMC_PATH(adlp, 2, 14) -#define ADLP_DMC_VERSION_REQUIRED DMC_VERSION(2, 14) +#define ADLP_DMC_PATH DMC_PATH(adlp, 2, 16) +#define ADLP_DMC_VERSION_REQUIRED DMC_VERSION(2, 16) MODULE_FIRMWARE(ADLP_DMC_PATH); #define ADLS_DMC_PATH DMC_PATH(adls, 2, 01) @@ -276,17 +281,8 @@ void intel_dmc_load_program(struct drm_i915_private *dev_priv) struct intel_dmc *dmc = &dev_priv->dmc; u32 id, i; - if (!HAS_DMC(dev_priv)) { - drm_err(&dev_priv->drm, - "No DMC support available for this platform\n"); + if (!intel_dmc_has_payload(dev_priv)) return; - } - - if (!dev_priv->dmc.dmc_info[DMC_FW_MAIN].payload) { - drm_err(&dev_priv->drm, - "Tried to program CSR with empty payload\n"); - return; - } assert_rpm_wakelock_held(&dev_priv->runtime_pm); @@ -314,6 +310,17 @@ void intel_dmc_load_program(struct drm_i915_private *dev_priv) gen9_set_dc_state_debugmask(dev_priv); } +void assert_dmc_loaded(struct drm_i915_private *i915) +{ + drm_WARN_ONCE(&i915->drm, + !intel_de_read(i915, DMC_PROGRAM(i915->dmc.dmc_info[DMC_FW_MAIN].start_mmioaddr, 0)), + "DMC program storage start is NULL\n"); + drm_WARN_ONCE(&i915->drm, !intel_de_read(i915, DMC_SSP_BASE), + "DMC SSP Base Not fine\n"); + drm_WARN_ONCE(&i915->drm, !intel_de_read(i915, DMC_HTP_SKL), + "DMC HTP Not fine\n"); +} + static bool fw_info_matches_stepping(const struct intel_fw_info *fw_info, const struct stepping_info *si) { @@ -741,7 +748,7 @@ void intel_dmc_ucode_init(struct drm_i915_private *dev_priv) dmc->fw_path = RKL_DMC_PATH; dmc->required_version = RKL_DMC_VERSION_REQUIRED; dmc->max_fw_size = DISPLAY_VER12_DMC_MAX_FW_SIZE; - } else if (DISPLAY_VER(dev_priv) >= 12) { + } else if (IS_TIGERLAKE(dev_priv)) { dmc->fw_path = TGL_DMC_PATH; dmc->required_version = TGL_DMC_VERSION_REQUIRED; dmc->max_fw_size = DISPLAY_VER12_DMC_MAX_FW_SIZE; @@ -852,3 +859,101 @@ void intel_dmc_ucode_fini(struct drm_i915_private *dev_priv) for (id = 0; id < DMC_FW_MAX; id++) kfree(dev_priv->dmc.dmc_info[id].payload); } + +void intel_dmc_print_error_state(struct drm_i915_error_state_buf *m, + struct drm_i915_private *i915) +{ + struct intel_dmc *dmc = &i915->dmc; + + if (!HAS_DMC(i915)) + return; + + i915_error_printf(m, "DMC loaded: %s\n", + str_yes_no(intel_dmc_has_payload(i915))); + i915_error_printf(m, "DMC fw version: %d.%d\n", + DMC_VERSION_MAJOR(dmc->version), + DMC_VERSION_MINOR(dmc->version)); +} + +static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused) +{ + struct drm_i915_private *i915 = m->private; + intel_wakeref_t wakeref; + struct intel_dmc *dmc; + i915_reg_t dc5_reg, dc6_reg = INVALID_MMIO_REG; + + if (!HAS_DMC(i915)) + return -ENODEV; + + dmc = &i915->dmc; + + wakeref = intel_runtime_pm_get(&i915->runtime_pm); + + seq_printf(m, "fw loaded: %s\n", + str_yes_no(intel_dmc_has_payload(i915))); + seq_printf(m, "path: %s\n", dmc->fw_path); + seq_printf(m, "Pipe A fw support: %s\n", + str_yes_no(GRAPHICS_VER(i915) >= 12)); + seq_printf(m, "Pipe A fw loaded: %s\n", + str_yes_no(dmc->dmc_info[DMC_FW_PIPEA].payload)); + seq_printf(m, "Pipe B fw support: %s\n", + str_yes_no(IS_ALDERLAKE_P(i915))); + seq_printf(m, "Pipe B fw loaded: %s\n", + str_yes_no(dmc->dmc_info[DMC_FW_PIPEB].payload)); + + if (!intel_dmc_has_payload(i915)) + goto out; + + seq_printf(m, "version: %d.%d\n", DMC_VERSION_MAJOR(dmc->version), + DMC_VERSION_MINOR(dmc->version)); + + if (DISPLAY_VER(i915) >= 12) { + if (IS_DGFX(i915)) { + dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; + } else { + dc5_reg = TGL_DMC_DEBUG_DC5_COUNT; + dc6_reg = TGL_DMC_DEBUG_DC6_COUNT; + } + + /* + * NOTE: DMC_DEBUG3 is a general purpose reg. + * According to B.Specs:49196 DMC f/w reuses DC5/6 counter + * reg for DC3CO debugging and validation, + * but TGL DMC f/w is using DMC_DEBUG3 reg for DC3CO counter. + */ + seq_printf(m, "DC3CO count: %d\n", + intel_de_read(i915, IS_DGFX(i915) ? + DG1_DMC_DEBUG3 : TGL_DMC_DEBUG3)); + } else { + dc5_reg = IS_BROXTON(i915) ? BXT_DMC_DC3_DC5_COUNT : + SKL_DMC_DC3_DC5_COUNT; + if (!IS_GEMINILAKE(i915) && !IS_BROXTON(i915)) + dc6_reg = SKL_DMC_DC5_DC6_COUNT; + } + + seq_printf(m, "DC3 -> DC5 count: %d\n", intel_de_read(i915, dc5_reg)); + if (i915_mmio_reg_valid(dc6_reg)) + seq_printf(m, "DC5 -> DC6 count: %d\n", + intel_de_read(i915, dc6_reg)); + +out: + seq_printf(m, "program base: 0x%08x\n", + intel_de_read(i915, DMC_PROGRAM(dmc->dmc_info[DMC_FW_MAIN].start_mmioaddr, 0))); + seq_printf(m, "ssp base: 0x%08x\n", + intel_de_read(i915, DMC_SSP_BASE)); + seq_printf(m, "htp: 0x%08x\n", intel_de_read(i915, DMC_HTP_SKL)); + + intel_runtime_pm_put(&i915->runtime_pm, wakeref); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(intel_dmc_debugfs_status); + +void intel_dmc_debugfs_register(struct drm_i915_private *i915) +{ + struct drm_minor *minor = i915->drm.primary; + + debugfs_create_file("i915_dmc_info", 0444, minor->debugfs_root, + i915, &intel_dmc_debugfs_status_fops); +} diff --git a/drivers/gpu/drm/i915/display/intel_dmc.h b/drivers/gpu/drm/i915/display/intel_dmc.h index 7c590309a3a9..41091aee3b47 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.h +++ b/drivers/gpu/drm/i915/display/intel_dmc.h @@ -10,12 +10,9 @@ #include "intel_wakeref.h" #include <linux/workqueue.h> +struct drm_i915_error_state_buf; struct drm_i915_private; -#define DMC_VERSION(major, minor) ((major) << 16 | (minor)) -#define DMC_VERSION_MAJOR(version) ((version) >> 16) -#define DMC_VERSION_MINOR(version) ((version) & 0xffff) - enum { DMC_FW_MAIN = 0, DMC_FW_PIPEA, @@ -54,5 +51,10 @@ void intel_dmc_ucode_fini(struct drm_i915_private *i915); void intel_dmc_ucode_suspend(struct drm_i915_private *i915); void intel_dmc_ucode_resume(struct drm_i915_private *i915); bool intel_dmc_has_payload(struct drm_i915_private *i915); +void intel_dmc_debugfs_register(struct drm_i915_private *i915); +void intel_dmc_print_error_state(struct drm_i915_error_state_buf *m, + struct drm_i915_private *i915); + +void assert_dmc_loaded(struct drm_i915_private *i915); #endif /* __INTEL_DMC_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dmc_regs.h b/drivers/gpu/drm/i915/display/intel_dmc_regs.h new file mode 100644 index 000000000000..7853827988d4 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dmc_regs.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2022 Intel Corporation + */ + +#ifndef __INTEL_DMC_REGS_H__ +#define __INTEL_DMC_REGS_H__ + +#include "i915_reg_defs.h" + +#define DMC_PROGRAM(addr, i) _MMIO((addr) + (i) * 4) +#define DMC_SSP_BASE_ADDR_GEN9 0x00002FC0 +#define DMC_HTP_ADDR_SKL 0x00500034 +#define DMC_SSP_BASE _MMIO(0x8F074) +#define DMC_HTP_SKL _MMIO(0x8F004) +#define DMC_LAST_WRITE _MMIO(0x8F034) +#define DMC_LAST_WRITE_VALUE 0xc003b400 +#define DMC_MMIO_START_RANGE 0x80000 +#define DMC_MMIO_END_RANGE 0x8FFFF +#define DMC_V1_MMIO_START_RANGE 0x80000 +#define TGL_MAIN_MMIO_START 0x8F000 +#define TGL_MAIN_MMIO_END 0x8FFFF +#define _TGL_PIPEA_MMIO_START 0x92000 +#define _TGL_PIPEA_MMIO_END 0x93FFF +#define _TGL_PIPEB_MMIO_START 0x96000 +#define _TGL_PIPEB_MMIO_END 0x97FFF +#define ADLP_PIPE_MMIO_START 0x5F000 +#define ADLP_PIPE_MMIO_END 0x5FFFF + +#define TGL_PIPE_MMIO_START(dmc_id) _PICK_EVEN(((dmc_id) - 1), _TGL_PIPEA_MMIO_START,\ + _TGL_PIPEB_MMIO_START) + +#define TGL_PIPE_MMIO_END(dmc_id) _PICK_EVEN(((dmc_id) - 1), _TGL_PIPEA_MMIO_END,\ + _TGL_PIPEB_MMIO_END) + +#define SKL_DMC_DC3_DC5_COUNT _MMIO(0x80030) +#define SKL_DMC_DC5_DC6_COUNT _MMIO(0x8002C) +#define BXT_DMC_DC3_DC5_COUNT _MMIO(0x80038) +#define TGL_DMC_DEBUG_DC5_COUNT _MMIO(0x101084) +#define TGL_DMC_DEBUG_DC6_COUNT _MMIO(0x101088) +#define DG1_DMC_DEBUG_DC5_COUNT _MMIO(0x134154) + +#define TGL_DMC_DEBUG3 _MMIO(0x101090) +#define DG1_DMC_DEBUG3 _MMIO(0x13415c) + +#endif /* __INTEL_DMC_REGS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index f868db8be02a..e4a79c11fd25 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -29,15 +29,17 @@ #include <linux/i2c.h> #include <linux/notifier.h> #include <linux/slab.h> +#include <linux/string_helpers.h> #include <linux/timekeeping.h> #include <linux/types.h> #include <asm/byteorder.h> +#include <drm/display/drm_dp_helper.h> +#include <drm/display/drm_dsc_helper.h> +#include <drm/display/drm_hdmi_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> -#include <drm/dp/drm_dp_helper.h> -#include <drm/drm_edid.h> #include <drm/drm_probe_helper.h> #include "g4x_dp.h" @@ -59,7 +61,6 @@ #include "intel_dp_mst.h" #include "intel_dpio_phy.h" #include "intel_dpll.h" -#include "intel_drrs.h" #include "intel_fifo_underrun.h" #include "intel_hdcp.h" #include "intel_hdmi.h" @@ -67,6 +68,7 @@ #include "intel_lspcon.h" #include "intel_lvds.h" #include "intel_panel.h" +#include "intel_pch_display.h" #include "intel_pps.h" #include "intel_psr.h" #include "intel_tc.h" @@ -386,23 +388,13 @@ static int dg2_max_source_rate(struct intel_dp *intel_dp) return intel_dp_is_edp(intel_dp) ? 810000 : 1350000; } -static bool is_low_voltage_sku(struct drm_i915_private *i915, enum phy phy) -{ - u32 voltage; - - voltage = intel_de_read(i915, ICL_PORT_COMP_DW3(phy)) & VOLTAGE_INFO_MASK; - - return voltage == VOLTAGE_INFO_0_85V; -} - static int icl_max_source_rate(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev); enum phy phy = intel_port_to_phy(dev_priv, dig_port->base.port); - if (intel_phy_is_combo(dev_priv, phy) && - (is_low_voltage_sku(dev_priv, phy) || !intel_dp_is_edp(intel_dp))) + if (intel_phy_is_combo(dev_priv, phy) && !intel_dp_is_edp(intel_dp)) return 540000; return 810000; @@ -410,23 +402,7 @@ static int icl_max_source_rate(struct intel_dp *intel_dp) static int ehl_max_source_rate(struct intel_dp *intel_dp) { - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev); - enum phy phy = intel_port_to_phy(dev_priv, dig_port->base.port); - - if (intel_dp_is_edp(intel_dp) || is_low_voltage_sku(dev_priv, phy)) - return 540000; - - return 810000; -} - -static int dg1_max_source_rate(struct intel_dp *intel_dp) -{ - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum phy phy = intel_port_to_phy(i915, dig_port->base.port); - - if (intel_phy_is_combo(i915, phy) && is_low_voltage_sku(i915, phy)) + if (intel_dp_is_edp(intel_dp)) return 540000; return 810000; @@ -469,7 +445,7 @@ intel_dp_set_source_rates(struct intel_dp *intel_dp) max_rate = dg2_max_source_rate(intel_dp); else if (IS_ALDERLAKE_P(dev_priv) || IS_ALDERLAKE_S(dev_priv) || IS_DG1(dev_priv) || IS_ROCKETLAKE(dev_priv)) - max_rate = dg1_max_source_rate(intel_dp); + max_rate = 810000; else if (IS_JSL_EHL(dev_priv)) max_rate = ehl_max_source_rate(intel_dp); else @@ -580,8 +556,9 @@ static bool intel_dp_can_link_train_fallback_for_edp(struct intel_dp *intel_dp, int link_rate, u8 lane_count) { + /* FIXME figure out what we actually want here */ const struct drm_display_mode *fixed_mode = - intel_dp->attached_connector->panel.fixed_mode; + intel_panel_preferred_fixed_mode(intel_dp->attached_connector); int mode_rate, max_rate; mode_rate = intel_dp_link_required(fixed_mode->clock, 18); @@ -783,14 +760,12 @@ static u8 intel_dp_dsc_get_slice_count(struct intel_dp *intel_dp, } static enum intel_output_format -intel_dp_output_format(struct drm_connector *connector, - const struct drm_display_mode *mode) +intel_dp_output_format(struct intel_connector *connector, + bool ycbcr_420_output) { - struct intel_dp *intel_dp = intel_attached_dp(to_intel_connector(connector)); - const struct drm_display_info *info = &connector->display_info; + struct intel_dp *intel_dp = intel_attached_dp(connector); - if (!connector->ycbcr_420_allowed || - !drm_mode_is_420_only(info, mode)) + if (!connector->base.ycbcr_420_allowed || !ycbcr_420_output) return INTEL_OUTPUT_FORMAT_RGB; if (intel_dp->dfp.rgb_to_ycbcr && @@ -825,11 +800,12 @@ static int intel_dp_output_bpp(enum intel_output_format output_format, int bpp) } static int -intel_dp_mode_min_output_bpp(struct drm_connector *connector, +intel_dp_mode_min_output_bpp(struct intel_connector *connector, const struct drm_display_mode *mode) { + const struct drm_display_info *info = &connector->base.display_info; enum intel_output_format output_format = - intel_dp_output_format(connector, mode); + intel_dp_output_format(connector, drm_mode_is_420_only(info, mode)); return intel_dp_output_bpp(output_format, intel_dp_min_bpp(output_format)); } @@ -853,6 +829,43 @@ static bool intel_dp_hdisplay_bad(struct drm_i915_private *dev_priv, return hdisplay == 4096 && !HAS_DDI(dev_priv); } +static int intel_dp_max_tmds_clock(struct intel_dp *intel_dp) +{ + struct intel_connector *connector = intel_dp->attached_connector; + const struct drm_display_info *info = &connector->base.display_info; + int max_tmds_clock = intel_dp->dfp.max_tmds_clock; + + /* Only consider the sink's max TMDS clock if we know this is a HDMI DFP */ + if (max_tmds_clock && info->max_tmds_clock) + max_tmds_clock = min(max_tmds_clock, info->max_tmds_clock); + + return max_tmds_clock; +} + +static enum drm_mode_status +intel_dp_tmds_clock_valid(struct intel_dp *intel_dp, + int clock, int bpc, bool ycbcr420_output, + bool respect_downstream_limits) +{ + int tmds_clock, min_tmds_clock, max_tmds_clock; + + if (!respect_downstream_limits) + return MODE_OK; + + tmds_clock = intel_hdmi_tmds_clock(clock, bpc, ycbcr420_output); + + min_tmds_clock = intel_dp->dfp.min_tmds_clock; + max_tmds_clock = intel_dp_max_tmds_clock(intel_dp); + + if (min_tmds_clock && tmds_clock < min_tmds_clock) + return MODE_CLOCK_LOW; + + if (max_tmds_clock && tmds_clock > max_tmds_clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + static enum drm_mode_status intel_dp_mode_valid_downstream(struct intel_connector *connector, const struct drm_display_mode *mode, @@ -860,13 +873,14 @@ intel_dp_mode_valid_downstream(struct intel_connector *connector, { struct intel_dp *intel_dp = intel_attached_dp(connector); const struct drm_display_info *info = &connector->base.display_info; - int tmds_clock; + enum drm_mode_status status; + bool ycbcr_420_only; /* If PCON supports FRL MODE, check FRL bandwidth constraints */ if (intel_dp->dfp.pcon_max_frl_bw) { int target_bw; int max_frl_bw; - int bpp = intel_dp_mode_min_output_bpp(&connector->base, mode); + int bpp = intel_dp_mode_min_output_bpp(connector, mode); target_bw = bpp * target_clock; @@ -885,16 +899,23 @@ intel_dp_mode_valid_downstream(struct intel_connector *connector, target_clock > intel_dp->dfp.max_dotclock) return MODE_CLOCK_HIGH; + ycbcr_420_only = drm_mode_is_420_only(info, mode); + /* Assume 8bpc for the DP++/HDMI/DVI TMDS clock check */ - tmds_clock = intel_hdmi_tmds_clock(target_clock, 8, - drm_mode_is_420_only(info, mode)); + status = intel_dp_tmds_clock_valid(intel_dp, target_clock, + 8, ycbcr_420_only, true); - if (intel_dp->dfp.min_tmds_clock && - tmds_clock < intel_dp->dfp.min_tmds_clock) - return MODE_CLOCK_LOW; - if (intel_dp->dfp.max_tmds_clock && - tmds_clock > intel_dp->dfp.max_tmds_clock) - return MODE_CLOCK_HIGH; + if (status != MODE_OK) { + if (ycbcr_420_only || + !connector->base.ycbcr_420_allowed || + !drm_mode_is_420_also(info, mode)) + return status; + + status = intel_dp_tmds_clock_valid(intel_dp, target_clock, + 8, true, true); + if (status != MODE_OK) + return status; + } return MODE_OK; } @@ -911,13 +932,13 @@ static bool intel_dp_need_bigjoiner(struct intel_dp *intel_dp, } static enum drm_mode_status -intel_dp_mode_valid(struct drm_connector *connector, +intel_dp_mode_valid(struct drm_connector *_connector, struct drm_display_mode *mode) { - struct intel_dp *intel_dp = intel_attached_dp(to_intel_connector(connector)); - struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; - struct drm_i915_private *dev_priv = to_i915(connector->dev); + struct intel_connector *connector = to_intel_connector(_connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + const struct drm_display_mode *fixed_mode; int target_clock = mode->clock; int max_rate, mode_rate, max_lanes, max_link_clock; int max_dotclk = dev_priv->max_dotclk_freq; @@ -932,8 +953,9 @@ intel_dp_mode_valid(struct drm_connector *connector, if (mode->flags & DRM_MODE_FLAG_DBLCLK) return MODE_H_ILLEGAL; + fixed_mode = intel_panel_fixed_mode(connector, mode); if (intel_dp_is_edp(intel_dp) && fixed_mode) { - status = intel_panel_mode_valid(intel_connector, mode); + status = intel_panel_mode_valid(connector, mode); if (status != MODE_OK) return status; @@ -1007,8 +1029,7 @@ intel_dp_mode_valid(struct drm_connector *connector, if (mode_rate > max_rate && !dsc) return MODE_CLOCK_HIGH; - status = intel_dp_mode_valid_downstream(intel_connector, - mode, target_clock); + status = intel_dp_mode_valid_downstream(connector, mode, target_clock); if (status != MODE_OK) return status; @@ -1130,44 +1151,50 @@ static bool intel_dp_supports_dsc(struct intel_dp *intel_dp, drm_dp_sink_supports_dsc(intel_dp->dsc_dpcd); } -static bool intel_dp_hdmi_ycbcr420(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) +static bool intel_dp_is_ycbcr420(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) { return crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420 || (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 && intel_dp->dfp.ycbcr_444_to_420); } -static bool intel_dp_hdmi_tmds_clock_valid(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, int bpc) +static int intel_dp_hdmi_compute_bpc(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state, + int bpc, bool respect_downstream_limits) { + bool ycbcr420_output = intel_dp_is_ycbcr420(intel_dp, crtc_state); int clock = crtc_state->hw.adjusted_mode.crtc_clock; - int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, - intel_dp_hdmi_ycbcr420(intel_dp, crtc_state)); - - if (intel_dp->dfp.min_tmds_clock && - tmds_clock < intel_dp->dfp.min_tmds_clock) - return false; - if (intel_dp->dfp.max_tmds_clock && - tmds_clock > intel_dp->dfp.max_tmds_clock) - return false; + /* + * Current bpc could already be below 8bpc due to + * FDI bandwidth constraints or other limits. + * HDMI minimum is 8bpc however. + */ + bpc = max(bpc, 8); - return true; -} + /* + * We will never exceed downstream TMDS clock limits while + * attempting deep color. If the user insists on forcing an + * out of spec mode they will have to be satisfied with 8bpc. + */ + if (!respect_downstream_limits) + bpc = 8; -static bool intel_dp_hdmi_bpc_possible(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - int bpc) -{ + for (; bpc >= 8; bpc -= 2) { + if (intel_hdmi_bpc_possible(crtc_state, bpc, + intel_dp->has_hdmi_sink, ycbcr420_output) && + intel_dp_tmds_clock_valid(intel_dp, clock, bpc, ycbcr420_output, + respect_downstream_limits) == MODE_OK) + return bpc; + } - return intel_hdmi_bpc_possible(crtc_state, bpc, intel_dp->has_hdmi_sink, - intel_dp_hdmi_ycbcr420(intel_dp, crtc_state)) && - intel_dp_hdmi_tmds_clock_valid(intel_dp, crtc_state, bpc); + return -EINVAL; } static int intel_dp_max_bpp(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) + const struct intel_crtc_state *crtc_state, + bool respect_downstream_limits) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_connector *intel_connector = intel_dp->attached_connector; @@ -1179,10 +1206,14 @@ static int intel_dp_max_bpp(struct intel_dp *intel_dp, bpc = min_t(int, bpc, intel_dp->dfp.max_bpc); if (intel_dp->dfp.min_tmds_clock) { - for (; bpc >= 10; bpc -= 2) { - if (intel_dp_hdmi_bpc_possible(intel_dp, crtc_state, bpc)) - break; - } + int max_hdmi_bpc; + + max_hdmi_bpc = intel_dp_hdmi_compute_bpc(intel_dp, crtc_state, bpc, + respect_downstream_limits); + if (max_hdmi_bpc < 0) + return 0; + + bpc = min(bpc, max_hdmi_bpc); } bpp = bpc * 3; @@ -1424,13 +1455,13 @@ static int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, pipe_config->lane_count, adjusted_mode->crtc_clock, adjusted_mode->crtc_hdisplay, - pipe_config->bigjoiner, + pipe_config->bigjoiner_pipes, pipe_bpp); dsc_dp_slice_count = intel_dp_dsc_get_slice_count(intel_dp, adjusted_mode->crtc_clock, adjusted_mode->crtc_hdisplay, - pipe_config->bigjoiner); + pipe_config->bigjoiner_pipes); if (!dsc_max_output_bpp || !dsc_dp_slice_count) { drm_dbg_kms(&dev_priv->drm, "Compressed BPP/Slice Count not supported\n"); @@ -1464,7 +1495,7 @@ static int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, * then we need to use 2 VDSC instances. */ if (adjusted_mode->crtc_clock > dev_priv->max_cdclk_freq || - pipe_config->bigjoiner) { + pipe_config->bigjoiner_pipes) { if (pipe_config->dsc.slice_count < 2) { drm_dbg_kms(&dev_priv->drm, "Cannot split stream to use 2 VDSC instances\n"); @@ -1497,13 +1528,16 @@ static int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, static int intel_dp_compute_link_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, - struct drm_connector_state *conn_state) + struct drm_connector_state *conn_state, + bool respect_downstream_limits) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct link_config_limits limits; + bool joiner_needs_dsc = false; int ret; limits.min_rate = intel_dp_common_rate(intel_dp, 0); @@ -1513,7 +1547,7 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, limits.max_lane_count = intel_dp_max_lane_count(intel_dp); limits.min_bpp = intel_dp_min_bpp(pipe_config->output_format); - limits.max_bpp = intel_dp_max_bpp(intel_dp, pipe_config); + limits.max_bpp = intel_dp_max_bpp(intel_dp, pipe_config, respect_downstream_limits); if (intel_dp->use_max_params) { /* @@ -1537,7 +1571,14 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, if (intel_dp_need_bigjoiner(intel_dp, adjusted_mode->crtc_hdisplay, adjusted_mode->crtc_clock)) - pipe_config->bigjoiner = true; + pipe_config->bigjoiner_pipes = GENMASK(crtc->pipe + 1, crtc->pipe); + + /* + * Pipe joiner needs compression up to display 12 due to bandwidth + * limitation. DG2 onwards pipe joiner can be enabled without + * compression. + */ + joiner_needs_dsc = DISPLAY_VER(i915) < 13 && pipe_config->bigjoiner_pipes; /* * Optimize for slow and wide for everything, because there are some @@ -1545,13 +1586,10 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, */ ret = intel_dp_compute_link_config_wide(intel_dp, pipe_config, &limits); - /* - * Pipe joiner needs compression upto display12 due to BW limitation. DG2 - * onwards pipe joiner can be enabled without compression. - */ - drm_dbg_kms(&i915->drm, "Force DSC en = %d\n", intel_dp->force_dsc_en); - if (ret || intel_dp->force_dsc_en || (DISPLAY_VER(i915) < 13 && - pipe_config->bigjoiner)) { + if (ret || joiner_needs_dsc || intel_dp->force_dsc_en) { + drm_dbg_kms(&i915->drm, "Try DSC (fallback=%s, joiner=%s, force=%s)\n", + str_yes_no(ret), str_yes_no(joiner_needs_dsc), + str_yes_no(intel_dp->force_dsc_en)); ret = intel_dp_dsc_compute_config(intel_dp, pipe_config, conn_state, &limits); if (ret < 0) @@ -1786,6 +1824,137 @@ intel_dp_compute_hdr_metadata_infoframe_sdp(struct intel_dp *intel_dp, intel_hdmi_infoframe_enable(HDMI_PACKET_TYPE_GAMUT_METADATA); } +static bool cpu_transcoder_has_drrs(struct drm_i915_private *i915, + enum transcoder cpu_transcoder) +{ + /* M1/N1 is double buffered */ + if (DISPLAY_VER(i915) >= 9 || IS_BROADWELL(i915)) + return true; + + return intel_cpu_transcoder_has_m2_n2(i915, cpu_transcoder); +} + +static bool can_enable_drrs(struct intel_connector *connector, + const struct intel_crtc_state *pipe_config, + const struct drm_display_mode *downclock_mode) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + + if (pipe_config->vrr.enable) + return false; + + /* + * DRRS and PSR can't be enable together, so giving preference to PSR + * as it allows more power-savings by complete shutting down display, + * so to guarantee this, intel_drrs_compute_config() must be called + * after intel_psr_compute_config(). + */ + if (pipe_config->has_psr) + return false; + + /* FIXME missing FDI M2/N2 etc. */ + if (pipe_config->has_pch_encoder) + return false; + + if (!cpu_transcoder_has_drrs(i915, pipe_config->cpu_transcoder)) + return false; + + return downclock_mode && + intel_panel_drrs_type(connector) == DRRS_TYPE_SEAMLESS; +} + +static void +intel_dp_drrs_compute_config(struct intel_connector *connector, + struct intel_crtc_state *pipe_config, + int output_bpp, bool constant_n) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + const struct drm_display_mode *downclock_mode = + intel_panel_downclock_mode(connector, &pipe_config->hw.adjusted_mode); + int pixel_clock; + + if (!can_enable_drrs(connector, pipe_config, downclock_mode)) { + if (intel_cpu_transcoder_has_m2_n2(i915, pipe_config->cpu_transcoder)) + intel_zero_m_n(&pipe_config->dp_m2_n2); + return; + } + + if (IS_IRONLAKE(i915) || IS_SANDYBRIDGE(i915) || IS_IVYBRIDGE(i915)) + pipe_config->msa_timing_delay = i915->vbt.edp.drrs_msa_timing_delay; + + pipe_config->has_drrs = true; + + pixel_clock = downclock_mode->clock; + if (pipe_config->splitter.enable) + pixel_clock /= pipe_config->splitter.link_count; + + intel_link_compute_m_n(output_bpp, pipe_config->lane_count, pixel_clock, + pipe_config->port_clock, &pipe_config->dp_m2_n2, + constant_n, pipe_config->fec_enable); + + /* FIXME: abstract this better */ + if (pipe_config->splitter.enable) + pipe_config->dp_m2_n2.data_m *= pipe_config->splitter.link_count; +} + +static bool intel_dp_has_audio(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + const struct intel_digital_connector_state *intel_conn_state = + to_intel_digital_connector_state(conn_state); + + if (!intel_dp_port_has_audio(i915, encoder->port)) + return false; + + if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO) + return intel_dp->has_audio; + else + return intel_conn_state->force_audio == HDMI_AUDIO_ON; +} + +static int +intel_dp_compute_output_format(struct intel_encoder *encoder, + struct intel_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + bool respect_downstream_limits) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_connector *connector = intel_dp->attached_connector; + const struct drm_display_info *info = &connector->base.display_info; + const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + bool ycbcr_420_only; + int ret; + + ycbcr_420_only = drm_mode_is_420_only(info, adjusted_mode); + + crtc_state->output_format = intel_dp_output_format(connector, ycbcr_420_only); + + if (ycbcr_420_only && !intel_dp_is_ycbcr420(intel_dp, crtc_state)) { + drm_dbg_kms(&i915->drm, + "YCbCr 4:2:0 mode but YCbCr 4:2:0 output not possible. Falling back to RGB.\n"); + crtc_state->output_format = INTEL_OUTPUT_FORMAT_RGB; + } + + ret = intel_dp_compute_link_config(encoder, crtc_state, conn_state, + respect_downstream_limits); + if (ret) { + if (intel_dp_is_ycbcr420(intel_dp, crtc_state) || + !connector->base.ycbcr_420_allowed || + !drm_mode_is_420_also(info, adjusted_mode)) + return ret; + + crtc_state->output_format = intel_dp_output_format(connector, true); + ret = intel_dp_compute_link_config(encoder, crtc_state, conn_state, + respect_downstream_limits); + } + + return ret; +} + int intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, @@ -1794,38 +1963,19 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - enum port port = encoder->port; - struct intel_connector *intel_connector = intel_dp->attached_connector; - struct intel_digital_connector_state *intel_conn_state = - to_intel_digital_connector_state(conn_state); + const struct drm_display_mode *fixed_mode; + struct intel_connector *connector = intel_dp->attached_connector; bool constant_n = drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_CONSTANT_N); int ret = 0, output_bpp; - if (HAS_PCH_SPLIT(dev_priv) && !HAS_DDI(dev_priv) && port != PORT_A) + if (HAS_PCH_SPLIT(dev_priv) && !HAS_DDI(dev_priv) && encoder->port != PORT_A) pipe_config->has_pch_encoder = true; - pipe_config->output_format = intel_dp_output_format(&intel_connector->base, - adjusted_mode); - - if (pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) { - ret = intel_panel_fitting(pipe_config, conn_state); - if (ret) - return ret; - } - - if (!intel_dp_port_has_audio(dev_priv, port)) - pipe_config->has_audio = false; - else if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO) - pipe_config->has_audio = intel_dp->has_audio; - else - pipe_config->has_audio = intel_conn_state->force_audio == HDMI_AUDIO_ON; - - if (intel_dp_is_edp(intel_dp) && intel_connector->panel.fixed_mode) { - ret = intel_panel_compute_config(intel_connector, adjusted_mode); - if (ret) - return ret; + pipe_config->has_audio = intel_dp_has_audio(encoder, pipe_config, conn_state); - ret = intel_panel_fitting(pipe_config, conn_state); + fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode); + if (intel_dp_is_edp(intel_dp) && fixed_mode) { + ret = intel_panel_compute_config(connector, adjusted_mode); if (ret) return ret; } @@ -1843,10 +1993,23 @@ intel_dp_compute_config(struct intel_encoder *encoder, if (intel_dp_hdisplay_bad(dev_priv, adjusted_mode->crtc_hdisplay)) return -EINVAL; - ret = intel_dp_compute_link_config(encoder, pipe_config, conn_state); - if (ret < 0) + /* + * Try to respect downstream TMDS clock limits first, if + * that fails assume the user might know something we don't. + */ + ret = intel_dp_compute_output_format(encoder, pipe_config, conn_state, true); + if (ret) + ret = intel_dp_compute_output_format(encoder, pipe_config, conn_state, false); + if (ret) return ret; + if ((intel_dp_is_edp(intel_dp) && fixed_mode) || + pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) { + ret = intel_panel_fitting(pipe_config, conn_state); + if (ret) + return ret; + } + pipe_config->limited_color_range = intel_dp_limited_color_range(pipe_config, conn_state); @@ -1892,8 +2055,8 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_vrr_compute_config(pipe_config, conn_state); intel_psr_compute_config(intel_dp, pipe_config, conn_state); - intel_drrs_compute_config(intel_dp, pipe_config, output_bpp, - constant_n); + intel_dp_drrs_compute_config(connector, pipe_config, + output_bpp, constant_n); intel_dp_compute_vsc_sdp(intel_dp, pipe_config, conn_state); intel_dp_compute_hdr_metadata_infoframe_sdp(intel_dp, pipe_config, conn_state); @@ -1976,7 +2139,7 @@ void intel_dp_sink_set_decompression_state(struct intel_dp *intel_dp, if (ret < 0) drm_dbg_kms(&i915->drm, "Failed to %s sink decompression state\n", - enabledisable(enable)); + str_enable_disable(enable)); } static void @@ -2452,7 +2615,7 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_PROTOCOL_CONVERTER_CONTROL_0, tmp) != 1) drm_dbg_kms(&i915->drm, "Failed to %s protocol converter HDMI mode\n", - enabledisable(intel_dp->has_hdmi_sink)); + str_enable_disable(intel_dp->has_hdmi_sink)); tmp = crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 && intel_dp->dfp.ycbcr_444_to_420 ? DP_CONVERSION_TO_YCBCR420_ENABLE : 0; @@ -2461,45 +2624,15 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, DP_PROTOCOL_CONVERTER_CONTROL_1, tmp) != 1) drm_dbg_kms(&i915->drm, "Failed to %s protocol converter YCbCr 4:2:0 conversion mode\n", - enabledisable(intel_dp->dfp.ycbcr_444_to_420)); + str_enable_disable(intel_dp->dfp.ycbcr_444_to_420)); - tmp = 0; - if (intel_dp->dfp.rgb_to_ycbcr) { - bool bt2020, bt709; - - /* - * FIXME: Currently if userspace selects BT2020 or BT709, but PCON supports only - * RGB->YCbCr for BT601 colorspace, we go ahead with BT601, as default. - * - */ - tmp = DP_CONVERSION_BT601_RGB_YCBCR_ENABLE; - - bt2020 = drm_dp_downstream_rgb_to_ycbcr_conversion(intel_dp->dpcd, - intel_dp->downstream_ports, - DP_DS_HDMI_BT2020_RGB_YCBCR_CONV); - bt709 = drm_dp_downstream_rgb_to_ycbcr_conversion(intel_dp->dpcd, - intel_dp->downstream_ports, - DP_DS_HDMI_BT709_RGB_YCBCR_CONV); - switch (crtc_state->infoframes.vsc.colorimetry) { - case DP_COLORIMETRY_BT2020_RGB: - case DP_COLORIMETRY_BT2020_YCC: - if (bt2020) - tmp = DP_CONVERSION_BT2020_RGB_YCBCR_ENABLE; - break; - case DP_COLORIMETRY_BT709_YCC: - case DP_COLORIMETRY_XVYCC_709: - if (bt709) - tmp = DP_CONVERSION_BT709_RGB_YCBCR_ENABLE; - break; - default: - break; - } - } + tmp = intel_dp->dfp.rgb_to_ycbcr ? + DP_CONVERSION_BT709_RGB_YCBCR_ENABLE : 0; if (drm_dp_pcon_convert_rgb_to_ycbcr(&intel_dp->aux, tmp) < 0) drm_dbg_kms(&i915->drm, "Failed to %s protocol converter RGB->YCbCr conversion mode\n", - enabledisable(tmp)); + str_enable_disable(tmp)); } @@ -2572,9 +2705,9 @@ static void intel_edp_mso_mode_fixup(struct intel_connector *connector, drm_mode_set_name(mode); drm_dbg_kms(&i915->drm, - "[CONNECTOR:%d:%s] using generated MSO mode: ", - connector->base.base.id, connector->base.name); - drm_mode_debug_printmodeline(mode); + "[CONNECTOR:%d:%s] using generated MSO mode: " DRM_MODE_FMT "\n", + connector->base.base.id, connector->base.name, + DRM_MODE_ARG(mode)); } static void intel_edp_mso_init(struct intel_dp *intel_dp) @@ -2787,8 +2920,9 @@ intel_dp_configure_mst(struct intel_dp *intel_dp) drm_dbg_kms(&i915->drm, "[ENCODER:%d:%s] MST support: port: %s, sink: %s, modparam: %s\n", encoder->base.base.id, encoder->base.name, - yesno(intel_dp_mst_source_support(intel_dp)), yesno(sink_can_mst), - yesno(i915->params.enable_dp_mst)); + str_yes_no(intel_dp_mst_source_support(intel_dp)), + str_yes_no(sink_can_mst), + str_yes_no(i915->params.enable_dp_mst)); if (!intel_dp_mst_source_support(intel_dp)) return; @@ -4347,9 +4481,7 @@ intel_dp_update_420(struct intel_dp *intel_dp) intel_dp->downstream_ports); rgb_to_ycbcr = drm_dp_downstream_rgb_to_ycbcr_conversion(intel_dp->dpcd, intel_dp->downstream_ports, - DP_DS_HDMI_BT601_RGB_YCBCR_CONV | - DP_DS_HDMI_BT709_RGB_YCBCR_CONV | - DP_DS_HDMI_BT2020_RGB_YCBCR_CONV); + DP_DS_HDMI_BT709_RGB_YCBCR_CONV); if (DISPLAY_VER(i915) >= 11) { /* Let PCON convert from RGB->YCbCr if possible */ @@ -4375,9 +4507,9 @@ intel_dp_update_420(struct intel_dp *intel_dp) drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] RGB->YcbCr conversion? %s, YCbCr 4:2:0 allowed? %s, YCbCr 4:4:4->4:2:0 conversion? %s\n", connector->base.base.id, connector->base.name, - yesno(intel_dp->dfp.rgb_to_ycbcr), - yesno(connector->base.ycbcr_420_allowed), - yesno(intel_dp->dfp.ycbcr_444_to_420)); + str_yes_no(intel_dp->dfp.rgb_to_ycbcr), + str_yes_no(connector->base.ycbcr_420_allowed), + str_yes_no(intel_dp->dfp.ycbcr_444_to_420)); } static void @@ -4586,17 +4718,8 @@ static int intel_dp_get_modes(struct drm_connector *connector) num_modes = intel_connector_update_modes(connector, edid); /* Also add fixed mode, which may or may not be present in EDID */ - if (intel_dp_is_edp(intel_attached_dp(intel_connector)) && - intel_connector->panel.fixed_mode) { - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, - intel_connector->panel.fixed_mode); - if (mode) { - drm_mode_probed_add(connector, mode); - num_modes++; - } - } + if (intel_dp_is_edp(intel_attached_dp(intel_connector))) + num_modes += intel_panel_get_modes(intel_connector); if (num_modes) return num_modes; @@ -4648,9 +4771,7 @@ intel_dp_connector_register(struct drm_connector *connector) if (lspcon_init(dig_port)) { lspcon_detect_hdr_capability(lspcon); if (lspcon->hdr_supported) - drm_object_attach_property(&connector->base, - connector->dev->mode_config.hdr_output_metadata_property, - 0); + drm_connector_attach_hdr_output_metadata_property(connector); } return ret; @@ -4919,6 +5040,25 @@ bool intel_dp_is_port_edp(struct drm_i915_private *dev_priv, enum port port) return intel_bios_is_port_edp(dev_priv, port); } +static bool +has_gamut_metadata_dip(struct drm_i915_private *i915, enum port port) +{ + if (intel_bios_is_lspcon_present(i915, port)) + return false; + + if (DISPLAY_VER(i915) >= 11) + return true; + + if (port == PORT_A) + return false; + + if (IS_HASWELL(i915) || IS_BROADWELL(i915) || + DISPLAY_VER(i915) >= 9) + return true; + + return false; +} + static void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector) { @@ -4945,10 +5085,8 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect intel_attach_dp_colorspace_property(connector); } - if (IS_GEMINILAKE(dev_priv) || DISPLAY_VER(dev_priv) >= 11) - drm_object_attach_property(&connector->base, - connector->dev->mode_config.hdr_output_metadata_property, - 0); + if (has_gamut_metadata_dip(dev_priv, port)) + drm_connector_attach_hdr_output_metadata_property(connector); if (intel_dp_is_edp(intel_dp)) { u32 allowed_scalers; @@ -4967,14 +5105,30 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect drm_connector_attach_vrr_capable_property(connector); } +static void +intel_edp_add_properties(struct intel_dp *intel_dp) +{ + struct intel_connector *connector = intel_dp->attached_connector; + struct drm_i915_private *i915 = to_i915(connector->base.dev); + const struct drm_display_mode *fixed_mode = + intel_panel_preferred_fixed_mode(connector); + + if (!fixed_mode) + return; + + drm_connector_set_panel_orientation_with_quirk(&connector->base, + i915->vbt.orientation, + fixed_mode->hdisplay, + fixed_mode->vdisplay); +} + static bool intel_edp_init_connector(struct intel_dp *intel_dp, struct intel_connector *intel_connector) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct drm_device *dev = &dev_priv->drm; struct drm_connector *connector = &intel_connector->base; - struct drm_display_mode *fixed_mode = NULL; - struct drm_display_mode *downclock_mode = NULL; + struct drm_display_mode *fixed_mode; bool has_dpcd; enum pipe pipe = INVALID_PIPE; struct edid *edid; @@ -5031,20 +5185,20 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } intel_connector->edid = edid; - fixed_mode = intel_panel_edid_fixed_mode(intel_connector); - if (fixed_mode) - downclock_mode = intel_drrs_init(intel_connector, fixed_mode); + intel_panel_add_edid_fixed_modes(intel_connector, + dev_priv->vbt.drrs_type != DRRS_TYPE_NONE); /* MSO requires information from the EDID */ intel_edp_mso_init(intel_dp); /* multiply the mode clock and horizontal timings for MSO */ - intel_edp_mso_mode_fixup(intel_connector, fixed_mode); - intel_edp_mso_mode_fixup(intel_connector, downclock_mode); + list_for_each_entry(fixed_mode, &intel_connector->panel.fixed_modes, head) + intel_edp_mso_mode_fixup(intel_connector, fixed_mode); /* fallback to VBT if available for eDP */ - if (!fixed_mode) - fixed_mode = intel_panel_vbt_fixed_mode(intel_connector); + if (!intel_panel_preferred_fixed_mode(intel_connector)) + intel_panel_add_vbt_lfp_fixed_mode(intel_connector); + mutex_unlock(&dev->mode_config.mutex); if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { @@ -5066,16 +5220,13 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, pipe_name(pipe)); } - intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); + intel_panel_init(intel_connector); + if (!(dev_priv->quirks & QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK)) intel_connector->panel.backlight.power = intel_pps_backlight_power; intel_backlight_setup(intel_connector, pipe); - if (fixed_mode) { - drm_connector_set_panel_orientation_with_quirk(connector, - dev_priv->vbt.orientation, - fixed_mode->hdisplay, fixed_mode->vdisplay); - } + intel_edp_add_properties(intel_dp); return true; diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 82d024dafe7b..a7640dbcf00e 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -6,9 +6,9 @@ * Sean Paul <seanpaul@chromium.org> */ -#include <drm/dp/drm_dp_helper.h> -#include <drm/dp/drm_dp_mst_helper.h> -#include <drm/drm_hdcp.h> +#include <drm/display/drm_dp_helper.h> +#include <drm/display/drm_dp_mst_helper.h> +#include <drm/display/drm_hdcp_helper.h> #include <drm/drm_print.h> #include "intel_ddi.h" diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 5d98773efd1b..9feaf1a589f3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -55,6 +55,7 @@ static u8 *intel_dp_lttpr_phy_caps(struct intel_dp *intel_dp, } static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], enum drm_dp_phy dp_phy) { struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; @@ -63,7 +64,7 @@ static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp, intel_dp_phy_name(dp_phy, phy_name, sizeof(phy_name)); - if (drm_dp_read_lttpr_phy_caps(&intel_dp->aux, dp_phy, phy_caps) < 0) { + if (drm_dp_read_lttpr_phy_caps(&intel_dp->aux, dpcd, dp_phy, phy_caps) < 0) { drm_dbg_kms(&dp_to_i915(intel_dp)->drm, "[ENCODER:%d:%s][%s] failed to read the PHY caps\n", encoder->base.base.id, encoder->base.name, phy_name); @@ -77,23 +78,15 @@ static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp, phy_caps); } -static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp) +static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp, + const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - - if (intel_dp_is_edp(intel_dp)) - return false; - - /* - * Detecting LTTPRs must be avoided on platforms with an AUX timeout - * period < 3.2ms. (see DP Standard v2.0, 2.11.2, 3.6.6.1). - */ - if (DISPLAY_VER(i915) < 10 || IS_GEMINILAKE(i915)) - return false; + int ret; - if (drm_dp_read_lttpr_common_caps(&intel_dp->aux, - intel_dp->lttpr_common_caps) < 0) + ret = drm_dp_read_lttpr_common_caps(&intel_dp->aux, dpcd, + intel_dp->lttpr_common_caps); + if (ret < 0) goto reset_caps; drm_dbg_kms(&dp_to_i915(intel_dp)->drm, @@ -122,14 +115,14 @@ intel_dp_set_lttpr_transparent_mode(struct intel_dp *intel_dp, bool enable) return drm_dp_dpcd_write(&intel_dp->aux, DP_PHY_REPEATER_MODE, &val, 1) == 1; } -static int intel_dp_init_lttpr(struct intel_dp *intel_dp) +static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; struct drm_i915_private *i915 = to_i915(encoder->base.dev); int lttpr_count; int i; - if (!intel_dp_read_lttpr_common_caps(intel_dp)) + if (!intel_dp_read_lttpr_common_caps(intel_dp, dpcd)) return 0; lttpr_count = drm_dp_lttpr_count(intel_dp->lttpr_common_caps); @@ -168,7 +161,7 @@ static int intel_dp_init_lttpr(struct intel_dp *intel_dp) } for (i = 0; i < lttpr_count; i++) - intel_dp_read_lttpr_phy_caps(intel_dp, DP_PHY_LTTPR(i)); + intel_dp_read_lttpr_phy_caps(intel_dp, dpcd, DP_PHY_LTTPR(i)); return lttpr_count; } @@ -193,9 +186,30 @@ static int intel_dp_init_lttpr(struct intel_dp *intel_dp) */ int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp) { - int lttpr_count = intel_dp_init_lttpr(intel_dp); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + int lttpr_count = 0; - /* The DPTX shall read the DPRX caps after LTTPR detection. */ + /* + * Detecting LTTPRs must be avoided on platforms with an AUX timeout + * period < 3.2ms. (see DP Standard v2.0, 2.11.2, 3.6.6.1). + */ + if (!intel_dp_is_edp(intel_dp) && + (DISPLAY_VER(i915) >= 10 && !IS_GEMINILAKE(i915))) { + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + + if (drm_dp_dpcd_probe(&intel_dp->aux, DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV)) + return -EIO; + + if (drm_dp_read_dpcd_caps(&intel_dp->aux, dpcd)) + return -EIO; + + lttpr_count = intel_dp_init_lttpr(intel_dp, dpcd); + } + + /* + * The DPTX shall read the DPRX caps after LTTPR detection, so re-read + * it here. + */ if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd)) { intel_dp_reset_lttpr_common_caps(intel_dp); return -EIO; diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index dc1556b46b85..7fa1c0833096 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -6,7 +6,7 @@ #ifndef __INTEL_DP_LINK_TRAINING_H__ #define __INTEL_DP_LINK_TRAINING_H__ -#include <drm/dp/drm_dp_helper.h> +#include <drm/display/drm_dp_helper.h> struct intel_crtc_state; struct intel_dp; diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index e30e698aa684..061b277e5ce7 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -398,9 +398,8 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state, if (ret) { drm_dbg_kms(&i915->drm, "failed to update payload %d\n", ret); } - if (old_crtc_state->has_audio) - intel_audio_codec_disable(encoder, - old_crtc_state, old_conn_state); + + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); } static void intel_mst_post_disable_dp(struct intel_atomic_state *state, @@ -599,8 +598,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, intel_crtc_vblank_on(pipe_config); - if (pipe_config->has_audio) - intel_audio_codec_enable(encoder, pipe_config, conn_state); + intel_audio_codec_enable(encoder, pipe_config, conn_state); /* Enable hdcp if it's desired */ if (conn_state->content_protection == diff --git a/drivers/gpu/drm/i915/display/intel_dpio_phy.c b/drivers/gpu/drm/i915/display/intel_dpio_phy.c index 44edeb2e55c0..cc6abe761f5e 100644 --- a/drivers/gpu/drm/i915/display/intel_dpio_phy.c +++ b/drivers/gpu/drm/i915/display/intel_dpio_phy.c @@ -24,6 +24,7 @@ #include "intel_ddi.h" #include "intel_ddi_buf_trans.h" #include "intel_de.h" +#include "intel_display_power_well.h" #include "intel_display_types.h" #include "intel_dp.h" #include "intel_dpio_phy.h" diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 14f5ffe27d05..6eef0b8a91eb 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -4,6 +4,7 @@ */ #include <linux/kernel.h> +#include <linux/string_helpers.h> #include "intel_crtc.h" #include "intel_de.h" @@ -17,7 +18,10 @@ #include "vlv_sideband.h" struct intel_dpll_funcs { - int (*crtc_compute_clock)(struct intel_crtc_state *crtc_state); + int (*crtc_compute_clock)(struct intel_atomic_state *state, + struct intel_crtc *crtc); + int (*crtc_get_shared_dpll)(struct intel_atomic_state *state, + struct intel_crtc *crtc); }; struct intel_limit { @@ -253,12 +257,12 @@ static const struct intel_limit ilk_limits_dual_lvds_100m = { static const struct intel_limit intel_limits_vlv = { /* - * These are the data rate limits (measured in fast clocks) + * These are based on the data rate limits (measured in fast clocks) * since those are the strictest limits we have. The fast * clock and actual rate limits are more relaxed, so checking * them would make no difference. */ - .dot = { .min = 25000 * 5, .max = 270000 * 5 }, + .dot = { .min = 25000, .max = 270000 }, .vco = { .min = 4000000, .max = 6000000 }, .n = { .min = 1, .max = 7 }, .m1 = { .min = 2, .max = 3 }, @@ -269,12 +273,12 @@ static const struct intel_limit intel_limits_vlv = { static const struct intel_limit intel_limits_chv = { /* - * These are the data rate limits (measured in fast clocks) + * These are based on the data rate limits (measured in fast clocks) * since those are the strictest limits we have. The fast * clock and actual rate limits are more relaxed, so checking * them would make no difference. */ - .dot = { .min = 25000 * 5, .max = 540000 * 5}, + .dot = { .min = 25000, .max = 540000 }, .vco = { .min = 4800000, .max = 6480000 }, .n = { .min = 1, .max = 1 }, .m1 = { .min = 2, .max = 2 }, @@ -284,8 +288,7 @@ static const struct intel_limit intel_limits_chv = { }; static const struct intel_limit intel_limits_bxt = { - /* FIXME: find real dot limits */ - .dot = { .min = 0, .max = INT_MAX }, + .dot = { .min = 25000, .max = 594000 }, .vco = { .min = 4800000, .max = 6700000 }, .n = { .min = 1, .max = 1 }, .m1 = { .min = 2, .max = 2 }, @@ -336,26 +339,26 @@ int i9xx_calc_dpll_params(int refclk, struct dpll *clock) int vlv_calc_dpll_params(int refclk, struct dpll *clock) { clock->m = clock->m1 * clock->m2; - clock->p = clock->p1 * clock->p2; + clock->p = clock->p1 * clock->p2 * 5; if (WARN_ON(clock->n == 0 || clock->p == 0)) return 0; clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); - return clock->dot / 5; + return clock->dot; } int chv_calc_dpll_params(int refclk, struct dpll *clock) { clock->m = clock->m1 * clock->m2; - clock->p = clock->p1 * clock->p2; + clock->p = clock->p1 * clock->p2 * 5; if (WARN_ON(clock->n == 0 || clock->p == 0)) return 0; clock->vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, clock->m), clock->n << 22); clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); - return clock->dot / 5; + return clock->dot; } /* @@ -424,8 +427,7 @@ i9xx_select_p2_div(const struct intel_limit *limit, /* * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * refclk, or FALSE. * * Target and reference clocks are specified in kHz. * @@ -483,8 +485,7 @@ i9xx_find_best_dpll(const struct intel_limit *limit, /* * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * refclk, or FALSE. * * Target and reference clocks are specified in kHz. * @@ -540,8 +541,7 @@ pnv_find_best_dpll(const struct intel_limit *limit, /* * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * refclk, or FALSE. * * Target and reference clocks are specified in kHz. * @@ -640,8 +640,7 @@ static bool vlv_PLL_is_optimal(struct drm_device *dev, int target_freq, /* * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * refclk, or FALSE. */ static bool vlv_find_best_dpll(const struct intel_limit *limit, @@ -658,8 +657,6 @@ vlv_find_best_dpll(const struct intel_limit *limit, int max_n = min(limit->n.max, refclk / 19200); bool found = false; - target *= 5; /* fast clock */ - memset(best_clock, 0, sizeof(*best_clock)); /* based on hardware requirement, prefer smaller n to precision */ @@ -667,7 +664,7 @@ vlv_find_best_dpll(const struct intel_limit *limit, for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow; clock.p2 -= clock.p2 > 10 ? 2 : 1) { - clock.p = clock.p1 * clock.p2; + clock.p = clock.p1 * clock.p2 * 5; /* based on hardware requirement, prefer bigger m1,m2 values */ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { unsigned int ppm; @@ -701,8 +698,7 @@ vlv_find_best_dpll(const struct intel_limit *limit, /* * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * refclk, or FALSE. */ static bool chv_find_best_dpll(const struct intel_limit *limit, @@ -728,7 +724,6 @@ chv_find_best_dpll(const struct intel_limit *limit, */ clock.n = 1; clock.m1 = 2; - target *= 5; /* fast clock */ for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { for (clock.p2 = limit->p2.p2_fast; @@ -736,7 +731,7 @@ chv_find_best_dpll(const struct intel_limit *limit, clock.p2 -= clock.p2 > 10 ? 2 : 1) { unsigned int error_ppm; - clock.p = clock.p1 * clock.p2; + clock.p = clock.p1 * clock.p2 * 5; m2 = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(target, clock.p * clock.n) << 22, refclk * clock.m1); @@ -767,8 +762,8 @@ chv_find_best_dpll(const struct intel_limit *limit, bool bxt_find_best_dpll(struct intel_crtc_state *crtc_state, struct dpll *best_clock) { - int refclk = 100000; const struct intel_limit *limit = &intel_limits_bxt; + int refclk = 100000; return chv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, refclk, @@ -935,32 +930,48 @@ static void i8xx_compute_dpll(struct intel_crtc_state *crtc_state, crtc_state->dpll_hw_state.dpll = dpll; } -static int hsw_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int hsw_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct intel_atomic_state *state = - to_intel_atomic_state(crtc_state->uapi.state); + return 0; +} + +static int hsw_crtc_get_shared_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); struct intel_encoder *encoder = intel_get_crtc_new_encoder(state, crtc_state); - - if (IS_DG2(dev_priv)) - return intel_mpllb_calc_state(crtc_state, encoder); + int ret; if (DISPLAY_VER(dev_priv) < 11 && intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI)) return 0; - if (!intel_reserve_shared_dplls(state, crtc, encoder)) { + ret = intel_reserve_shared_dplls(state, crtc, encoder); + if (ret) { drm_dbg_kms(&dev_priv->drm, "failed to find PLL for pipe %c\n", pipe_name(crtc->pipe)); - return -EINVAL; + return ret; } return 0; } +static int dg2_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct intel_encoder *encoder = + intel_get_crtc_new_encoder(state, crtc_state); + + return intel_mpllb_calc_state(crtc_state, encoder); +} + static bool ilk_needs_fb_cb_tune(const struct dpll *dpll, int factor) { return dpll->m < factor * dpll->n; @@ -1076,18 +1087,15 @@ static void ilk_compute_dpll(struct intel_crtc_state *crtc_state, crtc_state->dpll_hw_state.dpll = dpll; } -static int ilk_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int ilk_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct intel_atomic_state *state = - to_intel_atomic_state(crtc_state->uapi.state); + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); const struct intel_limit *limit; int refclk = 120000; - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ if (!crtc_state->has_pch_encoder) return 0; @@ -1126,11 +1134,27 @@ static int ilk_crtc_compute_clock(struct intel_crtc_state *crtc_state) ilk_compute_dpll(crtc_state, &crtc_state->dpll, &crtc_state->dpll); - if (!intel_reserve_shared_dplls(state, crtc, NULL)) { + return 0; +} + +static int ilk_crtc_get_shared_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + int ret; + + /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ + if (!crtc_state->has_pch_encoder) + return 0; + + ret = intel_reserve_shared_dplls(state, crtc, NULL); + if (ret) { drm_dbg_kms(&dev_priv->drm, "failed to find PLL for pipe %c\n", pipe_name(crtc->pipe)); - return -EINVAL; + return ret; } return 0; @@ -1171,14 +1195,14 @@ void chv_compute_dpll(struct intel_crtc_state *crtc_state) (crtc_state->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; } -static int chv_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int chv_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - int refclk = 100000; + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); const struct intel_limit *limit = &intel_limits_chv; - struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); + int refclk = 100000; if (!crtc_state->clock_set && !chv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, @@ -1192,14 +1216,14 @@ static int chv_crtc_compute_clock(struct intel_crtc_state *crtc_state) return 0; } -static int vlv_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int vlv_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - int refclk = 100000; + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); const struct intel_limit *limit = &intel_limits_vlv; - struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); + int refclk = 100000; if (!crtc_state->clock_set && !vlv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, @@ -1213,16 +1237,15 @@ static int vlv_crtc_compute_clock(struct intel_crtc_state *crtc_state) return 0; } -static int g4x_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int g4x_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); const struct intel_limit *limit; int refclk = 96000; - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { if (intel_panel_use_ssc(dev_priv)) { refclk = dev_priv->vbt.lvds_ssc_freq; @@ -1259,16 +1282,15 @@ static int g4x_crtc_compute_clock(struct intel_crtc_state *crtc_state) return 0; } -static int pnv_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int pnv_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); const struct intel_limit *limit; int refclk = 96000; - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { if (intel_panel_use_ssc(dev_priv)) { refclk = dev_priv->vbt.lvds_ssc_freq; @@ -1296,16 +1318,15 @@ static int pnv_crtc_compute_clock(struct intel_crtc_state *crtc_state) return 0; } -static int i9xx_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int i9xx_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); const struct intel_limit *limit; int refclk = 96000; - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { if (intel_panel_use_ssc(dev_priv)) { refclk = dev_priv->vbt.lvds_ssc_freq; @@ -1333,16 +1354,15 @@ static int i9xx_crtc_compute_clock(struct intel_crtc_state *crtc_state) return 0; } -static int i8xx_crtc_compute_clock(struct intel_crtc_state *crtc_state) +static int i8xx_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); const struct intel_limit *limit; int refclk = 48000; - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { if (intel_panel_use_ssc(dev_priv)) { refclk = dev_priv->vbt.lvds_ssc_freq; @@ -1372,12 +1392,18 @@ static int i8xx_crtc_compute_clock(struct intel_crtc_state *crtc_state) return 0; } +static const struct intel_dpll_funcs dg2_dpll_funcs = { + .crtc_compute_clock = dg2_crtc_compute_clock, +}; + static const struct intel_dpll_funcs hsw_dpll_funcs = { .crtc_compute_clock = hsw_crtc_compute_clock, + .crtc_get_shared_dpll = hsw_crtc_get_shared_dpll, }; static const struct intel_dpll_funcs ilk_dpll_funcs = { .crtc_compute_clock = ilk_crtc_compute_clock, + .crtc_get_shared_dpll = ilk_crtc_get_shared_dpll, }; static const struct intel_dpll_funcs chv_dpll_funcs = { @@ -1404,18 +1430,54 @@ static const struct intel_dpll_funcs i8xx_dpll_funcs = { .crtc_compute_clock = i8xx_crtc_compute_clock, }; -int intel_dpll_crtc_compute_clock(struct intel_crtc_state *crtc_state) +int intel_dpll_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *i915 = to_i915(crtc->base.dev); + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + + drm_WARN_ON(&i915->drm, !intel_crtc_needs_modeset(crtc_state)); + + if (drm_WARN_ON(&i915->drm, crtc_state->shared_dpll)) + return 0; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + if (!crtc_state->hw.enable) + return 0; + + return i915->dpll_funcs->crtc_compute_clock(state, crtc); +} + +int intel_dpll_crtc_get_shared_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + + drm_WARN_ON(&i915->drm, !intel_crtc_needs_modeset(crtc_state)); + + if (drm_WARN_ON(&i915->drm, crtc_state->shared_dpll)) + return 0; - return i915->dpll_funcs->crtc_compute_clock(crtc_state); + if (!crtc_state->hw.enable) + return 0; + + if (!i915->dpll_funcs->crtc_get_shared_dpll) + return 0; + + return i915->dpll_funcs->crtc_get_shared_dpll(state, crtc); } void intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv) { - if (DISPLAY_VER(dev_priv) >= 9 || HAS_DDI(dev_priv)) + if (IS_DG2(dev_priv)) + dev_priv->dpll_funcs = &dg2_dpll_funcs; + else if (DISPLAY_VER(dev_priv) >= 9 || HAS_DDI(dev_priv)) dev_priv->dpll_funcs = &hsw_dpll_funcs; else if (HAS_PCH_SPLIT(dev_priv)) dev_priv->dpll_funcs = &ilk_dpll_funcs; @@ -1945,7 +2007,7 @@ static void assert_pll(struct drm_i915_private *dev_priv, cur_state = intel_de_read(dev_priv, DPLL(pipe)) & DPLL_VCO_ENABLE; I915_STATE_WARN(cur_state != state, "PLL state assertion failure (expected %s, current %s)\n", - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } void assert_pll_enabled(struct drm_i915_private *i915, enum pipe pipe) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.h b/drivers/gpu/drm/i915/display/intel_dpll.h index 69b06a9e473e..bbc30542f29f 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.h +++ b/drivers/gpu/drm/i915/display/intel_dpll.h @@ -10,12 +10,16 @@ struct dpll; struct drm_i915_private; +struct intel_atomic_state; struct intel_crtc; struct intel_crtc_state; enum pipe; void intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv); -int intel_dpll_crtc_compute_clock(struct intel_crtc_state *crtc_state); +int intel_dpll_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc); +int intel_dpll_crtc_get_shared_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc); int vlv_calc_dpll_params(int refclk, struct dpll *clock); int pnv_calc_dpll_params(int refclk, struct dpll *clock); int i9xx_calc_dpll_params(int refclk, struct dpll *clock); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 569903d47aea..22f55574a35c 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -21,6 +21,8 @@ * DEALINGS IN THE SOFTWARE. */ +#include <linux/string_helpers.h> + #include "intel_de.h" #include "intel_display_types.h" #include "intel_dpio_phy.h" @@ -88,9 +90,9 @@ struct intel_shared_dpll_funcs { struct intel_dpll_mgr { const struct dpll_info *dpll_info; - bool (*get_dplls)(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder); + int (*get_dplls)(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder); void (*put_dplls)(struct intel_atomic_state *state, struct intel_crtc *crtc); void (*update_active_dpll)(struct intel_atomic_state *state, @@ -178,13 +180,14 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_dpll_hw_state hw_state; if (drm_WARN(&dev_priv->drm, !pll, - "asserting DPLL %s with no DPLL\n", onoff(state))) + "asserting DPLL %s with no DPLL\n", str_on_off(state))) return; cur_state = intel_dpll_get_hw_state(dev_priv, pll, &hw_state); I915_STATE_WARN(cur_state != state, "%s assertion failure (expected %s, current %s)\n", - pll->info->name, onoff(state), onoff(cur_state)); + pll->info->name, str_on_off(state), + str_on_off(cur_state)); } static enum tc_port icl_pll_id_to_tc_port(enum intel_dpll_id id) @@ -511,9 +514,9 @@ static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv, udelay(200); } -static bool ibx_get_dpll(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +static int ibx_get_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); @@ -538,7 +541,7 @@ static bool ibx_get_dpll(struct intel_atomic_state *state, } if (!pll) - return false; + return -EINVAL; /* reference the pll */ intel_reference_shared_dpll(state, crtc, @@ -546,7 +549,7 @@ static bool ibx_get_dpll(struct intel_atomic_state *state, crtc_state->shared_dpll = pll; - return true; + return 0; } static void ibx_dump_hw_state(struct drm_i915_private *dev_priv, @@ -581,7 +584,7 @@ static const struct intel_dpll_mgr pch_pll_mgr = { }; static void hsw_ddi_wrpll_enable(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll) + struct intel_shared_dpll *pll) { const enum intel_dpll_id id = pll->info->id; @@ -832,7 +835,7 @@ hsw_ddi_calculate_wrpll(int clock /* in Hz */, { u64 freq2k; unsigned p, n2, r2; - struct hsw_wrpll_rnp best = { 0, 0, 0 }; + struct hsw_wrpll_rnp best = {}; unsigned budget; freq2k = clock / 100; @@ -1057,16 +1060,13 @@ static int hsw_ddi_spll_get_freq(struct drm_i915_private *i915, return link_clock * 2; } -static bool hsw_get_dpll(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +static int hsw_get_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); - struct intel_shared_dpll *pll; - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); + struct intel_shared_dpll *pll = NULL; if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) pll = hsw_ddi_wrpll_get_dpll(state, crtc); @@ -1074,18 +1074,16 @@ static bool hsw_get_dpll(struct intel_atomic_state *state, pll = hsw_ddi_lcpll_get_dpll(crtc_state); else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG)) pll = hsw_ddi_spll_get_dpll(state, crtc); - else - return false; if (!pll) - return false; + return -EINVAL; intel_reference_shared_dpll(state, crtc, pll, &crtc_state->dpll_hw_state); crtc_state->shared_dpll = pll; - return true; + return 0; } static void hsw_update_dpll_ref_clks(struct drm_i915_private *i915) @@ -1330,13 +1328,6 @@ struct skl_wrpll_context { unsigned int p; /* chosen divider */ }; -static void skl_wrpll_context_init(struct skl_wrpll_context *ctx) -{ - memset(ctx, 0, sizeof(*ctx)); - - ctx->min_deviation = U64_MAX; -} - /* DCO freq must be within +1%/-6% of the DCO central freq */ #define SKL_DCO_MAX_PDEVIATION 100 #define SKL_DCO_MAX_NDEVIATION 600 @@ -1497,33 +1488,33 @@ static void skl_wrpll_params_populate(struct skl_wrpll_params *params, params->dco_integer * MHz(1)) * 0x8000, MHz(1)); } -static bool +static int skl_ddi_calculate_wrpll(int clock /* in Hz */, int ref_clock, struct skl_wrpll_params *wrpll_params) { - u64 afe_clock = clock * 5; /* AFE Clock is 5x Pixel clock */ - u64 dco_central_freq[3] = { 8400000000ULL, - 9000000000ULL, - 9600000000ULL }; - static const int even_dividers[] = { 4, 6, 8, 10, 12, 14, 16, 18, 20, - 24, 28, 30, 32, 36, 40, 42, 44, - 48, 52, 54, 56, 60, 64, 66, 68, - 70, 72, 76, 78, 80, 84, 88, 90, - 92, 96, 98 }; - static const int odd_dividers[] = { 3, 5, 7, 9, 15, 21, 35 }; + static const u64 dco_central_freq[3] = { 8400000000ULL, + 9000000000ULL, + 9600000000ULL }; + static const u8 even_dividers[] = { 4, 6, 8, 10, 12, 14, 16, 18, 20, + 24, 28, 30, 32, 36, 40, 42, 44, + 48, 52, 54, 56, 60, 64, 66, 68, + 70, 72, 76, 78, 80, 84, 88, 90, + 92, 96, 98 }; + static const u8 odd_dividers[] = { 3, 5, 7, 9, 15, 21, 35 }; static const struct { - const int *list; + const u8 *list; int n_dividers; } dividers[] = { { even_dividers, ARRAY_SIZE(even_dividers) }, { odd_dividers, ARRAY_SIZE(odd_dividers) }, }; - struct skl_wrpll_context ctx; + struct skl_wrpll_context ctx = { + .min_deviation = U64_MAX, + }; unsigned int dco, d, i; unsigned int p0, p1, p2; - - skl_wrpll_context_init(&ctx); + u64 afe_clock = clock * 5; /* AFE Clock is 5x Pixel clock */ for (d = 0; d < ARRAY_SIZE(dividers); d++) { for (dco = 0; dco < ARRAY_SIZE(dco_central_freq); dco++) { @@ -1556,7 +1547,7 @@ skip_remaining_dividers: if (!ctx.p) { DRM_DEBUG_DRIVER("No valid divider found for %dHz\n", clock); - return false; + return -EINVAL; } /* @@ -1568,14 +1559,15 @@ skip_remaining_dividers: skl_wrpll_params_populate(wrpll_params, afe_clock, ref_clock, ctx.central_freq, p0, p1, p2); - return true; + return 0; } -static bool skl_ddi_hdmi_pll_dividers(struct intel_crtc_state *crtc_state) +static int skl_ddi_hdmi_pll_dividers(struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); + struct skl_wrpll_params wrpll_params = {}; u32 ctrl1, cfgcr1, cfgcr2; - struct skl_wrpll_params wrpll_params = { 0, }; + int ret; /* * See comment in intel_dpll_hw_state to understand why we always use 0 @@ -1585,10 +1577,10 @@ static bool skl_ddi_hdmi_pll_dividers(struct intel_crtc_state *crtc_state) ctrl1 |= DPLL_CTRL1_HDMI_MODE(0); - if (!skl_ddi_calculate_wrpll(crtc_state->port_clock * 1000, - i915->dpll.ref_clks.nssc, - &wrpll_params)) - return false; + ret = skl_ddi_calculate_wrpll(crtc_state->port_clock * 1000, + i915->dpll.ref_clks.nssc, &wrpll_params); + if (ret) + return ret; cfgcr1 = DPLL_CFGCR1_FREQ_ENABLE | DPLL_CFGCR1_DCO_FRACTION(wrpll_params.dco_fraction) | @@ -1600,13 +1592,11 @@ static bool skl_ddi_hdmi_pll_dividers(struct intel_crtc_state *crtc_state) DPLL_CFGCR2_PDIV(wrpll_params.pdiv) | wrpll_params.central_freq; - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - crtc_state->dpll_hw_state.ctrl1 = ctrl1; crtc_state->dpll_hw_state.cfgcr1 = cfgcr1; crtc_state->dpll_hw_state.cfgcr2 = cfgcr2; - return true; + + return 0; } static int skl_ddi_wrpll_get_freq(struct drm_i915_private *i915, @@ -1680,7 +1670,7 @@ static int skl_ddi_wrpll_get_freq(struct drm_i915_private *i915, return dco_freq / (p0 * p1 * p2 * 5); } -static bool +static int skl_ddi_dp_set_dpll_hw_state(struct intel_crtc_state *crtc_state) { u32 ctrl1; @@ -1712,12 +1702,9 @@ skl_ddi_dp_set_dpll_hw_state(struct intel_crtc_state *crtc_state) break; } - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - crtc_state->dpll_hw_state.ctrl1 = ctrl1; - return true; + return 0; } static int skl_ddi_lcpll_get_freq(struct drm_i915_private *i915, @@ -1754,33 +1741,23 @@ static int skl_ddi_lcpll_get_freq(struct drm_i915_private *i915, return link_clock * 2; } -static bool skl_get_dpll(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +static int skl_get_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); - struct drm_i915_private *i915 = to_i915(crtc->base.dev); struct intel_shared_dpll *pll; - bool bret; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) { - bret = skl_ddi_hdmi_pll_dividers(crtc_state); - if (!bret) { - drm_dbg_kms(&i915->drm, - "Could not get HDMI pll dividers.\n"); - return false; - } - } else if (intel_crtc_has_dp_encoder(crtc_state)) { - bret = skl_ddi_dp_set_dpll_hw_state(crtc_state); - if (!bret) { - drm_dbg_kms(&i915->drm, - "Could not set DP dpll HW state.\n"); - return false; - } - } else { - return false; - } + int ret; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) + ret = skl_ddi_hdmi_pll_dividers(crtc_state); + else if (intel_crtc_has_dp_encoder(crtc_state)) + ret = skl_ddi_dp_set_dpll_hw_state(crtc_state); + else + ret = -EINVAL; + if (ret) + return ret; if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) pll = intel_find_shared_dpll(state, crtc, @@ -1793,14 +1770,14 @@ static bool skl_get_dpll(struct intel_atomic_state *state, BIT(DPLL_ID_SKL_DPLL2) | BIT(DPLL_ID_SKL_DPLL1)); if (!pll) - return false; + return -EINVAL; intel_reference_shared_dpll(state, crtc, pll, &crtc_state->dpll_hw_state); crtc_state->shared_dpll = pll; - return true; + return 0; } static int skl_ddi_pll_get_freq(struct drm_i915_private *i915, @@ -1902,7 +1879,7 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv, /* Write M2 integer */ temp = intel_de_read(dev_priv, BXT_PORT_PLL(phy, ch, 0)); - temp &= ~PORT_PLL_M2_MASK; + temp &= ~PORT_PLL_M2_INT_MASK; temp |= pll->state.hw_state.pll0; intel_de_write(dev_priv, BXT_PORT_PLL(phy, ch, 0), temp); @@ -2038,7 +2015,7 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, hw_state->ebb4 &= PORT_PLL_10BIT_CLK_ENABLE; hw_state->pll0 = intel_de_read(dev_priv, BXT_PORT_PLL(phy, ch, 0)); - hw_state->pll0 &= PORT_PLL_M2_MASK; + hw_state->pll0 &= PORT_PLL_M2_INT_MASK; hw_state->pll1 = intel_de_read(dev_priv, BXT_PORT_PLL(phy, ch, 1)); hw_state->pll1 &= PORT_PLL_N_MASK; @@ -2087,82 +2064,64 @@ out: return ret; } -/* bxt clock parameters */ -struct bxt_clk_div { - int clock; - u32 p1; - u32 p2; - u32 m2_int; - u32 m2_frac; - bool m2_frac_en; - u32 n; - - int vco; -}; - /* pre-calculated values for DP linkrates */ -static const struct bxt_clk_div bxt_dp_clk_val[] = { - {162000, 4, 2, 32, 1677722, 1, 1}, - {270000, 4, 1, 27, 0, 0, 1}, - {540000, 2, 1, 27, 0, 0, 1}, - {216000, 3, 2, 32, 1677722, 1, 1}, - {243000, 4, 1, 24, 1258291, 1, 1}, - {324000, 4, 1, 32, 1677722, 1, 1}, - {432000, 3, 1, 32, 1677722, 1, 1} +static const struct dpll bxt_dp_clk_val[] = { + /* m2 is .22 binary fixed point */ + { .dot = 162000, .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a /* 32.4 */ }, + { .dot = 270000, .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 /* 27.0 */ }, + { .dot = 540000, .p1 = 2, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 /* 27.0 */ }, + { .dot = 216000, .p1 = 3, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a /* 32.4 */ }, + { .dot = 243000, .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6133333 /* 24.3 */ }, + { .dot = 324000, .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x819999a /* 32.4 */ }, + { .dot = 432000, .p1 = 3, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x819999a /* 32.4 */ }, }; -static bool +static int bxt_ddi_hdmi_pll_dividers(struct intel_crtc_state *crtc_state, - struct bxt_clk_div *clk_div) + struct dpll *clk_div) { struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct dpll best_clock; /* Calculate HDMI div */ /* * FIXME: tie the following calculation into * i9xx_crtc_compute_clock */ - if (!bxt_find_best_dpll(crtc_state, &best_clock)) { + if (!bxt_find_best_dpll(crtc_state, clk_div)) { drm_dbg(&i915->drm, "no PLL dividers found for clock %d pipe %c\n", crtc_state->port_clock, pipe_name(crtc->pipe)); - return false; + return -EINVAL; } - clk_div->p1 = best_clock.p1; - clk_div->p2 = best_clock.p2; - drm_WARN_ON(&i915->drm, best_clock.m1 != 2); - clk_div->n = best_clock.n; - clk_div->m2_int = best_clock.m2 >> 22; - clk_div->m2_frac = best_clock.m2 & ((1 << 22) - 1); - clk_div->m2_frac_en = clk_div->m2_frac != 0; + drm_WARN_ON(&i915->drm, clk_div->m1 != 2); - clk_div->vco = best_clock.vco; - - return true; + return 0; } static void bxt_ddi_dp_pll_dividers(struct intel_crtc_state *crtc_state, - struct bxt_clk_div *clk_div) + struct dpll *clk_div) { - int clock = crtc_state->port_clock; + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); int i; *clk_div = bxt_dp_clk_val[0]; for (i = 0; i < ARRAY_SIZE(bxt_dp_clk_val); ++i) { - if (bxt_dp_clk_val[i].clock == clock) { + if (crtc_state->port_clock == bxt_dp_clk_val[i].dot) { *clk_div = bxt_dp_clk_val[i]; break; } } - clk_div->vco = clock * 10 / 2 * clk_div->p1 * clk_div->p2; + chv_calc_dpll_params(i915->dpll.ref_clks.nssc, clk_div); + + drm_WARN_ON(&i915->drm, clk_div->vco == 0 || + clk_div->dot != crtc_state->port_clock); } -static bool bxt_ddi_set_dpll_hw_state(struct intel_crtc_state *crtc_state, - const struct bxt_clk_div *clk_div) +static int bxt_ddi_set_dpll_hw_state(struct intel_crtc_state *crtc_state, + const struct dpll *clk_div) { struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); struct intel_dpll_hw_state *dpll_hw_state = &crtc_state->dpll_hw_state; @@ -2171,8 +2130,6 @@ static bool bxt_ddi_set_dpll_hw_state(struct intel_crtc_state *crtc_state, u32 prop_coef, int_coef, gain_ctl, targ_cnt; u32 lanestagger; - memset(dpll_hw_state, 0, sizeof(*dpll_hw_state)); - if (vco >= 6200000 && vco <= 6700000) { prop_coef = 4; int_coef = 9; @@ -2191,7 +2148,7 @@ static bool bxt_ddi_set_dpll_hw_state(struct intel_crtc_state *crtc_state, targ_cnt = 9; } else { drm_err(&i915->drm, "Invalid VCO\n"); - return false; + return -EINVAL; } if (clock > 270000) @@ -2206,45 +2163,45 @@ static bool bxt_ddi_set_dpll_hw_state(struct intel_crtc_state *crtc_state, lanestagger = 0x02; dpll_hw_state->ebb0 = PORT_PLL_P1(clk_div->p1) | PORT_PLL_P2(clk_div->p2); - dpll_hw_state->pll0 = clk_div->m2_int; + dpll_hw_state->pll0 = PORT_PLL_M2_INT(clk_div->m2 >> 22); dpll_hw_state->pll1 = PORT_PLL_N(clk_div->n); - dpll_hw_state->pll2 = clk_div->m2_frac; + dpll_hw_state->pll2 = PORT_PLL_M2_FRAC(clk_div->m2 & 0x3fffff); - if (clk_div->m2_frac_en) + if (clk_div->m2 & 0x3fffff) dpll_hw_state->pll3 = PORT_PLL_M2_FRAC_ENABLE; - dpll_hw_state->pll6 = prop_coef | PORT_PLL_INT_COEFF(int_coef); - dpll_hw_state->pll6 |= PORT_PLL_GAIN_CTL(gain_ctl); + dpll_hw_state->pll6 = PORT_PLL_PROP_COEFF(prop_coef) | + PORT_PLL_INT_COEFF(int_coef) | + PORT_PLL_GAIN_CTL(gain_ctl); - dpll_hw_state->pll8 = targ_cnt; + dpll_hw_state->pll8 = PORT_PLL_TARGET_CNT(targ_cnt); - dpll_hw_state->pll9 = 5 << PORT_PLL_LOCK_THRESHOLD_SHIFT; + dpll_hw_state->pll9 = PORT_PLL_LOCK_THRESHOLD(5); - dpll_hw_state->pll10 = - PORT_PLL_DCO_AMP(PORT_PLL_DCO_AMP_DEFAULT) - | PORT_PLL_DCO_AMP_OVR_EN_H; + dpll_hw_state->pll10 = PORT_PLL_DCO_AMP(15) | + PORT_PLL_DCO_AMP_OVR_EN_H; dpll_hw_state->ebb4 = PORT_PLL_10BIT_CLK_ENABLE; dpll_hw_state->pcsdw12 = LANESTAGGER_STRAP_OVRD | lanestagger; - return true; + return 0; } -static bool +static int bxt_ddi_dp_set_dpll_hw_state(struct intel_crtc_state *crtc_state) { - struct bxt_clk_div clk_div = {}; + struct dpll clk_div = {}; bxt_ddi_dp_pll_dividers(crtc_state, &clk_div); return bxt_ddi_set_dpll_hw_state(crtc_state, &clk_div); } -static bool +static int bxt_ddi_hdmi_set_dpll_hw_state(struct intel_crtc_state *crtc_state) { - struct bxt_clk_div clk_div = {}; + struct dpll clk_div = {}; bxt_ddi_hdmi_pll_dividers(crtc_state, &clk_div); @@ -2258,33 +2215,35 @@ static int bxt_ddi_pll_get_freq(struct drm_i915_private *i915, struct dpll clock; clock.m1 = 2; - clock.m2 = (pll_state->pll0 & PORT_PLL_M2_MASK) << 22; + clock.m2 = REG_FIELD_GET(PORT_PLL_M2_INT_MASK, pll_state->pll0) << 22; if (pll_state->pll3 & PORT_PLL_M2_FRAC_ENABLE) - clock.m2 |= pll_state->pll2 & PORT_PLL_M2_FRAC_MASK; - clock.n = (pll_state->pll1 & PORT_PLL_N_MASK) >> PORT_PLL_N_SHIFT; - clock.p1 = (pll_state->ebb0 & PORT_PLL_P1_MASK) >> PORT_PLL_P1_SHIFT; - clock.p2 = (pll_state->ebb0 & PORT_PLL_P2_MASK) >> PORT_PLL_P2_SHIFT; + clock.m2 |= REG_FIELD_GET(PORT_PLL_M2_FRAC_MASK, pll_state->pll2); + clock.n = REG_FIELD_GET(PORT_PLL_N_MASK, pll_state->pll1); + clock.p1 = REG_FIELD_GET(PORT_PLL_P1_MASK, pll_state->ebb0); + clock.p2 = REG_FIELD_GET(PORT_PLL_P2_MASK, pll_state->ebb0); return chv_calc_dpll_params(i915->dpll.ref_clks.nssc, &clock); } -static bool bxt_get_dpll(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +static int bxt_get_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct intel_shared_dpll *pll; enum intel_dpll_id id; + int ret; - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) && - !bxt_ddi_hdmi_set_dpll_hw_state(crtc_state)) - return false; - - if (intel_crtc_has_dp_encoder(crtc_state) && - !bxt_ddi_dp_set_dpll_hw_state(crtc_state)) - return false; + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) + ret = bxt_ddi_hdmi_set_dpll_hw_state(crtc_state); + else if (intel_crtc_has_dp_encoder(crtc_state)) + ret = bxt_ddi_dp_set_dpll_hw_state(crtc_state); + else + ret = -EINVAL; + if (ret) + return ret; /* 1:1 mapping between ports and PLLs */ id = (enum intel_dpll_id) encoder->port; @@ -2298,7 +2257,7 @@ static bool bxt_get_dpll(struct intel_atomic_state *state, crtc_state->shared_dpll = pll; - return true; + return 0; } static void bxt_update_dpll_ref_clks(struct drm_i915_private *i915) @@ -2535,8 +2494,8 @@ static const struct skl_wrpll_params tgl_tbt_pll_24MHz_values = { /* the following params are unused */ }; -static bool icl_calc_dp_combo_pll(struct intel_crtc_state *crtc_state, - struct skl_wrpll_params *pll_params) +static int icl_calc_dp_combo_pll(struct intel_crtc_state *crtc_state, + struct skl_wrpll_params *pll_params) { struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); const struct icl_combo_pll_params *params = @@ -2549,16 +2508,16 @@ static bool icl_calc_dp_combo_pll(struct intel_crtc_state *crtc_state, for (i = 0; i < ARRAY_SIZE(icl_dp_combo_pll_24MHz_values); i++) { if (clock == params[i].clock) { *pll_params = params[i].wrpll; - return true; + return 0; } } MISSING_CASE(clock); - return false; + return -EINVAL; } -static bool icl_calc_tbt_pll(struct intel_crtc_state *crtc_state, - struct skl_wrpll_params *pll_params) +static int icl_calc_tbt_pll(struct intel_crtc_state *crtc_state, + struct skl_wrpll_params *pll_params) { struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); @@ -2590,7 +2549,7 @@ static bool icl_calc_tbt_pll(struct intel_crtc_state *crtc_state, } } - return true; + return 0; } static int icl_ddi_tbt_pll_get_freq(struct drm_i915_private *i915, @@ -2620,7 +2579,7 @@ static int icl_wrpll_ref_clock(struct drm_i915_private *i915) return ref_clock; } -static bool +static int icl_calc_wrpll(struct intel_crtc_state *crtc_state, struct skl_wrpll_params *wrpll_params) { @@ -2655,13 +2614,13 @@ icl_calc_wrpll(struct intel_crtc_state *crtc_state, } if (best_div == 0) - return false; + return -EINVAL; icl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv); icl_wrpll_params_populate(wrpll_params, best_dco, ref_clock, pdiv, qdiv, kdiv); - return true; + return 0; } static int icl_ddi_combo_pll_get_freq(struct drm_i915_private *i915, @@ -2731,8 +2690,6 @@ static void icl_calc_dpll_state(struct drm_i915_private *i915, { u32 dco_fraction = pll_params->dco_fraction; - memset(pll_state, 0, sizeof(*pll_state)); - if (ehl_combo_pll_div_frac_wa_needed(i915)) dco_fraction = DIV_ROUND_CLOSEST(dco_fraction, 2); @@ -2753,13 +2710,13 @@ static void icl_calc_dpll_state(struct drm_i915_private *i915, pll_state->div0 = TGL_DPLL0_DIV0_AFC_STARTUP(i915->vbt.override_afc_startup_val); } -static bool icl_mg_pll_find_divisors(int clock_khz, bool is_dp, bool use_ssc, - u32 *target_dco_khz, - struct intel_dpll_hw_state *state, - bool is_dkl) +static int icl_mg_pll_find_divisors(int clock_khz, bool is_dp, bool use_ssc, + u32 *target_dco_khz, + struct intel_dpll_hw_state *state, + bool is_dkl) { + static const u8 div1_vals[] = { 7, 5, 3, 2 }; u32 dco_min_freq, dco_max_freq; - int div1_vals[] = {7, 5, 3, 2}; unsigned int i; int div2; @@ -2822,19 +2779,19 @@ static bool icl_mg_pll_find_divisors(int clock_khz, bool is_dp, bool use_ssc, hsdiv | MG_CLKTOP2_HSCLKCTL_DSDIV_RATIO(div2); - return true; + return 0; } } - return false; + return -EINVAL; } /* * The specification for this function uses real numbers, so the math had to be * adapted to integer-only calculation, that's why it looks so different. */ -static bool icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, - struct intel_dpll_hw_state *pll_state) +static int icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, + struct intel_dpll_hw_state *pll_state) { struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); int refclk_khz = dev_priv->dpll.ref_clks.nssc; @@ -2848,14 +2805,14 @@ static bool icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, bool use_ssc = false; bool is_dp = !intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI); bool is_dkl = DISPLAY_VER(dev_priv) >= 12; + int ret; - memset(pll_state, 0, sizeof(*pll_state)); - - if (!icl_mg_pll_find_divisors(clock, is_dp, use_ssc, &dco_khz, - pll_state, is_dkl)) { + ret = icl_mg_pll_find_divisors(clock, is_dp, use_ssc, &dco_khz, + pll_state, is_dkl); + if (ret) { drm_dbg_kms(&dev_priv->drm, "Failed to find divisors for clock %d\n", clock); - return false; + return ret; } m1div = 2; @@ -2870,7 +2827,7 @@ static bool icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, drm_dbg_kms(&dev_priv->drm, "Failed to find mdiv for clock %d\n", clock); - return false; + return -EINVAL; } } m2div_rem = dco_khz % (refclk_khz * m1div); @@ -2897,7 +2854,7 @@ static bool icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, break; default: MISSING_CASE(refclk_khz); - return false; + return -EINVAL; } /* @@ -3040,7 +2997,7 @@ static bool icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, pll_state->mg_pll_bias &= pll_state->mg_pll_bias_mask; } - return true; + return 0; } static int icl_ddi_mg_pll_get_freq(struct drm_i915_private *dev_priv, @@ -3162,9 +3119,9 @@ static u32 intel_get_hti_plls(struct drm_i915_private *i915) return REG_FIELD_GET(HDPORT_DPLL_USED_MASK, i915->hti_state); } -static bool icl_get_combo_phy_dpll(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +static int icl_get_combo_phy_dpll(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); @@ -3182,11 +3139,10 @@ static bool icl_get_combo_phy_dpll(struct intel_atomic_state *state, else ret = icl_calc_dp_combo_pll(crtc_state, &pll_params); - if (!ret) { + if (ret) { drm_dbg_kms(&dev_priv->drm, "Could not calculate combo PHY PLL state.\n"); - - return false; + return ret; } icl_calc_dpll_state(dev_priv, &pll_params, &port_dpll->hw_state); @@ -3231,7 +3187,7 @@ static bool icl_get_combo_phy_dpll(struct intel_atomic_state *state, drm_dbg_kms(&dev_priv->drm, "No combo PHY PLL found for [ENCODER:%d:%s]\n", encoder->base.base.id, encoder->base.name); - return false; + return -EINVAL; } intel_reference_shared_dpll(state, crtc, @@ -3239,12 +3195,12 @@ static bool icl_get_combo_phy_dpll(struct intel_atomic_state *state, icl_update_active_dpll(state, crtc, encoder); - return true; + return 0; } -static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +static int icl_get_tc_phy_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc_state *crtc_state = @@ -3252,12 +3208,14 @@ static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state, struct skl_wrpll_params pll_params = { }; struct icl_port_dpll *port_dpll; enum intel_dpll_id dpll_id; + int ret; port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; - if (!icl_calc_tbt_pll(crtc_state, &pll_params)) { + ret = icl_calc_tbt_pll(crtc_state, &pll_params); + if (ret) { drm_dbg_kms(&dev_priv->drm, "Could not calculate TBT PLL state.\n"); - return false; + return ret; } icl_calc_dpll_state(dev_priv, &pll_params, &port_dpll->hw_state); @@ -3267,14 +3225,15 @@ static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state, BIT(DPLL_ID_ICL_TBTPLL)); if (!port_dpll->pll) { drm_dbg_kms(&dev_priv->drm, "No TBT-ALT PLL found\n"); - return false; + return -EINVAL; } intel_reference_shared_dpll(state, crtc, port_dpll->pll, &port_dpll->hw_state); port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_MG_PHY]; - if (!icl_calc_mg_pll_state(crtc_state, &port_dpll->hw_state)) { + ret = icl_calc_mg_pll_state(crtc_state, &port_dpll->hw_state); + if (ret) { drm_dbg_kms(&dev_priv->drm, "Could not calculate MG PHY PLL state.\n"); goto err_unreference_tbt_pll; @@ -3286,6 +3245,7 @@ static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state, &port_dpll->hw_state, BIT(dpll_id)); if (!port_dpll->pll) { + ret = -EINVAL; drm_dbg_kms(&dev_priv->drm, "No MG PHY PLL found\n"); goto err_unreference_tbt_pll; } @@ -3294,18 +3254,18 @@ static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state, icl_update_active_dpll(state, crtc, encoder); - return true; + return 0; err_unreference_tbt_pll: port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; intel_unreference_shared_dpll(state, crtc, port_dpll->pll); - return false; + return ret; } -static bool icl_get_dplls(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +static int icl_get_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); enum phy phy = intel_port_to_phy(dev_priv, encoder->port); @@ -3317,7 +3277,7 @@ static bool icl_get_dplls(struct intel_atomic_state *state, MISSING_CASE(phy); - return false; + return -EINVAL; } static void icl_put_dplls(struct intel_atomic_state *state, @@ -4103,13 +4063,12 @@ static const struct intel_dpll_mgr adlp_pll_mgr = { /** * intel_shared_dpll_init - Initialize shared DPLLs - * @dev: drm device + * @dev_priv: i915 device * - * Initialize shared DPLLs for @dev. + * Initialize shared DPLLs for @dev_priv. */ -void intel_shared_dpll_init(struct drm_device *dev) +void intel_shared_dpll_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); const struct intel_dpll_mgr *dpll_mgr = NULL; const struct dpll_info *dpll_info; int i; @@ -4148,7 +4107,7 @@ void intel_shared_dpll_init(struct drm_device *dev) dpll_info = dpll_mgr->dpll_info; for (i = 0; dpll_info[i].name; i++) { - drm_WARN_ON(dev, i != dpll_info[i].id); + drm_WARN_ON(&dev_priv->drm, i != dpll_info[i].id); dev_priv->dpll.shared_dplls[i].info = &dpll_info[i]; } @@ -4176,17 +4135,18 @@ void intel_shared_dpll_init(struct drm_device *dev) * intel_release_shared_dplls(). * * Returns: - * True if all required DPLLs were successfully reserved. + * 0 if all required DPLLs were successfully reserved, + * negative error code otherwise. */ -bool intel_reserve_shared_dplls(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder) +int intel_reserve_shared_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); const struct intel_dpll_mgr *dpll_mgr = dev_priv->dpll.mgr; if (drm_WARN_ON(&dev_priv->drm, !dpll_mgr)) - return false; + return -EINVAL; return dpll_mgr->get_dplls(state, crtc, encoder); } diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index ba2fdfce1579..f7c96a1f13c8 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -37,7 +37,6 @@ __a > __b ? (__a - __b) : (__b - __a); }) enum tc_port; -struct drm_device; struct drm_i915_private; struct intel_atomic_state; struct intel_crtc; @@ -337,9 +336,9 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv, bool state); #define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true) #define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false) -bool intel_reserve_shared_dplls(struct intel_atomic_state *state, - struct intel_crtc *crtc, - struct intel_encoder *encoder); +int intel_reserve_shared_dplls(struct intel_atomic_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder); void intel_release_shared_dplls(struct intel_atomic_state *state, struct intel_crtc *crtc); void icl_set_active_port_dpll(struct intel_crtc_state *crtc_state, @@ -356,7 +355,7 @@ bool intel_dpll_get_hw_state(struct drm_i915_private *i915, void intel_enable_shared_dpll(const struct intel_crtc_state *crtc_state); void intel_disable_shared_dpll(const struct intel_crtc_state *crtc_state); void intel_shared_dpll_swap_state(struct intel_atomic_state *state); -void intel_shared_dpll_init(struct drm_device *dev); +void intel_shared_dpll_init(struct drm_i915_private *dev_priv); void intel_dpll_update_ref_clks(struct drm_i915_private *dev_priv); void intel_dpll_readout_hw_state(struct drm_i915_private *dev_priv); void intel_dpll_sanitize_state(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/display/intel_dpt.c b/drivers/gpu/drm/i915/display/intel_dpt.c index 05dd7dba3a5c..fb0e7e79e0cd 100644 --- a/drivers/gpu/drm/i915/display/intel_dpt.c +++ b/drivers/gpu/drm/i915/display/intel_dpt.c @@ -249,7 +249,7 @@ intel_dpt_create(struct intel_framebuffer *fb) size = round_up(size * sizeof(gen8_pte_t), I915_GTT_PAGE_SIZE); if (HAS_LMEM(i915)) - dpt_obj = i915_gem_object_create_lmem(i915, size, 0); + dpt_obj = i915_gem_object_create_lmem(i915, size, I915_BO_ALLOC_CONTIGUOUS); else dpt_obj = i915_gem_object_create_stolen(i915, size); if (IS_ERR(dpt_obj)) @@ -300,5 +300,5 @@ void intel_dpt_destroy(struct i915_address_space *vm) { struct i915_dpt *dpt = i915_vm_to_dpt(vm); - i915_vm_close(&dpt->vm); + i915_vm_put(&dpt->vm); } diff --git a/drivers/gpu/drm/i915/display/intel_drrs.c b/drivers/gpu/drm/i915/display/intel_drrs.c index fa715b8ea310..166caf293f7b 100644 --- a/drivers/gpu/drm/i915/display/intel_drrs.c +++ b/drivers/gpu/drm/i915/display/intel_drrs.c @@ -47,74 +47,36 @@ * requested by userspace. */ -static bool can_enable_drrs(struct intel_connector *connector, - const struct intel_crtc_state *pipe_config) +const char *intel_drrs_type_str(enum drrs_type drrs_type) { - const struct drm_i915_private *i915 = to_i915(connector->base.dev); - - if (pipe_config->vrr.enable) - return false; - - /* - * DRRS and PSR can't be enable together, so giving preference to PSR - * as it allows more power-savings by complete shutting down display, - * so to guarantee this, intel_drrs_compute_config() must be called - * after intel_psr_compute_config(). - */ - if (pipe_config->has_psr) - return false; - - return connector->panel.downclock_mode && - i915->drrs.type == SEAMLESS_DRRS_SUPPORT; -} - -void -intel_drrs_compute_config(struct intel_dp *intel_dp, - struct intel_crtc_state *pipe_config, - int output_bpp, bool constant_n) -{ - struct intel_connector *connector = intel_dp->attached_connector; - struct drm_i915_private *i915 = to_i915(connector->base.dev); - int pixel_clock; - - if (!can_enable_drrs(connector, pipe_config)) { - if (intel_cpu_transcoder_has_m2_n2(i915, pipe_config->cpu_transcoder)) - intel_zero_m_n(&pipe_config->dp_m2_n2); - return; - } - - pipe_config->has_drrs = true; - - pixel_clock = connector->panel.downclock_mode->clock; - if (pipe_config->splitter.enable) - pixel_clock /= pipe_config->splitter.link_count; + static const char * const str[] = { + [DRRS_TYPE_NONE] = "none", + [DRRS_TYPE_STATIC] = "static", + [DRRS_TYPE_SEAMLESS] = "seamless", + }; - intel_link_compute_m_n(output_bpp, pipe_config->lane_count, pixel_clock, - pipe_config->port_clock, &pipe_config->dp_m2_n2, - constant_n, pipe_config->fec_enable); + if (drrs_type >= ARRAY_SIZE(str)) + return "<invalid>"; - /* FIXME: abstract this better */ - if (pipe_config->splitter.enable) - pipe_config->dp_m2_n2.data_m *= pipe_config->splitter.link_count; + return str[drrs_type]; } static void -intel_drrs_set_refresh_rate_pipeconf(const struct intel_crtc_state *crtc_state, - enum drrs_refresh_rate_type refresh_type) +intel_drrs_set_refresh_rate_pipeconf(struct intel_crtc *crtc, + enum drrs_refresh_rate refresh_rate) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + enum transcoder cpu_transcoder = crtc->drrs.cpu_transcoder; u32 val, bit; if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - bit = PIPECONF_EDP_RR_MODE_SWITCH_VLV; + bit = PIPECONF_REFRESH_RATE_ALT_VLV; else - bit = PIPECONF_EDP_RR_MODE_SWITCH; + bit = PIPECONF_REFRESH_RATE_ALT_ILK; val = intel_de_read(dev_priv, PIPECONF(cpu_transcoder)); - if (refresh_type == DRRS_LOW_RR) + if (refresh_rate == DRRS_REFRESH_RATE_LOW) val |= bit; else val &= ~bit; @@ -123,244 +85,171 @@ intel_drrs_set_refresh_rate_pipeconf(const struct intel_crtc_state *crtc_state, } static void -intel_drrs_set_refresh_rate_m_n(const struct intel_crtc_state *crtc_state, - enum drrs_refresh_rate_type refresh_type) +intel_drrs_set_refresh_rate_m_n(struct intel_crtc *crtc, + enum drrs_refresh_rate refresh_rate) { - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - - intel_cpu_transcoder_set_m1_n1(crtc, crtc_state->cpu_transcoder, - refresh_type == DRRS_LOW_RR ? - &crtc_state->dp_m2_n2 : &crtc_state->dp_m_n); + intel_cpu_transcoder_set_m1_n1(crtc, crtc->drrs.cpu_transcoder, + refresh_rate == DRRS_REFRESH_RATE_LOW ? + &crtc->drrs.m2_n2 : &crtc->drrs.m_n); } -static void intel_drrs_set_state(struct drm_i915_private *dev_priv, - const struct intel_crtc_state *crtc_state, - enum drrs_refresh_rate_type refresh_type) +bool intel_drrs_is_active(struct intel_crtc *crtc) { - struct intel_dp *intel_dp = dev_priv->drrs.dp; - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_display_mode *mode; - - if (!intel_dp) { - drm_dbg_kms(&dev_priv->drm, "DRRS not supported.\n"); - return; - } - - if (!crtc) { - drm_dbg_kms(&dev_priv->drm, - "DRRS: intel_crtc not initialized\n"); - return; - } - - if (dev_priv->drrs.type < SEAMLESS_DRRS_SUPPORT) { - drm_dbg_kms(&dev_priv->drm, "Only Seamless DRRS supported.\n"); - return; - } + return crtc->drrs.cpu_transcoder != INVALID_TRANSCODER; +} - if (refresh_type == dev_priv->drrs.refresh_rate_type) - return; +static void intel_drrs_set_state(struct intel_crtc *crtc, + enum drrs_refresh_rate refresh_rate) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - if (!crtc_state->hw.active) { - drm_dbg_kms(&dev_priv->drm, - "eDP encoder disabled. CRTC not Active\n"); + if (refresh_rate == crtc->drrs.refresh_rate) return; - } - if (DISPLAY_VER(dev_priv) >= 8 && !IS_CHERRYVIEW(dev_priv)) - intel_drrs_set_refresh_rate_m_n(crtc_state, refresh_type); - else if (DISPLAY_VER(dev_priv) > 6) - intel_drrs_set_refresh_rate_pipeconf(crtc_state, refresh_type); + if (intel_cpu_transcoder_has_m2_n2(dev_priv, crtc->drrs.cpu_transcoder)) + intel_drrs_set_refresh_rate_pipeconf(crtc, refresh_rate); + else + intel_drrs_set_refresh_rate_m_n(crtc, refresh_rate); - dev_priv->drrs.refresh_rate_type = refresh_type; + crtc->drrs.refresh_rate = refresh_rate; +} - if (refresh_type == DRRS_LOW_RR) - mode = intel_dp->attached_connector->panel.downclock_mode; - else - mode = intel_dp->attached_connector->panel.fixed_mode; - drm_dbg_kms(&dev_priv->drm, "eDP Refresh Rate set to : %dHz\n", - drm_mode_vrefresh(mode)); +static void intel_drrs_schedule_work(struct intel_crtc *crtc) +{ + mod_delayed_work(system_wq, &crtc->drrs.work, msecs_to_jiffies(1000)); } -static void -intel_drrs_enable_locked(struct intel_dp *intel_dp) +static unsigned int intel_drrs_frontbuffer_bits(const struct intel_crtc_state *crtc_state) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + unsigned int frontbuffer_bits; + + frontbuffer_bits = INTEL_FRONTBUFFER_ALL_MASK(crtc->pipe); - dev_priv->drrs.busy_frontbuffer_bits = 0; - dev_priv->drrs.dp = intel_dp; + for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, + crtc_state->bigjoiner_pipes) + frontbuffer_bits |= INTEL_FRONTBUFFER_ALL_MASK(crtc->pipe); + + return frontbuffer_bits; } /** - * intel_drrs_enable - init drrs struct if supported - * @intel_dp: DP struct - * @crtc_state: A pointer to the active crtc state. + * intel_drrs_activate - activate DRRS + * @crtc_state: the crtc state * - * Initializes frontbuffer_bits and drrs.dp + * Activates DRRS on the crtc. */ -void intel_drrs_enable(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) +void intel_drrs_activate(const struct intel_crtc_state *crtc_state) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); if (!crtc_state->has_drrs) return; - drm_dbg_kms(&dev_priv->drm, "Enabling DRRS\n"); + if (!crtc_state->hw.active) + return; - mutex_lock(&dev_priv->drrs.mutex); + if (intel_crtc_is_bigjoiner_slave(crtc_state)) + return; - if (dev_priv->drrs.dp) { - drm_warn(&dev_priv->drm, "DRRS already enabled\n"); - goto unlock; - } + mutex_lock(&crtc->drrs.mutex); - intel_drrs_enable_locked(intel_dp); + crtc->drrs.cpu_transcoder = crtc_state->cpu_transcoder; + crtc->drrs.m_n = crtc_state->dp_m_n; + crtc->drrs.m2_n2 = crtc_state->dp_m2_n2; + crtc->drrs.frontbuffer_bits = intel_drrs_frontbuffer_bits(crtc_state); + crtc->drrs.busy_frontbuffer_bits = 0; -unlock: - mutex_unlock(&dev_priv->drrs.mutex); -} + intel_drrs_schedule_work(crtc); -static void -intel_drrs_disable_locked(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - - intel_drrs_set_state(dev_priv, crtc_state, DRRS_HIGH_RR); - dev_priv->drrs.dp = NULL; + mutex_unlock(&crtc->drrs.mutex); } /** - * intel_drrs_disable - Disable DRRS - * @intel_dp: DP struct - * @old_crtc_state: Pointer to old crtc_state. + * intel_drrs_deactivate - deactivate DRRS + * @old_crtc_state: the old crtc state * + * Deactivates DRRS on the crtc. */ -void intel_drrs_disable(struct intel_dp *intel_dp, - const struct intel_crtc_state *old_crtc_state) +void intel_drrs_deactivate(const struct intel_crtc_state *old_crtc_state) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc); if (!old_crtc_state->has_drrs) return; - mutex_lock(&dev_priv->drrs.mutex); - if (!dev_priv->drrs.dp) { - mutex_unlock(&dev_priv->drrs.mutex); + if (!old_crtc_state->hw.active) return; - } - - intel_drrs_disable_locked(intel_dp, old_crtc_state); - mutex_unlock(&dev_priv->drrs.mutex); - - cancel_delayed_work_sync(&dev_priv->drrs.work); -} - -/** - * intel_drrs_update - Update DRRS state - * @intel_dp: Intel DP - * @crtc_state: new CRTC state - * - * This function will update DRRS states, disabling or enabling DRRS when - * executing fastsets. For full modeset, intel_drrs_disable() and - * intel_drrs_enable() should be called instead. - */ -void -intel_drrs_update(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - if (dev_priv->drrs.type != SEAMLESS_DRRS_SUPPORT) + if (intel_crtc_is_bigjoiner_slave(old_crtc_state)) return; - mutex_lock(&dev_priv->drrs.mutex); + mutex_lock(&crtc->drrs.mutex); - /* New state matches current one? */ - if (crtc_state->has_drrs == !!dev_priv->drrs.dp) - goto unlock; + if (intel_drrs_is_active(crtc)) + intel_drrs_set_state(crtc, DRRS_REFRESH_RATE_HIGH); - if (crtc_state->has_drrs) - intel_drrs_enable_locked(intel_dp); - else - intel_drrs_disable_locked(intel_dp, crtc_state); + crtc->drrs.cpu_transcoder = INVALID_TRANSCODER; + crtc->drrs.frontbuffer_bits = 0; + crtc->drrs.busy_frontbuffer_bits = 0; + + mutex_unlock(&crtc->drrs.mutex); -unlock: - mutex_unlock(&dev_priv->drrs.mutex); + cancel_delayed_work_sync(&crtc->drrs.work); } static void intel_drrs_downclock_work(struct work_struct *work) { - struct drm_i915_private *dev_priv = - container_of(work, typeof(*dev_priv), drrs.work.work); - struct intel_dp *intel_dp; - struct drm_crtc *crtc; - - mutex_lock(&dev_priv->drrs.mutex); - - intel_dp = dev_priv->drrs.dp; + struct intel_crtc *crtc = container_of(work, typeof(*crtc), drrs.work.work); - if (!intel_dp) - goto unlock; + mutex_lock(&crtc->drrs.mutex); - /* - * The delayed work can race with an invalidate hence we need to - * recheck. - */ + if (intel_drrs_is_active(crtc) && !crtc->drrs.busy_frontbuffer_bits) + intel_drrs_set_state(crtc, DRRS_REFRESH_RATE_LOW); - if (dev_priv->drrs.busy_frontbuffer_bits) - goto unlock; - - crtc = dp_to_dig_port(intel_dp)->base.base.crtc; - intel_drrs_set_state(dev_priv, to_intel_crtc(crtc)->config, DRRS_LOW_RR); - -unlock: - mutex_unlock(&dev_priv->drrs.mutex); + mutex_unlock(&crtc->drrs.mutex); } static void intel_drrs_frontbuffer_update(struct drm_i915_private *dev_priv, - unsigned int frontbuffer_bits, + unsigned int all_frontbuffer_bits, bool invalidate) { - struct intel_dp *intel_dp; - struct drm_crtc *crtc; - enum pipe pipe; + struct intel_crtc *crtc; - if (dev_priv->drrs.type == DRRS_NOT_SUPPORTED) + if (dev_priv->vbt.drrs_type != DRRS_TYPE_SEAMLESS) return; - cancel_delayed_work(&dev_priv->drrs.work); + for_each_intel_crtc(&dev_priv->drm, crtc) { + unsigned int frontbuffer_bits; - mutex_lock(&dev_priv->drrs.mutex); + mutex_lock(&crtc->drrs.mutex); - intel_dp = dev_priv->drrs.dp; - if (!intel_dp) { - mutex_unlock(&dev_priv->drrs.mutex); - return; - } + frontbuffer_bits = all_frontbuffer_bits & crtc->drrs.frontbuffer_bits; + if (!frontbuffer_bits) { + mutex_unlock(&crtc->drrs.mutex); + continue; + } - crtc = dp_to_dig_port(intel_dp)->base.base.crtc; - pipe = to_intel_crtc(crtc)->pipe; + if (invalidate) + crtc->drrs.busy_frontbuffer_bits |= frontbuffer_bits; + else + crtc->drrs.busy_frontbuffer_bits &= ~frontbuffer_bits; - frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe); - if (invalidate) - dev_priv->drrs.busy_frontbuffer_bits |= frontbuffer_bits; - else - dev_priv->drrs.busy_frontbuffer_bits &= ~frontbuffer_bits; - - /* flush/invalidate means busy screen hence upclock */ - if (frontbuffer_bits) - intel_drrs_set_state(dev_priv, to_intel_crtc(crtc)->config, - DRRS_HIGH_RR); - - /* - * flush also means no more activity hence schedule downclock, if all - * other fbs are quiescent too - */ - if (!invalidate && !dev_priv->drrs.busy_frontbuffer_bits) - schedule_delayed_work(&dev_priv->drrs.work, - msecs_to_jiffies(1000)); - mutex_unlock(&dev_priv->drrs.mutex); + /* flush/invalidate means busy screen hence upclock */ + intel_drrs_set_state(crtc, DRRS_REFRESH_RATE_HIGH); + + /* + * flush also means no more activity hence schedule downclock, if all + * other fbs are quiescent too + */ + if (!crtc->drrs.busy_frontbuffer_bits) + intel_drrs_schedule_work(crtc); + else + cancel_delayed_work(&crtc->drrs.work); + + mutex_unlock(&crtc->drrs.mutex); + } } /** @@ -397,68 +286,17 @@ void intel_drrs_flush(struct drm_i915_private *dev_priv, intel_drrs_frontbuffer_update(dev_priv, frontbuffer_bits, false); } -void intel_drrs_page_flip(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(state->base.dev); - unsigned int frontbuffer_bits = INTEL_FRONTBUFFER_ALL_MASK(crtc->pipe); - - intel_drrs_frontbuffer_update(dev_priv, frontbuffer_bits, false); -} - /** - * intel_drrs_init - Init basic DRRS work and mutex. - * @connector: eDP connector - * @fixed_mode: preferred mode of panel + * intel_crtc_drrs_init - Init DRRS for CRTC + * @crtc: crtc * - * This function is called only once at driver load to initialize basic + * This function is called only once at driver load to initialize basic * DRRS stuff. * - * Returns: - * Downclock mode if panel supports it, else return NULL. - * DRRS support is determined by the presence of downclock mode (apart - * from VBT setting). */ -struct drm_display_mode * -intel_drrs_init(struct intel_connector *connector, - struct drm_display_mode *fixed_mode) +void intel_crtc_drrs_init(struct intel_crtc *crtc) { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_encoder *encoder = connector->encoder; - struct drm_display_mode *downclock_mode = NULL; - - INIT_DELAYED_WORK(&dev_priv->drrs.work, intel_drrs_downclock_work); - mutex_init(&dev_priv->drrs.mutex); - - if (DISPLAY_VER(dev_priv) <= 6) { - drm_dbg_kms(&dev_priv->drm, - "DRRS supported for Gen7 and above\n"); - return NULL; - } - - if ((DISPLAY_VER(dev_priv) < 8 && !HAS_GMCH(dev_priv)) && - encoder->port != PORT_A) { - drm_dbg_kms(&dev_priv->drm, - "DRRS only supported on eDP port A\n"); - return NULL; - } - - if (dev_priv->vbt.drrs_type != SEAMLESS_DRRS_SUPPORT) { - drm_dbg_kms(&dev_priv->drm, "VBT doesn't support DRRS\n"); - return NULL; - } - - downclock_mode = intel_panel_edid_downclock_mode(connector, fixed_mode); - if (!downclock_mode) { - drm_dbg_kms(&dev_priv->drm, - "Downclock mode is not found. DRRS not supported\n"); - return NULL; - } - - dev_priv->drrs.type = dev_priv->vbt.drrs_type; - - dev_priv->drrs.refresh_rate_type = DRRS_HIGH_RR; - drm_dbg_kms(&dev_priv->drm, - "seamless DRRS supported for eDP panel.\n"); - return downclock_mode; + INIT_DELAYED_WORK(&crtc->drrs.work, intel_drrs_downclock_work); + mutex_init(&crtc->drrs.mutex); + crtc->drrs.cpu_transcoder = INVALID_TRANSCODER; } diff --git a/drivers/gpu/drm/i915/display/intel_drrs.h b/drivers/gpu/drm/i915/display/intel_drrs.h index 9ec9c447211a..3ad1be1ad9c1 100644 --- a/drivers/gpu/drm/i915/display/intel_drrs.h +++ b/drivers/gpu/drm/i915/display/intel_drrs.h @@ -8,29 +8,21 @@ #include <linux/types.h> +enum drrs_type; struct drm_i915_private; struct intel_atomic_state; struct intel_crtc; struct intel_crtc_state; struct intel_connector; -struct intel_dp; -void intel_drrs_enable(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state); -void intel_drrs_disable(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state); -void intel_drrs_update(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state); +const char *intel_drrs_type_str(enum drrs_type drrs_type); +bool intel_drrs_is_active(struct intel_crtc *crtc); +void intel_drrs_activate(const struct intel_crtc_state *crtc_state); +void intel_drrs_deactivate(const struct intel_crtc_state *crtc_state); void intel_drrs_invalidate(struct drm_i915_private *dev_priv, unsigned int frontbuffer_bits); void intel_drrs_flush(struct drm_i915_private *dev_priv, unsigned int frontbuffer_bits); -void intel_drrs_page_flip(struct intel_atomic_state *state, - struct intel_crtc *crtc); -void intel_drrs_compute_config(struct intel_dp *intel_dp, - struct intel_crtc_state *pipe_config, - int output_bpp, bool constant_n); -struct drm_display_mode *intel_drrs_init(struct intel_connector *connector, - struct drm_display_mode *fixed_mode); +void intel_crtc_drrs_init(struct intel_crtc *crtc); #endif /* __INTEL_DRRS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index b34a67309976..c4affcb216fd 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -283,14 +283,12 @@ void intel_dsb_prepare(struct intel_crtc_state *crtc_state) obj = i915_gem_object_create_internal(i915, DSB_BUF_SIZE); if (IS_ERR(obj)) { - drm_err(&i915->drm, "Gem object creation failed\n"); kfree(dsb); goto out; } vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0); if (IS_ERR(vma)) { - drm_err(&i915->drm, "Vma creation failed\n"); i915_gem_object_put(obj); kfree(dsb); goto out; @@ -298,7 +296,6 @@ void intel_dsb_prepare(struct intel_crtc_state *crtc_state) buf = i915_gem_object_pin_map_unlocked(vma->obj, I915_MAP_WC); if (IS_ERR(buf)) { - drm_err(&i915->drm, "Command buffer creation failed\n"); i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP); kfree(dsb); goto out; @@ -311,6 +308,10 @@ void intel_dsb_prepare(struct intel_crtc_state *crtc_state) dsb->ins_start_offset = 0; crtc_state->dsb = dsb; out: + if (!crtc_state->dsb) + drm_info(&i915->drm, + "DSB queue setup failed, will fallback to MMIO for display HW programming\n"); + intel_runtime_pm_put(&i915->runtime_pm, wakeref); } diff --git a/drivers/gpu/drm/i915/display/intel_dsi.c b/drivers/gpu/drm/i915/display/intel_dsi.c index a50422e03a7e..389a8c24cdc1 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.c +++ b/drivers/gpu/drm/i915/display/intel_dsi.c @@ -34,26 +34,7 @@ int intel_dsi_tlpx_ns(const struct intel_dsi *intel_dsi) int intel_dsi_get_modes(struct drm_connector *connector) { - struct drm_i915_private *i915 = to_i915(connector->dev); - struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_display_mode *mode; - - drm_dbg_kms(&i915->drm, "\n"); - - if (!intel_connector->panel.fixed_mode) { - drm_dbg_kms(&i915->drm, "no fixed mode\n"); - return 0; - } - - mode = drm_mode_duplicate(connector->dev, - intel_connector->panel.fixed_mode); - if (!mode) { - drm_dbg_kms(&i915->drm, "drm_mode_duplicate failed\n"); - return 0; - } - - drm_mode_probed_add(connector, mode); - return 1; + return intel_panel_get_modes(to_intel_connector(connector)); } enum drm_mode_status intel_dsi_mode_valid(struct drm_connector *connector, @@ -61,7 +42,8 @@ enum drm_mode_status intel_dsi_mode_valid(struct drm_connector *connector, { struct drm_i915_private *dev_priv = to_i915(connector->dev); struct intel_connector *intel_connector = to_intel_connector(connector); - const struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; + const struct drm_display_mode *fixed_mode = + intel_panel_fixed_mode(intel_connector, mode); int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; enum drm_mode_status status; diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 6b4a27372c82..dd24aef925f2 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -30,6 +30,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/machine.h> #include <linux/slab.h> +#include <linux/string_helpers.h> #include <asm/unaligned.h> @@ -124,9 +125,25 @@ struct i2c_adapter_lookup { #define ICL_GPIO_DDPA_CTRLCLK_2 8 #define ICL_GPIO_DDPA_CTRLDATA_2 9 -static enum port intel_dsi_seq_port_to_port(u8 port) +static enum port intel_dsi_seq_port_to_port(struct intel_dsi *intel_dsi, + u8 seq_port) { - return port ? PORT_C : PORT_A; + /* + * If single link DSI is being used on any port, the VBT sequence block + * send packet apparently always has 0 for the port. Just use the port + * we have configured, and ignore the sequence block port. + */ + if (hweight8(intel_dsi->ports) == 1) + return ffs(intel_dsi->ports) - 1; + + if (seq_port) { + if (intel_dsi->ports & PORT_B) + return PORT_B; + else if (intel_dsi->ports & PORT_C) + return PORT_C; + } + + return PORT_A; } static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, @@ -148,15 +165,10 @@ static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, seq_port = (flags >> MIPI_PORT_SHIFT) & 3; - /* For DSI single link on Port A & C, the seq_port value which is - * parsed from Sequence Block#53 of VBT has been set to 0 - * Now, read/write of packets for the DSI single link on Port A and - * Port C will based on the DVO port from VBT block 2. - */ - if (intel_dsi->ports == (1 << PORT_C)) - port = PORT_C; - else - port = intel_dsi_seq_port_to_port(seq_port); + port = intel_dsi_seq_port_to_port(intel_dsi, seq_port); + + if (drm_WARN_ON(&dev_priv->drm, !intel_dsi->dsi_hosts[port])) + goto out; dsi_device = intel_dsi->dsi_hosts[port]->device; if (!dsi_device) { @@ -686,9 +698,9 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) intel_dsi->burst_mode_ratio); drm_dbg_kms(&i915->drm, "Reset timer %d\n", intel_dsi->rst_timer_val); drm_dbg_kms(&i915->drm, "Eot %s\n", - enableddisabled(intel_dsi->eotp_pkt)); + str_enabled_disabled(intel_dsi->eotp_pkt)); drm_dbg_kms(&i915->drm, "Clockstop %s\n", - enableddisabled(!intel_dsi->clock_stop)); + str_enabled_disabled(!intel_dsi->clock_stop)); drm_dbg_kms(&i915->drm, "Mode %s\n", intel_dsi->operation_mode ? "command" : "video"); if (intel_dsi->dual_link == DSI_DUAL_LINK_FRONT_BACK) @@ -715,7 +727,7 @@ void intel_dsi_log_params(struct intel_dsi *intel_dsi) drm_dbg_kms(&i915->drm, "HS to LP Clock Count 0x%x\n", intel_dsi->clk_hs_to_lp_count); drm_dbg_kms(&i915->drm, "BTA %s\n", - enableddisabled(!(intel_dsi->video_frmt_cfg_bits & DISABLE_VIDEO_BTA))); + str_enabled_disabled(!(intel_dsi->video_frmt_cfg_bits & DISABLE_VIDEO_BTA))); } bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c index 2eeb209afc64..5572e43026e4 100644 --- a/drivers/gpu/drm/i915/display/intel_dvo.c +++ b/drivers/gpu/drm/i915/display/intel_dvo.c @@ -226,7 +226,7 @@ intel_dvo_mode_valid(struct drm_connector *connector, struct intel_connector *intel_connector = to_intel_connector(connector); struct intel_dvo *intel_dvo = intel_attached_dvo(intel_connector); const struct drm_display_mode *fixed_mode = - intel_connector->panel.fixed_mode; + intel_panel_fixed_mode(intel_connector, mode); int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; int target_clock = mode->clock; @@ -257,9 +257,9 @@ static int intel_dvo_compute_config(struct intel_encoder *encoder, { struct intel_dvo *intel_dvo = enc_to_dvo(encoder); struct intel_connector *connector = to_intel_connector(conn_state->connector); - const struct drm_display_mode *fixed_mode = - intel_dvo->attached_connector->panel.fixed_mode; struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; + const struct drm_display_mode *fixed_mode = + intel_panel_fixed_mode(intel_dvo->attached_connector, adjusted_mode); /* * If we have timings from the BIOS for the panel, put them in @@ -333,8 +333,6 @@ intel_dvo_detect(struct drm_connector *connector, bool force) static int intel_dvo_get_modes(struct drm_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->dev); - const struct drm_display_mode *fixed_mode = - to_intel_connector(connector)->panel.fixed_mode; int num_modes; /* @@ -348,17 +346,7 @@ static int intel_dvo_get_modes(struct drm_connector *connector) if (num_modes) return num_modes; - if (fixed_mode) { - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, fixed_mode); - if (mode) { - drm_mode_probed_add(connector, mode); - num_modes++; - } - } - - return num_modes; + return intel_panel_get_modes(to_intel_connector(connector)); } static const struct drm_connector_funcs intel_dvo_connector_funcs = { @@ -390,27 +378,6 @@ static const struct drm_encoder_funcs intel_dvo_enc_funcs = { .destroy = intel_dvo_enc_destroy, }; -/* - * Attempts to get a fixed panel timing for LVDS (currently only the i830). - * - * Other chips with DVO LVDS will need to extend this to deal with the LVDS - * chip being on DVOB/C and having multiple pipes. - */ -static struct drm_display_mode * -intel_dvo_get_current_mode(struct intel_encoder *encoder) -{ - struct drm_display_mode *mode; - - mode = intel_encoder_current_mode(encoder); - if (mode) { - DRM_DEBUG_KMS("using current (BIOS) mode: "); - drm_mode_debug_printmodeline(mode); - mode->type |= DRM_MODE_TYPE_PREFERRED; - } - - return mode; -} - static enum port intel_dvo_port(i915_reg_t dvo_reg) { if (i915_mmio_reg_equal(dvo_reg, DVOA)) @@ -561,9 +528,11 @@ void intel_dvo_init(struct drm_i915_private *dev_priv) * headers, likely), so for now, just get the current * mode being output through DVO. */ - intel_panel_init(&intel_connector->panel, - intel_dvo_get_current_mode(intel_encoder), - NULL); + intel_panel_add_encoder_fixed_mode(intel_connector, + intel_encoder); + + intel_panel_init(intel_connector); + intel_dvo->panel_wants_dither = true; } diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c index 23cfe2e5ce2a..9f5a6b79e95b 100644 --- a/drivers/gpu/drm/i915/display/intel_fb.c +++ b/drivers/gpu/drm/i915/display/intel_fb.c @@ -107,6 +107,21 @@ static const struct drm_format_info gen12_ccs_cc_formats[] = { .hsub = 1, .vsub = 1, .has_alpha = true }, }; +static const struct drm_format_info gen12_flat_ccs_cc_formats[] = { + { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, }, + { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, + { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2, + .char_per_block = { 4, 0 }, .block_w = { 1, 2 }, .block_h = { 1, 1 }, + .hsub = 1, .vsub = 1, .has_alpha = true }, +}; + struct intel_modifier_desc { u64 modifier; struct { @@ -135,11 +150,32 @@ struct intel_modifier_desc { INTEL_PLANE_CAP_CCS_MC) #define INTEL_PLANE_CAP_TILING_MASK (INTEL_PLANE_CAP_TILING_X | \ INTEL_PLANE_CAP_TILING_Y | \ - INTEL_PLANE_CAP_TILING_Yf) + INTEL_PLANE_CAP_TILING_Yf | \ + INTEL_PLANE_CAP_TILING_4) #define INTEL_PLANE_CAP_TILING_NONE 0 static const struct intel_modifier_desc intel_modifiers[] = { { + .modifier = I915_FORMAT_MOD_4_TILED_DG2_MC_CCS, + .display_ver = { 13, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_CCS_MC, + }, { + .modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC, + .display_ver = { 13, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_CCS_RC_CC, + + .ccs.cc_planes = BIT(1), + + FORMAT_OVERRIDE(gen12_flat_ccs_cc_formats), + }, { + .modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS, + .display_ver = { 13, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4 | INTEL_PLANE_CAP_CCS_RC, + }, { + .modifier = I915_FORMAT_MOD_4_TILED, + .display_ver = { 13, 13 }, + .plane_caps = INTEL_PLANE_CAP_TILING_4, + }, { .modifier = I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS, .display_ver = { 12, 13 }, .plane_caps = INTEL_PLANE_CAP_TILING_Y | INTEL_PLANE_CAP_CCS_MC, @@ -380,17 +416,13 @@ bool intel_fb_plane_supports_modifier(struct intel_plane *plane, u64 modifier) static bool format_is_yuv_semiplanar(const struct intel_modifier_desc *md, const struct drm_format_info *info) { - int yuv_planes; - if (!info->is_yuv) return false; - if (plane_caps_contain_any(md->plane_caps, INTEL_PLANE_CAP_CCS_MASK)) - yuv_planes = 4; + if (hweight8(md->ccs.planar_aux_planes) == 2) + return info->num_planes == 4; else - yuv_planes = 2; - - return info->num_planes == yuv_planes; + return info->num_planes == 2; } /** @@ -515,12 +547,13 @@ static unsigned int gen12_ccs_aux_stride(struct intel_framebuffer *fb, int ccs_p int skl_main_to_aux_plane(const struct drm_framebuffer *fb, int main_plane) { + const struct intel_modifier_desc *md = lookup_modifier(fb->modifier); struct drm_i915_private *i915 = to_i915(fb->dev); - if (intel_fb_is_ccs_modifier(fb->modifier)) + if (md->ccs.packed_aux_planes | md->ccs.planar_aux_planes) return main_to_ccs_plane(fb, main_plane); else if (DISPLAY_VER(i915) < 11 && - intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) + format_is_yuv_semiplanar(md, fb->format)) return 1; else return 0; @@ -545,6 +578,15 @@ intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane) return 128; else return 512; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + case I915_FORMAT_MOD_4_TILED: + /* + * Each 4K tile consists of 64B(8*8) subtiles, with + * same shape as Y Tile(i.e 4*16B OWords) + */ + return 128; case I915_FORMAT_MOD_Y_TILED_CCS: if (intel_fb_is_ccs_aux_plane(fb, color_plane)) return 128; @@ -650,6 +692,7 @@ static unsigned int intel_fb_modifier_to_tiling(u64 fb_modifier) return I915_TILING_Y; case INTEL_PLANE_CAP_TILING_X: return I915_TILING_X; + case INTEL_PLANE_CAP_TILING_4: case INTEL_PLANE_CAP_TILING_Yf: case INTEL_PLANE_CAP_TILING_NONE: return I915_TILING_NONE; @@ -737,8 +780,13 @@ unsigned int intel_surf_alignment(const struct drm_framebuffer *fb, case I915_FORMAT_MOD_Y_TILED_CCS: case I915_FORMAT_MOD_Yf_TILED_CCS: case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_4_TILED: case I915_FORMAT_MOD_Yf_TILED: return 1 * 1024 * 1024; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + return 16 * 1024; default: MISSING_CASE(fb->modifier); return 0; @@ -1981,7 +2029,7 @@ intel_user_framebuffer_create(struct drm_device *dev, /* object is backed with LMEM for discrete */ i915 = to_i915(obj->base.dev); - if (HAS_LMEM(i915) && !i915_gem_object_can_migrate(obj, INTEL_REGION_LMEM)) { + if (HAS_LMEM(i915) && !i915_gem_object_can_migrate(obj, INTEL_REGION_LMEM_0)) { /* object is "remote", not in local memory */ i915_gem_object_put(obj); return ERR_PTR(-EREMOTE); diff --git a/drivers/gpu/drm/i915/display/intel_fb.h b/drivers/gpu/drm/i915/display/intel_fb.h index ba9df8986c1e..12386f13a4e0 100644 --- a/drivers/gpu/drm/i915/display/intel_fb.h +++ b/drivers/gpu/drm/i915/display/intel_fb.h @@ -27,6 +27,7 @@ struct intel_plane_state; #define INTEL_PLANE_CAP_TILING_X BIT(3) #define INTEL_PLANE_CAP_TILING_Y BIT(4) #define INTEL_PLANE_CAP_TILING_Yf BIT(5) +#define INTEL_PLANE_CAP_TILING_4 BIT(6) bool intel_fb_is_ccs_modifier(u64 modifier); bool intel_fb_is_rc_ccs_cc_modifier(u64 modifier); diff --git a/drivers/gpu/drm/i915/display/intel_fb_pin.c b/drivers/gpu/drm/i915/display/intel_fb_pin.c index a307b4993bcf..bd6e7c98e751 100644 --- a/drivers/gpu/drm/i915/display/intel_fb_pin.c +++ b/drivers/gpu/drm/i915/display/intel_fb_pin.c @@ -140,7 +140,7 @@ retry: if (!ret && phys_cursor) ret = i915_gem_object_attach_phys(obj, alignment); else if (!ret && HAS_LMEM(dev_priv)) - ret = i915_gem_object_migrate(obj, &ww, INTEL_REGION_LMEM); + ret = i915_gem_object_migrate(obj, &ww, INTEL_REGION_LMEM_0); /* TODO: Do we need to sync when migration becomes async? */ if (!ret) ret = i915_gem_object_pin_pages(obj); diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index 3e61a8936245..bbdc34a23d54 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -38,9 +38,12 @@ * forcibly disable it to allow proper screen updates. */ +#include <linux/string_helpers.h> + #include <drm/drm_fourcc.h> #include "i915_drv.h" +#include "i915_utils.h" #include "i915_vgpu.h" #include "intel_cdclk.h" #include "intel_de.h" @@ -87,7 +90,6 @@ struct intel_fbc { * with stolen_lock. */ struct mutex lock; - unsigned int possible_framebuffer_bits; unsigned int busy_bits; struct drm_mm_node compressed_fb; @@ -665,6 +667,10 @@ static bool intel_fbc_is_compressing(struct intel_fbc *fbc) static void intel_fbc_nuke(struct intel_fbc *fbc) { + struct drm_i915_private *i915 = fbc->i915; + + drm_WARN_ON(&i915->drm, fbc->flip_pending); + trace_intel_fbc_nuke(fbc->state.plane); fbc->funcs->nuke(fbc); @@ -805,6 +811,14 @@ static void intel_fbc_program_cfb(struct intel_fbc *fbc) fbc->funcs->program_cfb(fbc); } +static void intel_fbc_program_workarounds(struct intel_fbc *fbc) +{ + /* Wa_22014263786:icl,jsl,tgl,dg1,rkl,adls,dg2,adlp */ + if (DISPLAY_VER(fbc->i915) >= 11) + intel_de_rmw(fbc->i915, ILK_DPFC_CHICKEN(fbc->id), 0, + DPFC_CHICKEN_FORCE_SLB_INVALIDATION); +} + static void __intel_fbc_cleanup_cfb(struct intel_fbc *fbc) { struct drm_i915_private *i915 = fbc->i915; @@ -946,6 +960,7 @@ static bool tiling_is_valid(const struct intel_plane_state *plane_state) case I915_FORMAT_MOD_Y_TILED: case I915_FORMAT_MOD_Yf_TILED: return DISPLAY_VER(i915) >= 9; + case I915_FORMAT_MOD_4_TILED: case I915_FORMAT_MOD_X_TILED: return true; default: @@ -966,6 +981,7 @@ static void intel_fbc_update_state(struct intel_atomic_state *state, struct intel_fbc_state *fbc_state = &fbc->state; WARN_ON(plane_state->no_fbc_reason); + WARN_ON(fbc_state->plane && fbc_state->plane != plane); fbc_state->plane = plane; @@ -1078,7 +1094,7 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state, */ if (DISPLAY_VER(i915) >= 12 && crtc_state->has_psr2) { plane_state->no_fbc_reason = "PSR2 enabled"; - return false; + return 0; } if (!pixel_format_is_valid(plane_state)) { @@ -1104,7 +1120,7 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state, if (plane_state->hw.pixel_blend_mode != DRM_MODE_BLEND_PIXEL_NONE && fb->format->has_alpha) { plane_state->no_fbc_reason = "per-pixel alpha not supported"; - return false; + return 0; } if (!intel_fbc_hw_tracking_covers_screen(plane_state)) { @@ -1120,7 +1136,7 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state, if (DISPLAY_VER(i915) >= 9 && plane_state->view.color_plane[0].y & 3) { plane_state->no_fbc_reason = "plane start Y offset misaligned"; - return false; + return 0; } /* Wa_22010751166: icl, ehl, tgl, dg1, rkl */ @@ -1128,7 +1144,7 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state, (plane_state->view.color_plane[0].y + (drm_rect_height(&plane_state->uapi.src) >> 16)) & 3) { plane_state->no_fbc_reason = "plane end Y offset misaligned"; - return false; + return 0; } /* WaFbcExceedCdClockThreshold:hsw,bdw */ @@ -1270,6 +1286,8 @@ static void __intel_fbc_disable(struct intel_fbc *fbc) __intel_fbc_cleanup_cfb(fbc); fbc->state.plane = NULL; + fbc->flip_pending = false; + fbc->busy_bits = 0; } static void __intel_fbc_post_update(struct intel_fbc *fbc) @@ -1313,7 +1331,7 @@ static unsigned int intel_fbc_get_frontbuffer_bit(struct intel_fbc *fbc) if (fbc->state.plane) return fbc->state.plane->frontbuffer_bit; else - return fbc->possible_framebuffer_bits; + return 0; } static void __intel_fbc_invalidate(struct intel_fbc *fbc, @@ -1325,11 +1343,14 @@ static void __intel_fbc_invalidate(struct intel_fbc *fbc, mutex_lock(&fbc->lock); - fbc->busy_bits |= intel_fbc_get_frontbuffer_bit(fbc) & frontbuffer_bits; + frontbuffer_bits &= intel_fbc_get_frontbuffer_bit(fbc); + if (!frontbuffer_bits) + goto out; - if (fbc->state.plane && fbc->busy_bits) - intel_fbc_deactivate(fbc, "frontbuffer write"); + fbc->busy_bits |= frontbuffer_bits; + intel_fbc_deactivate(fbc, "frontbuffer write"); +out: mutex_unlock(&fbc->lock); } @@ -1351,18 +1372,22 @@ static void __intel_fbc_flush(struct intel_fbc *fbc, { mutex_lock(&fbc->lock); + frontbuffer_bits &= intel_fbc_get_frontbuffer_bit(fbc); + if (!frontbuffer_bits) + goto out; + fbc->busy_bits &= ~frontbuffer_bits; if (origin == ORIGIN_FLIP || origin == ORIGIN_CURSOR_UPDATE) goto out; - if (!fbc->busy_bits && fbc->state.plane && - (frontbuffer_bits & intel_fbc_get_frontbuffer_bit(fbc))) { - if (fbc->active) - intel_fbc_nuke(fbc); - else if (!fbc->flip_pending) - __intel_fbc_post_update(fbc); - } + if (fbc->busy_bits || fbc->flip_pending) + goto out; + + if (fbc->active) + intel_fbc_nuke(fbc); + else + intel_fbc_activate(fbc); out: mutex_unlock(&fbc->lock); @@ -1445,6 +1470,7 @@ static void __intel_fbc_enable(struct intel_atomic_state *state, intel_fbc_update_state(state, crtc, plane); + intel_fbc_program_workarounds(fbc); intel_fbc_program_cfb(fbc); } @@ -1500,25 +1526,6 @@ void intel_fbc_update(struct intel_atomic_state *state, } } -/** - * intel_fbc_global_disable - globally disable FBC - * @i915: i915 device instance - * - * This function disables FBC regardless of which CRTC is associated with it. - */ -void intel_fbc_global_disable(struct drm_i915_private *i915) -{ - struct intel_fbc *fbc; - enum intel_fbc_id fbc_id; - - for_each_intel_fbc(i915, fbc, fbc_id) { - mutex_lock(&fbc->lock); - if (fbc->state.plane) - __intel_fbc_disable(fbc); - mutex_unlock(&fbc->lock); - } -} - static void intel_fbc_underrun_work_fn(struct work_struct *work) { struct intel_fbc *fbc = container_of(work, typeof(*fbc), underrun_work); @@ -1640,7 +1647,7 @@ static int intel_sanitize_fbc_option(struct drm_i915_private *i915) static bool need_fbc_vtd_wa(struct drm_i915_private *i915) { /* WaFbcTurnOffFbcWhenHyperVisorIsUsed:skl,bxt */ - if (intel_vtd_active(i915) && + if (i915_vtd_active(i915) && (IS_SKYLAKE(i915) || IS_BROXTON(i915))) { drm_info(&i915->drm, "Disabling framebuffer compression (FBC) to prevent screen flicker with VT-d enabled\n"); @@ -1652,11 +1659,7 @@ static bool need_fbc_vtd_wa(struct drm_i915_private *i915) void intel_fbc_add_plane(struct intel_fbc *fbc, struct intel_plane *plane) { - if (!fbc) - return; - plane->fbc = fbc; - fbc->possible_framebuffer_bits |= plane->frontbuffer_bit; } static struct intel_fbc *intel_fbc_create(struct drm_i915_private *i915, @@ -1709,22 +1712,26 @@ void intel_fbc_init(struct drm_i915_private *i915) drm_dbg_kms(&i915->drm, "Sanitized enable_fbc value: %d\n", i915->params.enable_fbc); - for_each_fbc_id(i915, fbc_id) { - struct intel_fbc *fbc; + for_each_fbc_id(i915, fbc_id) + i915->fbc[fbc_id] = intel_fbc_create(i915, fbc_id); +} - fbc = intel_fbc_create(i915, fbc_id); - if (!fbc) - continue; +/** + * intel_fbc_sanitize - Sanitize FBC + * @i915: the i915 device + * + * Make sure FBC is initially disabled since we have no + * idea eg. into which parts of stolen it might be scribbling + * into. + */ +void intel_fbc_sanitize(struct drm_i915_private *i915) +{ + struct intel_fbc *fbc; + enum intel_fbc_id fbc_id; - /* - * We still don't have any sort of hardware state readout - * for FBC, so deactivate it in case the BIOS activated it - * to make sure software matches the hardware state. - */ + for_each_intel_fbc(i915, fbc, fbc_id) { if (intel_fbc_hw_is_active(fbc)) intel_fbc_hw_deactivate(fbc); - - i915->fbc[fbc->id] = fbc; } } @@ -1743,7 +1750,7 @@ static int intel_fbc_debugfs_status_show(struct seq_file *m, void *unused) if (fbc->active) { seq_puts(m, "FBC enabled\n"); seq_printf(m, "Compressing: %s\n", - yesno(intel_fbc_is_compressing(fbc))); + str_yes_no(intel_fbc_is_compressing(fbc))); } else { seq_printf(m, "FBC disabled: %s\n", fbc->no_fbc_reason); } diff --git a/drivers/gpu/drm/i915/display/intel_fbc.h b/drivers/gpu/drm/i915/display/intel_fbc.h index 8c5a7339a27f..db60143295ec 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.h +++ b/drivers/gpu/drm/i915/display/intel_fbc.h @@ -30,10 +30,10 @@ void intel_fbc_post_update(struct intel_atomic_state *state, struct intel_crtc *crtc); void intel_fbc_init(struct drm_i915_private *dev_priv); void intel_fbc_cleanup(struct drm_i915_private *dev_priv); +void intel_fbc_sanitize(struct drm_i915_private *dev_priv); void intel_fbc_update(struct intel_atomic_state *state, struct intel_crtc *crtc); void intel_fbc_disable(struct intel_crtc *crtc); -void intel_fbc_global_disable(struct drm_i915_private *dev_priv); void intel_fbc_invalidate(struct drm_i915_private *dev_priv, unsigned int frontbuffer_bits, enum fb_op_origin origin); diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index 2cd62a187df3..221336178991 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -279,7 +279,7 @@ static int intelfb_create(struct drm_fb_helper *helper, /* Our framebuffer is the entirety of fbdev's system memory */ info->fix.smem_start = (unsigned long)(ggtt->gmadr.start + vma->node.start); - info->fix.smem_len = vma->node.size; + info->fix.smem_len = vma->size; } vaddr = i915_vma_pin_iomap(vma); @@ -290,7 +290,7 @@ static int intelfb_create(struct drm_fb_helper *helper, goto out_unpin; } info->screen_base = vaddr; - info->screen_size = vma->node.size; + info->screen_size = vma->size; drm_fb_helper_fill_info(info, &ifbdev->helper, sizes); diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c index 4e4b43669b14..67d2484afbaa 100644 --- a/drivers/gpu/drm/i915/display/intel_fdi.c +++ b/drivers/gpu/drm/i915/display/intel_fdi.c @@ -3,6 +3,8 @@ * Copyright © 2020 Intel Corporation */ +#include <linux/string_helpers.h> + #include "intel_atomic.h" #include "intel_crtc.h" #include "intel_ddi.h" @@ -34,7 +36,7 @@ static void assert_fdi_tx(struct drm_i915_private *dev_priv, } I915_STATE_WARN(cur_state != state, "FDI TX state assertion failure (expected %s, current %s)\n", - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } void assert_fdi_tx_enabled(struct drm_i915_private *i915, enum pipe pipe) @@ -55,7 +57,7 @@ static void assert_fdi_rx(struct drm_i915_private *dev_priv, cur_state = intel_de_read(dev_priv, FDI_RX_CTL(pipe)) & FDI_RX_ENABLE; I915_STATE_WARN(cur_state != state, "FDI RX state assertion failure (expected %s, current %s)\n", - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } void assert_fdi_rx_enabled(struct drm_i915_private *i915, enum pipe pipe) @@ -93,7 +95,7 @@ static void assert_fdi_rx_pll(struct drm_i915_private *i915, cur_state = intel_de_read(i915, FDI_RX_CTL(pipe)) & FDI_RX_PLL_ENABLE; I915_STATE_WARN(cur_state != state, "FDI RX PLL assertion failure (expected %s, current %s)\n", - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } void assert_fdi_rx_pll_enabled(struct drm_i915_private *i915, enum pipe pipe) diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index 2fad03250661..a6ba7fb72339 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -31,13 +31,23 @@ #include <linux/i2c-algo-bit.h> #include <linux/i2c.h> -#include <drm/drm_hdcp.h> +#include <drm/display/drm_hdcp_helper.h> #include "i915_drv.h" #include "intel_de.h" #include "intel_display_types.h" #include "intel_gmbus.h" +struct intel_gmbus { + struct i2c_adapter adapter; +#define GMBUS_FORCE_BIT_RETRY (1U << 31) + u32 force_bit; + u32 reg0; + i915_reg_t gpio_reg; + struct i2c_algo_bit_data bit_algo; + struct drm_i915_private *dev_priv; +}; + struct gmbus_pin { const char *name; enum i915_gpio gpio; @@ -106,51 +116,47 @@ static const struct gmbus_pin gmbus_pins_dg2[] = { [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, }; -/* pin is expected to be valid */ -static const struct gmbus_pin *get_gmbus_pin(struct drm_i915_private *dev_priv, +static const struct gmbus_pin *get_gmbus_pin(struct drm_i915_private *i915, unsigned int pin) { - if (INTEL_PCH_TYPE(dev_priv) >= PCH_DG2) - return &gmbus_pins_dg2[pin]; - else if (INTEL_PCH_TYPE(dev_priv) >= PCH_DG1) - return &gmbus_pins_dg1[pin]; - else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) - return &gmbus_pins_icp[pin]; - else if (HAS_PCH_CNP(dev_priv)) - return &gmbus_pins_cnp[pin]; - else if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) - return &gmbus_pins_bxt[pin]; - else if (DISPLAY_VER(dev_priv) == 9) - return &gmbus_pins_skl[pin]; - else if (IS_BROADWELL(dev_priv)) - return &gmbus_pins_bdw[pin]; - else - return &gmbus_pins[pin]; -} - -bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv, - unsigned int pin) -{ - unsigned int size; + const struct gmbus_pin *pins; + size_t size; - if (INTEL_PCH_TYPE(dev_priv) >= PCH_DG2) + if (INTEL_PCH_TYPE(i915) >= PCH_DG2) { + pins = gmbus_pins_dg2; size = ARRAY_SIZE(gmbus_pins_dg2); - else if (INTEL_PCH_TYPE(dev_priv) >= PCH_DG1) + } else if (INTEL_PCH_TYPE(i915) >= PCH_DG1) { + pins = gmbus_pins_dg1; size = ARRAY_SIZE(gmbus_pins_dg1); - else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) + } else if (INTEL_PCH_TYPE(i915) >= PCH_ICP) { + pins = gmbus_pins_icp; size = ARRAY_SIZE(gmbus_pins_icp); - else if (HAS_PCH_CNP(dev_priv)) + } else if (HAS_PCH_CNP(i915)) { + pins = gmbus_pins_cnp; size = ARRAY_SIZE(gmbus_pins_cnp); - else if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) + } else if (IS_GEMINILAKE(i915) || IS_BROXTON(i915)) { + pins = gmbus_pins_bxt; size = ARRAY_SIZE(gmbus_pins_bxt); - else if (DISPLAY_VER(dev_priv) == 9) + } else if (DISPLAY_VER(i915) == 9) { + pins = gmbus_pins_skl; size = ARRAY_SIZE(gmbus_pins_skl); - else if (IS_BROADWELL(dev_priv)) + } else if (IS_BROADWELL(i915)) { + pins = gmbus_pins_bdw; size = ARRAY_SIZE(gmbus_pins_bdw); - else + } else { + pins = gmbus_pins; size = ARRAY_SIZE(gmbus_pins); + } + + if (pin >= size || !pins[pin].name) + return NULL; + + return &pins[pin]; +} - return pin < size && get_gmbus_pin(dev_priv, pin)->name; +bool intel_gmbus_is_valid_pin(struct drm_i915_private *i915, unsigned int pin) +{ + return get_gmbus_pin(i915, pin); } /* Intel GPIO access functions */ @@ -294,9 +300,7 @@ static void set_data(void *data, int state_high) static int intel_gpio_pre_xfer(struct i2c_adapter *adapter) { - struct intel_gmbus *bus = container_of(adapter, - struct intel_gmbus, - adapter); + struct intel_gmbus *bus = to_intel_gmbus(adapter); struct drm_i915_private *dev_priv = bus->dev_priv; intel_gmbus_reset(dev_priv); @@ -313,9 +317,7 @@ intel_gpio_pre_xfer(struct i2c_adapter *adapter) static void intel_gpio_post_xfer(struct i2c_adapter *adapter) { - struct intel_gmbus *bus = container_of(adapter, - struct intel_gmbus, - adapter); + struct intel_gmbus *bus = to_intel_gmbus(adapter); struct drm_i915_private *dev_priv = bus->dev_priv; set_data(bus, 1); @@ -326,14 +328,13 @@ intel_gpio_post_xfer(struct i2c_adapter *adapter) } static void -intel_gpio_setup(struct intel_gmbus *bus, unsigned int pin) +intel_gpio_setup(struct intel_gmbus *bus, i915_reg_t gpio_reg) { - struct drm_i915_private *dev_priv = bus->dev_priv; struct i2c_algo_bit_data *algo; algo = &bus->bit_algo; - bus->gpio_reg = GPIO(get_gmbus_pin(dev_priv, pin)->gpio); + bus->gpio_reg = gpio_reg; bus->adapter.algo_data = algo; algo->setsda = set_data; algo->setscl = set_clock; @@ -614,9 +615,7 @@ static int do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num, u32 gmbus0_source) { - struct intel_gmbus *bus = container_of(adapter, - struct intel_gmbus, - adapter); + struct intel_gmbus *bus = to_intel_gmbus(adapter); struct drm_i915_private *dev_priv = bus->dev_priv; int i = 0, inc, try = 0; int ret = 0; @@ -746,8 +745,7 @@ out: static int gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { - struct intel_gmbus *bus = - container_of(adapter, struct intel_gmbus, adapter); + struct intel_gmbus *bus = to_intel_gmbus(adapter); struct drm_i915_private *dev_priv = bus->dev_priv; intel_wakeref_t wakeref; int ret; @@ -771,8 +769,7 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) int intel_gmbus_output_aksv(struct i2c_adapter *adapter) { - struct intel_gmbus *bus = - container_of(adapter, struct intel_gmbus, adapter); + struct intel_gmbus *bus = to_intel_gmbus(adapter); struct drm_i915_private *dev_priv = bus->dev_priv; u8 cmd = DRM_HDCP_DDC_AKSV; u8 buf[DRM_HDCP_KSV_LEN] = { 0 }; @@ -863,7 +860,6 @@ static const struct i2c_lock_operations gmbus_lock_ops = { int intel_gmbus_setup(struct drm_i915_private *dev_priv) { struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); - struct intel_gmbus *bus; unsigned int pin; int ret; @@ -880,17 +876,24 @@ int intel_gmbus_setup(struct drm_i915_private *dev_priv) init_waitqueue_head(&dev_priv->gmbus_wait_queue); for (pin = 0; pin < ARRAY_SIZE(dev_priv->gmbus); pin++) { - if (!intel_gmbus_is_valid_pin(dev_priv, pin)) + const struct gmbus_pin *gmbus_pin; + struct intel_gmbus *bus; + + gmbus_pin = get_gmbus_pin(dev_priv, pin); + if (!gmbus_pin) continue; - bus = &dev_priv->gmbus[pin]; + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) { + ret = -ENOMEM; + goto err; + } bus->adapter.owner = THIS_MODULE; bus->adapter.class = I2C_CLASS_DDC; snprintf(bus->adapter.name, sizeof(bus->adapter.name), - "i915 gmbus %s", - get_gmbus_pin(dev_priv, pin)->name); + "i915 gmbus %s", gmbus_pin->name); bus->adapter.dev.parent = &pdev->dev; bus->dev_priv = dev_priv; @@ -911,11 +914,15 @@ int intel_gmbus_setup(struct drm_i915_private *dev_priv) if (IS_I830(dev_priv)) bus->force_bit = 1; - intel_gpio_setup(bus, pin); + intel_gpio_setup(bus, GPIO(gmbus_pin->gpio)); ret = i2c_add_adapter(&bus->adapter); - if (ret) + if (ret) { + kfree(bus); goto err; + } + + dev_priv->gmbus[pin] = bus; } intel_gmbus_reset(dev_priv); @@ -923,24 +930,19 @@ int intel_gmbus_setup(struct drm_i915_private *dev_priv) return 0; err: - while (pin--) { - if (!intel_gmbus_is_valid_pin(dev_priv, pin)) - continue; + intel_gmbus_teardown(dev_priv); - bus = &dev_priv->gmbus[pin]; - i2c_del_adapter(&bus->adapter); - } return ret; } struct i2c_adapter *intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned int pin) { - if (drm_WARN_ON(&dev_priv->drm, - !intel_gmbus_is_valid_pin(dev_priv, pin))) + if (drm_WARN_ON(&dev_priv->drm, pin >= ARRAY_SIZE(dev_priv->gmbus) || + !dev_priv->gmbus[pin])) return NULL; - return &dev_priv->gmbus[pin].adapter; + return &dev_priv->gmbus[pin]->adapter; } void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit) @@ -968,14 +970,18 @@ bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter) void intel_gmbus_teardown(struct drm_i915_private *dev_priv) { - struct intel_gmbus *bus; unsigned int pin; for (pin = 0; pin < ARRAY_SIZE(dev_priv->gmbus); pin++) { - if (!intel_gmbus_is_valid_pin(dev_priv, pin)) + struct intel_gmbus *bus; + + bus = dev_priv->gmbus[pin]; + if (!bus) continue; - bus = &dev_priv->gmbus[pin]; i2c_del_adapter(&bus->adapter); + + kfree(bus); + dev_priv->gmbus[pin] = NULL; } } diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index e1ecf38db0ef..44ac0cee8b77 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -12,7 +12,7 @@ #include <linux/i2c.h> #include <linux/random.h> -#include <drm/drm_hdcp.h> +#include <drm/display/drm_hdcp_helper.h> #include <drm/i915_component.h> #include "i915_drv.h" @@ -20,6 +20,7 @@ #include "intel_connector.h" #include "intel_de.h" #include "intel_display_power.h" +#include "intel_display_power_well.h" #include "intel_display_types.h" #include "intel_hdcp.h" #include "intel_pcode.h" diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 6512f014cad4..1ae09431f53a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -30,12 +30,14 @@ #include <linux/hdmi.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/string_helpers.h> +#include <drm/display/drm_hdcp_helper.h> +#include <drm/display/drm_hdmi_helper.h> +#include <drm/display/drm_scdc_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> -#include <drm/drm_hdcp.h> -#include <drm/drm_scdc_helper.h> #include <drm/intel_lpe_audio.h> #include "i915_debugfs.h" @@ -2637,7 +2639,7 @@ bool intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder, drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s] scrambling=%s, TMDS bit clock ratio=1/%d\n", connector->base.id, connector->name, - yesno(scrambling), high_tmds_clock_ratio ? 40 : 10); + str_yes_no(scrambling), high_tmds_clock_ratio ? 40 : 10); /* Set TMDS bit clock ratio to 1/40 or 1/10, and enable/disable scrambling */ return drm_scdc_set_high_tmds_clock_ratio(adapter, diff --git a/drivers/gpu/drm/i915/display/intel_lspcon.c b/drivers/gpu/drm/i915/display/intel_lspcon.c index 76357c9b76e4..7fbc8031a5aa 100644 --- a/drivers/gpu/drm/i915/display/intel_lspcon.c +++ b/drivers/gpu/drm/i915/display/intel_lspcon.c @@ -23,9 +23,9 @@ * */ +#include <drm/display/drm_dp_dual_mode_helper.h> +#include <drm/display/drm_hdmi_helper.h> #include <drm/drm_atomic_helper.h> -#include <drm/dp/drm_dp_dual_mode_helper.h> -#include <drm/drm_edid.h> #include "intel_de.h" #include "intel_display_types.h" diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c index 9fced37bed70..e8478161f8b9 100644 --- a/drivers/gpu/drm/i915/display/intel_lvds.c +++ b/drivers/gpu/drm/i915/display/intel_lvds.c @@ -389,7 +389,8 @@ intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; + const struct drm_display_mode *fixed_mode = + intel_panel_fixed_mode(intel_connector, mode); int max_pixclk = to_i915(connector->dev)->max_dotclk_freq; enum drm_mode_status status; @@ -475,19 +476,12 @@ static int intel_lvds_compute_config(struct intel_encoder *intel_encoder, static int intel_lvds_get_modes(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode; /* use cached edid if we have one */ if (!IS_ERR_OR_NULL(intel_connector->edid)) return drm_add_edid_modes(connector, intel_connector->edid); - mode = drm_mode_duplicate(dev, intel_connector->panel.fixed_mode); - if (mode == NULL) - return 0; - - drm_mode_probed_add(connector, mode); - return 1; + return intel_panel_get_modes(intel_connector); } static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { @@ -786,16 +780,18 @@ bool intel_is_dual_link_lvds(struct drm_i915_private *dev_priv) static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) { - struct drm_device *dev = lvds_encoder->base.base.dev; + struct drm_i915_private *dev_priv = to_i915(lvds_encoder->base.base.dev); + struct intel_connector *connector = lvds_encoder->attached_connector; + const struct drm_display_mode *fixed_mode = + intel_panel_preferred_fixed_mode(connector); unsigned int val; - struct drm_i915_private *dev_priv = to_i915(dev); /* use the module option value if specified */ if (dev_priv->params.lvds_channel_mode > 0) return dev_priv->params.lvds_channel_mode == 2; /* single channel LVDS is limited to 112 MHz */ - if (lvds_encoder->attached_connector->panel.fixed_mode->clock > 112999) + if (fixed_mode->clock > 112999) return true; if (dmi_check_system(intel_dual_link_lvds)) @@ -833,8 +829,6 @@ void intel_lvds_init(struct drm_i915_private *dev_priv) struct intel_connector *intel_connector; struct drm_connector *connector; struct drm_encoder *encoder; - struct drm_display_mode *fixed_mode = NULL; - struct drm_display_mode *downclock_mode = NULL; struct edid *edid; i915_reg_t lvds_reg; u32 lvds; @@ -973,35 +967,30 @@ void intel_lvds_init(struct drm_i915_private *dev_priv) } intel_connector->edid = edid; - fixed_mode = intel_panel_edid_fixed_mode(intel_connector); - if (fixed_mode) - goto out; + /* Try EDID first */ + intel_panel_add_edid_fixed_modes(intel_connector, + dev_priv->vbt.drrs_type != DRRS_TYPE_NONE); /* Failed to get EDID, what about VBT? */ - fixed_mode = intel_panel_vbt_fixed_mode(intel_connector); - if (fixed_mode) - goto out; + if (!intel_panel_preferred_fixed_mode(intel_connector)) + intel_panel_add_vbt_lfp_fixed_mode(intel_connector); /* - * If we didn't get EDID, try checking if the panel is already turned - * on. If so, assume that whatever is currently programmed is the - * correct mode. + * If we didn't get a fixed mode from EDID or VBT, try checking + * if the panel is already turned on. If so, assume that + * whatever is currently programmed is the correct mode. */ - fixed_mode = intel_encoder_current_mode(intel_encoder); - if (fixed_mode) { - drm_dbg_kms(&dev_priv->drm, "using current (BIOS) mode: "); - drm_mode_debug_printmodeline(fixed_mode); - fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; - } + if (!intel_panel_preferred_fixed_mode(intel_connector)) + intel_panel_add_encoder_fixed_mode(intel_connector, intel_encoder); + + mutex_unlock(&dev->mode_config.mutex); /* If we still don't have a mode after all that, give up. */ - if (!fixed_mode) + if (!intel_panel_preferred_fixed_mode(intel_connector)) goto failed; -out: - mutex_unlock(&dev->mode_config.mutex); + intel_panel_init(intel_connector); - intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); intel_backlight_setup(intel_connector, INVALID_PIPE); lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder); @@ -1013,8 +1002,6 @@ out: return; failed: - mutex_unlock(&dev->mode_config.mutex); - drm_dbg_kms(&dev_priv->drm, "No LVDS modes found, disabling.\n"); drm_connector_cleanup(connector); drm_encoder_cleanup(encoder); diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c index 76845d34ad0c..ee46561b5ae8 100644 --- a/drivers/gpu/drm/i915/display/intel_overlay.c +++ b/drivers/gpu/drm/i915/display/intel_overlay.c @@ -958,19 +958,21 @@ static void update_pfit_vscale_ratio(struct intel_overlay *overlay) static int check_overlay_dst(struct intel_overlay *overlay, struct drm_intel_overlay_put_image *rec) { - const struct intel_crtc_state *pipe_config = + const struct intel_crtc_state *crtc_state = overlay->crtc->config; + struct drm_rect req, clipped; - if (rec->dst_height == 0 || rec->dst_width == 0) - return -EINVAL; + drm_rect_init(&req, rec->dst_x, rec->dst_y, + rec->dst_width, rec->dst_height); - if (rec->dst_x < pipe_config->pipe_src_w && - rec->dst_x + rec->dst_width <= pipe_config->pipe_src_w && - rec->dst_y < pipe_config->pipe_src_h && - rec->dst_y + rec->dst_height <= pipe_config->pipe_src_h) - return 0; - else + clipped = req; + drm_rect_intersect(&clipped, &crtc_state->pipe_src); + + if (!drm_rect_visible(&clipped) || + !drm_rect_equals(&clipped, &req)) return -EINVAL; + + return 0; } static int check_overlay_scaling(struct drm_intel_overlay_put_image *rec) @@ -1160,7 +1162,7 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, crtc->overlay = overlay; /* line too wide, i.e. one-line-mode */ - if (crtc->config->pipe_src_w > 1024 && + if (drm_rect_width(&crtc->config->pipe_src) > 1024 && crtc->config->gmch_pfit.control & PFIT_ENABLE) { overlay->pfit_active = true; update_pfit_vscale_ratio(overlay); diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index a0c8e43db5eb..d1d1b59102d6 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -35,6 +35,7 @@ #include "intel_connector.h" #include "intel_de.h" #include "intel_display_types.h" +#include "intel_drrs.h" #include "intel_panel.h" bool intel_panel_use_ssc(struct drm_i915_private *i915) @@ -45,10 +46,87 @@ bool intel_panel_use_ssc(struct drm_i915_private *i915) && !(i915->quirks & QUIRK_LVDS_SSC_DISABLE); } +const struct drm_display_mode * +intel_panel_preferred_fixed_mode(struct intel_connector *connector) +{ + return list_first_entry_or_null(&connector->panel.fixed_modes, + struct drm_display_mode, head); +} + +const struct drm_display_mode * +intel_panel_fixed_mode(struct intel_connector *connector, + const struct drm_display_mode *mode) +{ + const struct drm_display_mode *fixed_mode, *best_mode = NULL; + int vrefresh = drm_mode_vrefresh(mode); + + /* pick the fixed_mode that is closest in terms of vrefresh */ + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { + if (!best_mode || + abs(drm_mode_vrefresh(fixed_mode) - vrefresh) < + abs(drm_mode_vrefresh(best_mode) - vrefresh)) + best_mode = fixed_mode; + } + + return best_mode; +} + +const struct drm_display_mode * +intel_panel_downclock_mode(struct intel_connector *connector, + const struct drm_display_mode *adjusted_mode) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + const struct drm_display_mode *fixed_mode, *best_mode = NULL; + int min_vrefresh = i915->vbt.seamless_drrs_min_refresh_rate; + int max_vrefresh = drm_mode_vrefresh(adjusted_mode); + + /* pick the fixed_mode with the lowest refresh rate */ + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { + int vrefresh = drm_mode_vrefresh(fixed_mode); + + if (vrefresh >= min_vrefresh && vrefresh < max_vrefresh) { + max_vrefresh = vrefresh; + best_mode = fixed_mode; + } + } + + return best_mode; +} + +int intel_panel_get_modes(struct intel_connector *connector) +{ + const struct drm_display_mode *fixed_mode; + int num_modes = 0; + + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->base.dev, fixed_mode); + if (mode) { + drm_mode_probed_add(&connector->base, mode); + num_modes++; + } + } + + return num_modes; +} + +enum drrs_type intel_panel_drrs_type(struct intel_connector *connector) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + + if (list_empty(&connector->panel.fixed_modes) || + list_is_singular(&connector->panel.fixed_modes)) + return DRRS_TYPE_NONE; + + return i915->vbt.drrs_type; +} + int intel_panel_compute_config(struct intel_connector *connector, struct drm_display_mode *adjusted_mode) { - const struct drm_display_mode *fixed_mode = connector->panel.fixed_mode; + const struct drm_display_mode *fixed_mode = + intel_panel_fixed_mode(connector, adjusted_mode); if (!fixed_mode) return 0; @@ -75,128 +153,142 @@ int intel_panel_compute_config(struct intel_connector *connector, return 0; } -static bool is_downclock_mode(const struct drm_display_mode *downclock_mode, - const struct drm_display_mode *fixed_mode) +static bool is_alt_fixed_mode(const struct drm_display_mode *mode, + const struct drm_display_mode *preferred_mode) { - return drm_mode_match(downclock_mode, fixed_mode, + return drm_mode_match(mode, preferred_mode, DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_FLAGS | DRM_MODE_MATCH_3D_FLAGS) && - downclock_mode->clock < fixed_mode->clock; + mode->clock != preferred_mode->clock; } -struct drm_display_mode * -intel_panel_edid_downclock_mode(struct intel_connector *connector, - const struct drm_display_mode *fixed_mode) +static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - const struct drm_display_mode *scan, *best_mode = NULL; - struct drm_display_mode *downclock_mode; - int best_clock = fixed_mode->clock; + const struct drm_display_mode *preferred_mode = + intel_panel_preferred_fixed_mode(connector); + struct drm_display_mode *mode, *next; - list_for_each_entry(scan, &connector->base.probed_modes, head) { - /* - * If one mode has the same resolution with the fixed_panel - * mode while they have the different refresh rate, it means - * that the reduced downclock is found. In such - * case we can set the different FPx0/1 to dynamically select - * between low and high frequency. - */ - if (is_downclock_mode(scan, fixed_mode) && - scan->clock < best_clock) { - /* - * The downclock is already found. But we - * expect to find the lower downclock. - */ - best_clock = scan->clock; - best_mode = scan; - } - } - - if (!best_mode) - return NULL; - - downclock_mode = drm_mode_duplicate(&dev_priv->drm, best_mode); - if (!downclock_mode) - return NULL; + list_for_each_entry_safe(mode, next, &connector->base.probed_modes, head) { + if (!is_alt_fixed_mode(mode, preferred_mode)) + continue; - drm_dbg_kms(&dev_priv->drm, - "[CONNECTOR:%d:%s] using downclock mode from EDID: ", - connector->base.base.id, connector->base.name); - drm_mode_debug_printmodeline(downclock_mode); + drm_dbg_kms(&dev_priv->drm, + "[CONNECTOR:%d:%s] using alternate EDID fixed mode: " DRM_MODE_FMT "\n", + connector->base.base.id, connector->base.name, + DRM_MODE_ARG(mode)); - return downclock_mode; + list_move_tail(&mode->head, &connector->panel.fixed_modes); + } } -struct drm_display_mode * -intel_panel_edid_fixed_mode(struct intel_connector *connector) +static void intel_panel_add_edid_preferred_mode(struct intel_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - const struct drm_display_mode *scan; - struct drm_display_mode *fixed_mode; + struct drm_display_mode *scan, *fixed_mode = NULL; if (list_empty(&connector->base.probed_modes)) - return NULL; + return; - /* prefer fixed mode from EDID if available */ + /* make sure the preferred mode is first */ list_for_each_entry(scan, &connector->base.probed_modes, head) { - if ((scan->type & DRM_MODE_TYPE_PREFERRED) == 0) - continue; + if (scan->type & DRM_MODE_TYPE_PREFERRED) { + fixed_mode = scan; + break; + } + } - fixed_mode = drm_mode_duplicate(&dev_priv->drm, scan); - if (!fixed_mode) - return NULL; + if (!fixed_mode) + fixed_mode = list_first_entry(&connector->base.probed_modes, + typeof(*fixed_mode), head); - drm_dbg_kms(&dev_priv->drm, - "[CONNECTOR:%d:%s] using preferred mode from EDID: ", - connector->base.base.id, connector->base.name); - drm_mode_debug_printmodeline(fixed_mode); + drm_dbg_kms(&dev_priv->drm, + "[CONNECTOR:%d:%s] using %s EDID fixed mode: " DRM_MODE_FMT "\n", + connector->base.base.id, connector->base.name, + fixed_mode->type & DRM_MODE_TYPE_PREFERRED ? "preferred" : "first", + DRM_MODE_ARG(fixed_mode)); + + fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; - return fixed_mode; + list_move_tail(&fixed_mode->head, &connector->panel.fixed_modes); +} + +static void intel_panel_destroy_probed_modes(struct intel_connector *connector) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct drm_display_mode *mode, *next; + + list_for_each_entry_safe(mode, next, &connector->base.probed_modes, head) { + list_del(&mode->head); + drm_mode_destroy(&i915->drm, mode); } +} - scan = list_first_entry(&connector->base.probed_modes, - typeof(*scan), head); +void intel_panel_add_edid_fixed_modes(struct intel_connector *connector, bool has_drrs) +{ + intel_panel_add_edid_preferred_mode(connector); + if (intel_panel_preferred_fixed_mode(connector) && has_drrs) + intel_panel_add_edid_alt_fixed_modes(connector); + intel_panel_destroy_probed_modes(connector); +} + +static void intel_panel_add_fixed_mode(struct intel_connector *connector, + struct drm_display_mode *fixed_mode, + const char *type) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct drm_display_info *info = &connector->base.display_info; - fixed_mode = drm_mode_duplicate(&dev_priv->drm, scan); if (!fixed_mode) - return NULL; + return; - fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; + fixed_mode->type |= DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; - drm_dbg_kms(&dev_priv->drm, - "[CONNECTOR:%d:%s] using first mode from EDID: ", - connector->base.base.id, connector->base.name); - drm_mode_debug_printmodeline(fixed_mode); + info->width_mm = fixed_mode->width_mm; + info->height_mm = fixed_mode->height_mm; - return fixed_mode; + drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] using %s fixed mode: " DRM_MODE_FMT "\n", + connector->base.base.id, connector->base.name, type, + DRM_MODE_ARG(fixed_mode)); + + list_add_tail(&fixed_mode->head, &connector->panel.fixed_modes); } -struct drm_display_mode * -intel_panel_vbt_fixed_mode(struct intel_connector *connector) +void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector) { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct drm_display_info *info = &connector->base.display_info; - struct drm_display_mode *fixed_mode; + struct drm_i915_private *i915 = to_i915(connector->base.dev); + const struct drm_display_mode *mode; - if (!dev_priv->vbt.lfp_lvds_vbt_mode) - return NULL; + mode = i915->vbt.lfp_lvds_vbt_mode; + if (!mode) + return; - fixed_mode = drm_mode_duplicate(&dev_priv->drm, - dev_priv->vbt.lfp_lvds_vbt_mode); - if (!fixed_mode) - return NULL; + intel_panel_add_fixed_mode(connector, + drm_mode_duplicate(&i915->drm, mode), + "VBT LFP"); +} - fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; +void intel_panel_add_vbt_sdvo_fixed_mode(struct intel_connector *connector) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + const struct drm_display_mode *mode; - drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s] using mode from VBT: ", - connector->base.base.id, connector->base.name); - drm_mode_debug_printmodeline(fixed_mode); + mode = i915->vbt.sdvo_lvds_vbt_mode; + if (!mode) + return; - info->width_mm = fixed_mode->width_mm; - info->height_mm = fixed_mode->height_mm; + intel_panel_add_fixed_mode(connector, + drm_mode_duplicate(&i915->drm, mode), + "VBT SDVO"); +} - return fixed_mode; +void intel_panel_add_encoder_fixed_mode(struct intel_connector *connector, + struct intel_encoder *encoder) +{ + intel_panel_add_fixed_mode(connector, + intel_encoder_current_mode(encoder), + "current (BIOS)"); } /* adjusted_mode has been preset to be the panel's fixed mode */ @@ -205,18 +297,20 @@ static int pch_panel_fitting(struct intel_crtc_state *crtc_state, { const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); + int pipe_src_h = drm_rect_height(&crtc_state->pipe_src); int x, y, width, height; /* Native modes don't need fitting */ - if (adjusted_mode->crtc_hdisplay == crtc_state->pipe_src_w && - adjusted_mode->crtc_vdisplay == crtc_state->pipe_src_h && + if (adjusted_mode->crtc_hdisplay == pipe_src_w && + adjusted_mode->crtc_vdisplay == pipe_src_h && crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420) return 0; switch (conn_state->scaling_mode) { case DRM_MODE_SCALE_CENTER: - width = crtc_state->pipe_src_w; - height = crtc_state->pipe_src_h; + width = pipe_src_w; + height = pipe_src_h; x = (adjusted_mode->crtc_hdisplay - width + 1)/2; y = (adjusted_mode->crtc_vdisplay - height + 1)/2; break; @@ -224,19 +318,17 @@ static int pch_panel_fitting(struct intel_crtc_state *crtc_state, case DRM_MODE_SCALE_ASPECT: /* Scale but preserve the aspect ratio */ { - u32 scaled_width = adjusted_mode->crtc_hdisplay - * crtc_state->pipe_src_h; - u32 scaled_height = crtc_state->pipe_src_w - * adjusted_mode->crtc_vdisplay; + u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h; + u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay; if (scaled_width > scaled_height) { /* pillar */ - width = scaled_height / crtc_state->pipe_src_h; + width = scaled_height / pipe_src_h; if (width & 1) width++; x = (adjusted_mode->crtc_hdisplay - width + 1) / 2; y = 0; height = adjusted_mode->crtc_vdisplay; } else if (scaled_width < scaled_height) { /* letter */ - height = scaled_width / crtc_state->pipe_src_w; + height = scaled_width / pipe_src_w; if (height & 1) height++; y = (adjusted_mode->crtc_vdisplay - height + 1) / 2; @@ -251,8 +343,8 @@ static int pch_panel_fitting(struct intel_crtc_state *crtc_state, break; case DRM_MODE_SCALE_NONE: - WARN_ON(adjusted_mode->crtc_hdisplay != crtc_state->pipe_src_w); - WARN_ON(adjusted_mode->crtc_vdisplay != crtc_state->pipe_src_h); + WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w); + WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h); fallthrough; case DRM_MODE_SCALE_FULLSCREEN: x = y = 0; @@ -333,10 +425,10 @@ static void i965_scale_aspect(struct intel_crtc_state *crtc_state, { const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - u32 scaled_width = adjusted_mode->crtc_hdisplay * - crtc_state->pipe_src_h; - u32 scaled_height = crtc_state->pipe_src_w * - adjusted_mode->crtc_vdisplay; + int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); + int pipe_src_h = drm_rect_height(&crtc_state->pipe_src); + u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h; + u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay; /* 965+ is easy, it does everything in hw */ if (scaled_width > scaled_height) @@ -345,7 +437,7 @@ static void i965_scale_aspect(struct intel_crtc_state *crtc_state, else if (scaled_width < scaled_height) *pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER; - else if (adjusted_mode->crtc_hdisplay != crtc_state->pipe_src_w) + else if (adjusted_mode->crtc_hdisplay != pipe_src_w) *pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; } @@ -354,10 +446,10 @@ static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state, u32 *border) { struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - u32 scaled_width = adjusted_mode->crtc_hdisplay * - crtc_state->pipe_src_h; - u32 scaled_height = crtc_state->pipe_src_w * - adjusted_mode->crtc_vdisplay; + int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); + int pipe_src_h = drm_rect_height(&crtc_state->pipe_src); + u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h; + u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay; u32 bits; /* @@ -367,12 +459,11 @@ static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state, */ if (scaled_width > scaled_height) { /* pillar */ centre_horizontally(adjusted_mode, - scaled_height / - crtc_state->pipe_src_h); + scaled_height / pipe_src_h); *border = LVDS_BORDER_ENABLE; - if (crtc_state->pipe_src_h != adjusted_mode->crtc_vdisplay) { - bits = panel_fitter_scaling(crtc_state->pipe_src_h, + if (pipe_src_h != adjusted_mode->crtc_vdisplay) { + bits = panel_fitter_scaling(pipe_src_h, adjusted_mode->crtc_vdisplay); *pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | @@ -383,12 +474,11 @@ static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state, } } else if (scaled_width < scaled_height) { /* letter */ centre_vertically(adjusted_mode, - scaled_width / - crtc_state->pipe_src_w); + scaled_width / pipe_src_w); *border = LVDS_BORDER_ENABLE; - if (crtc_state->pipe_src_w != adjusted_mode->crtc_hdisplay) { - bits = panel_fitter_scaling(crtc_state->pipe_src_w, + if (pipe_src_w != adjusted_mode->crtc_hdisplay) { + bits = panel_fitter_scaling(pipe_src_w, adjusted_mode->crtc_hdisplay); *pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | @@ -413,10 +503,12 @@ static int gmch_panel_fitting(struct intel_crtc_state *crtc_state, struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); + int pipe_src_h = drm_rect_height(&crtc_state->pipe_src); /* Native modes don't need fitting */ - if (adjusted_mode->crtc_hdisplay == crtc_state->pipe_src_w && - adjusted_mode->crtc_vdisplay == crtc_state->pipe_src_h) + if (adjusted_mode->crtc_hdisplay == pipe_src_w && + adjusted_mode->crtc_vdisplay == pipe_src_h) goto out; switch (conn_state->scaling_mode) { @@ -425,8 +517,8 @@ static int gmch_panel_fitting(struct intel_crtc_state *crtc_state, * For centered modes, we have to calculate border widths & * heights and modify the values programmed into the CRTC. */ - centre_horizontally(adjusted_mode, crtc_state->pipe_src_w); - centre_vertically(adjusted_mode, crtc_state->pipe_src_h); + centre_horizontally(adjusted_mode, pipe_src_w); + centre_vertically(adjusted_mode, pipe_src_h); border = LVDS_BORDER_ENABLE; break; case DRM_MODE_SCALE_ASPECT: @@ -442,8 +534,8 @@ static int gmch_panel_fitting(struct intel_crtc_state *crtc_state, * Full scaling, even if it changes the aspect ratio. * Fortunately this is all done for us in hw. */ - if (crtc_state->pipe_src_h != adjusted_mode->crtc_vdisplay || - crtc_state->pipe_src_w != adjusted_mode->crtc_hdisplay) { + if (pipe_src_h != adjusted_mode->crtc_vdisplay || + pipe_src_w != adjusted_mode->crtc_hdisplay) { pfit_control |= PFIT_ENABLE; if (DISPLAY_VER(dev_priv) >= 4) pfit_control |= PFIT_SCALING_AUTO; @@ -508,7 +600,8 @@ enum drm_mode_status intel_panel_mode_valid(struct intel_connector *connector, const struct drm_display_mode *mode) { - const struct drm_display_mode *fixed_mode = connector->panel.fixed_mode; + const struct drm_display_mode *fixed_mode = + intel_panel_fixed_mode(connector, mode); if (!fixed_mode) return MODE_OK; @@ -525,29 +618,29 @@ intel_panel_mode_valid(struct intel_connector *connector, return MODE_OK; } -int intel_panel_init(struct intel_panel *panel, - struct drm_display_mode *fixed_mode, - struct drm_display_mode *downclock_mode) +int intel_panel_init(struct intel_connector *connector) { + struct intel_panel *panel = &connector->panel; + intel_backlight_init_funcs(panel); - panel->fixed_mode = fixed_mode; - panel->downclock_mode = downclock_mode; + drm_dbg_kms(connector->base.dev, + "[CONNECTOR:%d:%s] DRRS type: %s\n", + connector->base.base.id, connector->base.name, + intel_drrs_type_str(intel_panel_drrs_type(connector))); return 0; } -void intel_panel_fini(struct intel_panel *panel) +void intel_panel_fini(struct intel_connector *connector) { - struct intel_connector *intel_connector = - container_of(panel, struct intel_connector, panel); + struct intel_panel *panel = &connector->panel; + struct drm_display_mode *fixed_mode, *next; intel_backlight_destroy(panel); - if (panel->fixed_mode) - drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode); - - if (panel->downclock_mode) - drm_mode_destroy(intel_connector->base.dev, - panel->downclock_mode); + list_for_each_entry_safe(fixed_mode, next, &panel->fixed_modes, head) { + list_del(&fixed_mode->head); + drm_mode_destroy(connector->base.dev, fixed_mode); + } } diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h index d50b3f7e9e58..2e32bb728beb 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.h +++ b/drivers/gpu/drm/i915/display/intel_panel.h @@ -9,23 +9,30 @@ #include <linux/types.h> enum drm_connector_status; +enum drrs_type; struct drm_connector; struct drm_connector_state; struct drm_display_mode; struct drm_i915_private; struct intel_connector; struct intel_crtc_state; -struct intel_panel; +struct intel_encoder; -int intel_panel_init(struct intel_panel *panel, - struct drm_display_mode *fixed_mode, - struct drm_display_mode *downclock_mode); -void intel_panel_fini(struct intel_panel *panel); +int intel_panel_init(struct intel_connector *connector); +void intel_panel_fini(struct intel_connector *connector); enum drm_connector_status intel_panel_detect(struct drm_connector *connector, bool force); bool intel_panel_use_ssc(struct drm_i915_private *i915); -void intel_panel_fixed_mode(const struct drm_display_mode *fixed_mode, - struct drm_display_mode *adjusted_mode); +const struct drm_display_mode * +intel_panel_preferred_fixed_mode(struct intel_connector *connector); +const struct drm_display_mode * +intel_panel_fixed_mode(struct intel_connector *connector, + const struct drm_display_mode *mode); +const struct drm_display_mode * +intel_panel_downclock_mode(struct intel_connector *connector, + const struct drm_display_mode *adjusted_mode); +int intel_panel_get_modes(struct intel_connector *connector); +enum drrs_type intel_panel_drrs_type(struct intel_connector *connector); enum drm_mode_status intel_panel_mode_valid(struct intel_connector *connector, const struct drm_display_mode *mode); @@ -33,12 +40,10 @@ int intel_panel_fitting(struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); int intel_panel_compute_config(struct intel_connector *connector, struct drm_display_mode *adjusted_mode); -struct drm_display_mode * -intel_panel_edid_downclock_mode(struct intel_connector *connector, - const struct drm_display_mode *fixed_mode); -struct drm_display_mode * -intel_panel_edid_fixed_mode(struct intel_connector *connector); -struct drm_display_mode * -intel_panel_vbt_fixed_mode(struct intel_connector *connector); +void intel_panel_add_edid_fixed_modes(struct intel_connector *connector, bool has_drrs); +void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector); +void intel_panel_add_vbt_sdvo_fixed_mode(struct intel_connector *connector); +void intel_panel_add_encoder_fixed_mode(struct intel_connector *connector, + struct intel_encoder *encoder); #endif /* __INTEL_PANEL_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_pch_display.c b/drivers/gpu/drm/i915/display/intel_pch_display.c index 9192769e3337..837152dca063 100644 --- a/drivers/gpu/drm/i915/display/intel_pch_display.c +++ b/drivers/gpu/drm/i915/display/intel_pch_display.c @@ -14,6 +14,23 @@ #include "intel_pps.h" #include "intel_sdvo.h" +bool intel_has_pch_trancoder(struct drm_i915_private *i915, + enum pipe pch_transcoder) +{ + return HAS_PCH_IBX(i915) || HAS_PCH_CPT(i915) || + (HAS_PCH_LPT_H(i915) && pch_transcoder == PIPE_A); +} + +enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc) +{ + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + + if (HAS_PCH_LPT(i915)) + return PIPE_A; + else + return crtc->pipe; +} + static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv, enum pipe pipe, enum port port, i915_reg_t dp_reg) @@ -88,6 +105,67 @@ static void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, pipe_name(pipe)); } +static void ibx_sanitize_pch_hdmi_port(struct drm_i915_private *dev_priv, + enum port port, i915_reg_t hdmi_reg) +{ + u32 val = intel_de_read(dev_priv, hdmi_reg); + + if (val & SDVO_ENABLE || + (val & SDVO_PIPE_SEL_MASK) == SDVO_PIPE_SEL(PIPE_A)) + return; + + drm_dbg_kms(&dev_priv->drm, + "Sanitizing transcoder select for HDMI %c\n", + port_name(port)); + + val &= ~SDVO_PIPE_SEL_MASK; + val |= SDVO_PIPE_SEL(PIPE_A); + + intel_de_write(dev_priv, hdmi_reg, val); +} + +static void ibx_sanitize_pch_dp_port(struct drm_i915_private *dev_priv, + enum port port, i915_reg_t dp_reg) +{ + u32 val = intel_de_read(dev_priv, dp_reg); + + if (val & DP_PORT_EN || + (val & DP_PIPE_SEL_MASK) == DP_PIPE_SEL(PIPE_A)) + return; + + drm_dbg_kms(&dev_priv->drm, + "Sanitizing transcoder select for DP %c\n", + port_name(port)); + + val &= ~DP_PIPE_SEL_MASK; + val |= DP_PIPE_SEL(PIPE_A); + + intel_de_write(dev_priv, dp_reg, val); +} + +static void ibx_sanitize_pch_ports(struct drm_i915_private *dev_priv) +{ + /* + * The BIOS may select transcoder B on some of the PCH + * ports even it doesn't enable the port. This would trip + * assert_pch_dp_disabled() and assert_pch_hdmi_disabled(). + * Sanitize the transcoder select bits to prevent that. We + * assume that the BIOS never actually enabled the port, + * because if it did we'd actually have to toggle the port + * on and back off to make the transcoder A select stick + * (see. intel_dp_link_down(), intel_disable_hdmi(), + * intel_disable_sdvo()). + */ + ibx_sanitize_pch_dp_port(dev_priv, PORT_B, PCH_DP_B); + ibx_sanitize_pch_dp_port(dev_priv, PORT_C, PCH_DP_C); + ibx_sanitize_pch_dp_port(dev_priv, PORT_D, PCH_DP_D); + + /* PCH SDVOB multiplex with HDMIB */ + ibx_sanitize_pch_hdmi_port(dev_priv, PORT_B, PCH_HDMIB); + ibx_sanitize_pch_hdmi_port(dev_priv, PORT_C, PCH_HDMIC); + ibx_sanitize_pch_hdmi_port(dev_priv, PORT_D, PCH_HDMID); +} + static void intel_pch_transcoder_set_m1_n1(struct intel_crtc *crtc, const struct intel_link_m_n *m_n) { @@ -181,7 +259,7 @@ static void ilk_enable_pch_transcoder(const struct intel_crtc_state *crtc_state) val |= TRANS_CHICKEN2_TIMING_OVERRIDE; /* Configure frame start delay to match the CPU */ val &= ~TRANS_CHICKEN2_FRAME_START_DELAY_MASK; - val |= TRANS_CHICKEN2_FRAME_START_DELAY(dev_priv->framestart_delay - 1); + val |= TRANS_CHICKEN2_FRAME_START_DELAY(crtc_state->framestart_delay - 1); intel_de_write(dev_priv, reg, val); } @@ -192,7 +270,7 @@ static void ilk_enable_pch_transcoder(const struct intel_crtc_state *crtc_state) if (HAS_PCH_IBX(dev_priv)) { /* Configure frame start delay to match the CPU */ val &= ~TRANS_FRAME_START_DELAY_MASK; - val |= TRANS_FRAME_START_DELAY(dev_priv->framestart_delay - 1); + val |= TRANS_FRAME_START_DELAY(crtc_state->framestart_delay - 1); /* * Make the BPC in transcoder be consistent with @@ -466,9 +544,11 @@ void ilk_pch_get_config(struct intel_crtc_state *crtc_state) ilk_pch_clock_get(crtc_state); } -static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, - enum transcoder cpu_transcoder) +static void lpt_enable_pch_transcoder(const struct intel_crtc_state *crtc_state) { + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; u32 val, pipeconf_val; /* FDI must be feeding us bits for PCH ports */ @@ -480,7 +560,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, val |= TRANS_CHICKEN2_TIMING_OVERRIDE; /* Configure frame start delay to match the CPU */ val &= ~TRANS_CHICKEN2_FRAME_START_DELAY_MASK; - val |= TRANS_CHICKEN2_FRAME_START_DELAY(dev_priv->framestart_delay - 1); + val |= TRANS_CHICKEN2_FRAME_START_DELAY(crtc_state->framestart_delay - 1); intel_de_write(dev_priv, TRANS_CHICKEN2(PIPE_A), val); val = TRANS_ENABLE; @@ -521,7 +601,6 @@ void lpt_pch_enable(struct intel_atomic_state *state, struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); const struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; assert_pch_transcoder_disabled(dev_priv, PIPE_A); @@ -530,7 +609,7 @@ void lpt_pch_enable(struct intel_atomic_state *state, /* Set transcoder timing. */ ilk_pch_transcoder_set_timings(crtc_state, PIPE_A); - lpt_enable_pch_transcoder(dev_priv, cpu_transcoder); + lpt_enable_pch_transcoder(crtc_state); } void lpt_pch_disable(struct intel_atomic_state *state, @@ -563,3 +642,9 @@ void lpt_pch_get_config(struct intel_crtc_state *crtc_state) crtc_state->hw.adjusted_mode.crtc_clock = lpt_get_iclkip(dev_priv); } + +void intel_pch_sanitize(struct drm_i915_private *i915) +{ + if (HAS_PCH_IBX(i915)) + ibx_sanitize_pch_ports(i915); +} diff --git a/drivers/gpu/drm/i915/display/intel_pch_display.h b/drivers/gpu/drm/i915/display/intel_pch_display.h index 749473d99320..41a63413cb3d 100644 --- a/drivers/gpu/drm/i915/display/intel_pch_display.h +++ b/drivers/gpu/drm/i915/display/intel_pch_display.h @@ -6,11 +6,19 @@ #ifndef _INTEL_PCH_DISPLAY_H_ #define _INTEL_PCH_DISPLAY_H_ +#include <linux/types.h> + +enum pipe; +struct drm_i915_private; struct intel_atomic_state; struct intel_crtc; struct intel_crtc_state; struct intel_link_m_n; +bool intel_has_pch_trancoder(struct drm_i915_private *i915, + enum pipe pch_transcoder); +enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc); + void ilk_pch_pre_enable(struct intel_atomic_state *state, struct intel_crtc *crtc); void ilk_pch_enable(struct intel_atomic_state *state, @@ -32,4 +40,6 @@ void intel_pch_transcoder_get_m1_n1(struct intel_crtc *crtc, void intel_pch_transcoder_get_m2_n2(struct intel_crtc *crtc, struct intel_link_m_n *m_n); +void intel_pch_sanitize(struct drm_i915_private *i915); + #endif diff --git a/drivers/gpu/drm/i915/display/intel_plane_initial.c b/drivers/gpu/drm/i915/display/intel_plane_initial.c index d7b1de4cc205..d10f27d0b7b0 100644 --- a/drivers/gpu/drm/i915/display/intel_plane_initial.c +++ b/drivers/gpu/drm/i915/display/intel_plane_initial.c @@ -3,6 +3,7 @@ * Copyright © 2021 Intel Corporation */ +#include "gem/i915_gem_region.h" #include "i915_drv.h" #include "intel_atomic_plane.h" #include "intel_display.h" @@ -46,16 +47,55 @@ static struct i915_vma * initial_plane_vma(struct drm_i915_private *i915, struct intel_initial_plane_config *plane_config) { - struct intel_memory_region *mem = i915->mm.stolen_region; + struct intel_memory_region *mem; struct drm_i915_gem_object *obj; struct i915_vma *vma; + resource_size_t phys_base; u32 base, size; + u64 pinctl; - if (!mem || plane_config->size == 0) + if (plane_config->size == 0) + return NULL; + + base = round_down(plane_config->base, I915_GTT_MIN_ALIGNMENT); + if (IS_DGFX(i915)) { + gen8_pte_t __iomem *gte = to_gt(i915)->ggtt->gsm; + gen8_pte_t pte; + + gte += base / I915_GTT_PAGE_SIZE; + + pte = ioread64(gte); + if (!(pte & GEN12_GGTT_PTE_LM)) { + drm_err(&i915->drm, + "Initial plane programming missing PTE_LM bit\n"); + return NULL; + } + + phys_base = pte & I915_GTT_PAGE_MASK; + mem = i915->mm.regions[INTEL_REGION_LMEM_0]; + + /* + * We don't currently expect this to ever be placed in the + * stolen portion. + */ + if (phys_base >= resource_size(&mem->region)) { + drm_err(&i915->drm, + "Initial plane programming using invalid range, phys_base=%pa\n", + &phys_base); + return NULL; + } + + drm_dbg(&i915->drm, + "Using phys_base=%pa, based on initial plane programming\n", + &phys_base); + } else { + phys_base = base; + mem = i915->mm.stolen_region; + } + + if (!mem) return NULL; - base = round_down(plane_config->base, - I915_GTT_MIN_ALIGNMENT); size = round_up(plane_config->base + plane_config->size, mem->min_page_size); size -= base; @@ -66,10 +106,11 @@ initial_plane_vma(struct drm_i915_private *i915, * features. */ if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE) && + mem == i915->mm.stolen_region && size * 2 > i915->stolen_usable_size) return NULL; - obj = i915_gem_object_create_stolen_for_preallocated(i915, base, size); + obj = i915_gem_object_create_region_at(mem, phys_base, size, 0); if (IS_ERR(obj)) return NULL; @@ -99,7 +140,10 @@ initial_plane_vma(struct drm_i915_private *i915, if (IS_ERR(vma)) goto err_obj; - if (i915_ggtt_pin(vma, NULL, 0, PIN_MAPPABLE | PIN_OFFSET_FIXED | base)) + pinctl = PIN_GLOBAL | PIN_OFFSET_FIXED | base; + if (HAS_GMCH(i915)) + pinctl |= PIN_MAPPABLE; + if (i915_vma_pin(vma, 0, 0, pinctl)) goto err_obj; if (i915_gem_object_is_tiled(obj) && @@ -127,6 +171,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, case DRM_FORMAT_MOD_LINEAR: case I915_FORMAT_MOD_X_TILED: case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_4_TILED: break; default: drm_dbg(&dev_priv->drm, diff --git a/drivers/gpu/drm/i915/display/intel_pps.c b/drivers/gpu/drm/i915/display/intel_pps.c index 64bd4ca0edd4..5a598dd06039 100644 --- a/drivers/gpu/drm/i915/display/intel_pps.c +++ b/drivers/gpu/drm/i915/display/intel_pps.c @@ -6,6 +6,7 @@ #include "g4x_dp.h" #include "i915_drv.h" #include "intel_de.h" +#include "intel_display_power_well.h" #include "intel_display_types.h" #include "intel_dp.h" #include "intel_dpll.h" diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 6c9e6e7f0afd..06db407e2749 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -100,11 +100,15 @@ static bool psr_global_enabled(struct intel_dp *intel_dp) static bool psr2_global_enabled(struct intel_dp *intel_dp) { + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + switch (intel_dp->psr.debug & I915_PSR_DEBUG_MODE_MASK) { case I915_PSR_DEBUG_DISABLE: case I915_PSR_DEBUG_FORCE_PSR1: return false; default: + if (i915->params.enable_psr == 1) + return false; return true; } } @@ -1221,6 +1225,7 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, intel_dp->psr.dc3co_exit_delay = val; intel_dp->psr.dc3co_exitline = crtc_state->dc3co_exitline; intel_dp->psr.psr2_sel_fetch_enabled = crtc_state->enable_psr2_sel_fetch; + intel_dp->psr.psr2_sel_fetch_cff_enabled = false; intel_dp->psr.req_psr2_sdp_prior_scanline = crtc_state->req_psr2_sdp_prior_scanline; @@ -1348,6 +1353,9 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) drm_dp_dpcd_writeb(&intel_dp->aux, DP_RECEIVER_ALPM_CONFIG, 0); intel_dp->psr.enabled = false; + intel_dp->psr.psr2_enabled = false; + intel_dp->psr.psr2_sel_fetch_enabled = false; + intel_dp->psr.psr2_sel_fetch_cff_enabled = false; } /** @@ -1436,28 +1444,42 @@ unlock: mutex_unlock(&psr->lock); } -static inline u32 man_trk_ctl_single_full_frame_bit_get(struct drm_i915_private *dev_priv) +static u32 man_trk_ctl_enable_bit_get(struct drm_i915_private *dev_priv) +{ + return IS_ALDERLAKE_P(dev_priv) ? 0 : PSR2_MAN_TRK_CTL_ENABLE; +} + +static u32 man_trk_ctl_single_full_frame_bit_get(struct drm_i915_private *dev_priv) { return IS_ALDERLAKE_P(dev_priv) ? ADLP_PSR2_MAN_TRK_CTL_SF_SINGLE_FULL_FRAME : PSR2_MAN_TRK_CTL_SF_SINGLE_FULL_FRAME; } -static inline u32 man_trk_ctl_partial_frame_bit_get(struct drm_i915_private *dev_priv) +static u32 man_trk_ctl_partial_frame_bit_get(struct drm_i915_private *dev_priv) { return IS_ALDERLAKE_P(dev_priv) ? ADLP_PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE : PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE; } +static u32 man_trk_ctl_continuos_full_frame(struct drm_i915_private *dev_priv) +{ + return IS_ALDERLAKE_P(dev_priv) ? + ADLP_PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME : + PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME; +} + static void psr_force_hw_tracking_exit(struct intel_dp *intel_dp) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); if (intel_dp->psr.psr2_sel_fetch_enabled) - intel_de_rmw(dev_priv, - PSR2_MAN_TRK_CTL(intel_dp->psr.transcoder), 0, - man_trk_ctl_single_full_frame_bit_get(dev_priv)); + intel_de_write(dev_priv, + PSR2_MAN_TRK_CTL(intel_dp->psr.transcoder), + man_trk_ctl_enable_bit_get(dev_priv) | + man_trk_ctl_partial_frame_bit_get(dev_priv) | + man_trk_ctl_single_full_frame_bit_get(dev_priv)); /* * Display WA #0884: skl+ @@ -1541,10 +1563,21 @@ void intel_psr2_program_plane_sel_fetch(struct intel_plane *plane, void intel_psr2_program_trans_man_trk_ctl(const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); + struct intel_encoder *encoder; if (!crtc_state->enable_psr2_sel_fetch) return; + for_each_intel_encoder_mask_with_psr(&dev_priv->drm, encoder, + crtc_state->uapi.encoder_mask) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + lockdep_assert_held(&intel_dp->psr.lock); + if (intel_dp->psr.psr2_sel_fetch_cff_enabled) + return; + break; + } + intel_de_write(dev_priv, PSR2_MAN_TRK_CTL(crtc_state->cpu_transcoder), crtc_state->psr2_man_track_ctl); } @@ -1554,10 +1587,7 @@ static void psr2_man_trk_ctl_calc(struct intel_crtc_state *crtc_state, { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - u32 val = 0; - - if (!IS_ALDERLAKE_P(dev_priv)) - val = PSR2_MAN_TRK_CTL_ENABLE; + u32 val = man_trk_ctl_enable_bit_get(dev_priv); /* SF partial frame enable has to be set even on full update */ val |= man_trk_ctl_partial_frame_bit_get(dev_priv); @@ -1915,13 +1945,13 @@ static int _psr1_ready_for_pipe_update_locked(struct intel_dp *intel_dp) } /** - * intel_psr_wait_for_idle - wait for PSR be ready for a pipe update + * intel_psr_wait_for_idle_locked - wait for PSR be ready for a pipe update * @new_crtc_state: new CRTC state * * This function is expected to be called from pipe_update_start() where it is * not expected to race with PSR enable or disable. */ -void intel_psr_wait_for_idle(const struct intel_crtc_state *new_crtc_state) +void intel_psr_wait_for_idle_locked(const struct intel_crtc_state *new_crtc_state) { struct drm_i915_private *dev_priv = to_i915(new_crtc_state->uapi.crtc->dev); struct intel_encoder *encoder; @@ -1934,12 +1964,10 @@ void intel_psr_wait_for_idle(const struct intel_crtc_state *new_crtc_state) struct intel_dp *intel_dp = enc_to_intel_dp(encoder); int ret; - mutex_lock(&intel_dp->psr.lock); + lockdep_assert_held(&intel_dp->psr.lock); - if (!intel_dp->psr.enabled) { - mutex_unlock(&intel_dp->psr.lock); + if (!intel_dp->psr.enabled) continue; - } if (intel_dp->psr.psr2_enabled) ret = _psr2_ready_for_pipe_update_locked(intel_dp); @@ -1948,8 +1976,6 @@ void intel_psr_wait_for_idle(const struct intel_crtc_state *new_crtc_state) if (ret) drm_err(&dev_priv->drm, "PSR wait timed out, atomic update may fail\n"); - - mutex_unlock(&intel_dp->psr.lock); } } @@ -2126,6 +2152,27 @@ unlock: mutex_unlock(&intel_dp->psr.lock); } +static void _psr_invalidate_handle(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + if (intel_dp->psr.psr2_sel_fetch_enabled) { + u32 val; + + if (intel_dp->psr.psr2_sel_fetch_cff_enabled) + return; + + val = man_trk_ctl_enable_bit_get(dev_priv) | + man_trk_ctl_partial_frame_bit_get(dev_priv) | + man_trk_ctl_continuos_full_frame(dev_priv); + intel_de_write(dev_priv, PSR2_MAN_TRK_CTL(intel_dp->psr.transcoder), val); + intel_de_write(dev_priv, CURSURFLIVE(intel_dp->psr.pipe), 0); + intel_dp->psr.psr2_sel_fetch_cff_enabled = true; + } else { + intel_psr_exit(intel_dp); + } +} + /** * intel_psr_invalidate - Invalidade PSR * @dev_priv: i915 device @@ -2162,7 +2209,7 @@ void intel_psr_invalidate(struct drm_i915_private *dev_priv, intel_dp->psr.busy_frontbuffer_bits |= pipe_frontbuffer_bits; if (pipe_frontbuffer_bits) - intel_psr_exit(intel_dp); + _psr_invalidate_handle(intel_dp); mutex_unlock(&intel_dp->psr.lock); } @@ -2194,6 +2241,42 @@ tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits, intel_dp->psr.dc3co_exit_delay); } +static void _psr_flush_handle(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + if (intel_dp->psr.psr2_sel_fetch_enabled) { + if (intel_dp->psr.psr2_sel_fetch_cff_enabled) { + /* can we turn CFF off? */ + if (intel_dp->psr.busy_frontbuffer_bits == 0) { + u32 val = man_trk_ctl_enable_bit_get(dev_priv) | + man_trk_ctl_partial_frame_bit_get(dev_priv) | + man_trk_ctl_single_full_frame_bit_get(dev_priv); + + /* + * turn continuous full frame off and do a single + * full frame + */ + intel_de_write(dev_priv, PSR2_MAN_TRK_CTL(intel_dp->psr.transcoder), + val); + intel_de_write(dev_priv, CURSURFLIVE(intel_dp->psr.pipe), 0); + intel_dp->psr.psr2_sel_fetch_cff_enabled = false; + } + } else { + /* + * continuous full frame is disabled, only a single full + * frame is required + */ + psr_force_hw_tracking_exit(intel_dp); + } + } else { + psr_force_hw_tracking_exit(intel_dp); + + if (!intel_dp->psr.active && !intel_dp->psr.busy_frontbuffer_bits) + schedule_work(&intel_dp->psr.work); + } +} + /** * intel_psr_flush - Flush PSR * @dev_priv: i915 device @@ -2231,25 +2314,22 @@ void intel_psr_flush(struct drm_i915_private *dev_priv, * we have to ensure that the PSR is not activated until * intel_psr_resume() is called. */ - if (intel_dp->psr.paused) { - mutex_unlock(&intel_dp->psr.lock); - continue; - } + if (intel_dp->psr.paused) + goto unlock; if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { tgl_dc3co_flush_locked(intel_dp, frontbuffer_bits, origin); - mutex_unlock(&intel_dp->psr.lock); - continue; + goto unlock; } - /* By definition flush = invalidate + flush */ - if (pipe_frontbuffer_bits) - psr_force_hw_tracking_exit(intel_dp); + if (pipe_frontbuffer_bits == 0) + goto unlock; - if (!intel_dp->psr.active && !intel_dp->psr.busy_frontbuffer_bits) - schedule_work(&intel_dp->psr.work); + /* By definition flush = invalidate + flush */ + _psr_flush_handle(intel_dp); +unlock: mutex_unlock(&intel_dp->psr.lock); } } @@ -2440,3 +2520,51 @@ bool intel_psr_enabled(struct intel_dp *intel_dp) return ret; } + +/** + * intel_psr_lock - grab PSR lock + * @crtc_state: the crtc state + * + * This is initially meant to be used by around CRTC update, when + * vblank sensitive registers are updated and we need grab the lock + * before it to avoid vblank evasion. + */ +void intel_psr_lock(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); + struct intel_encoder *encoder; + + if (!crtc_state->has_psr) + return; + + for_each_intel_encoder_mask_with_psr(&i915->drm, encoder, + crtc_state->uapi.encoder_mask) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + mutex_lock(&intel_dp->psr.lock); + break; + } +} + +/** + * intel_psr_unlock - release PSR lock + * @crtc_state: the crtc state + * + * Release the PSR lock that was held during pipe update. + */ +void intel_psr_unlock(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); + struct intel_encoder *encoder; + + if (!crtc_state->has_psr) + return; + + for_each_intel_encoder_mask_with_psr(&i915->drm, encoder, + crtc_state->uapi.encoder_mask) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + mutex_unlock(&intel_dp->psr.lock); + break; + } +} diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h index f6526d9ccfdc..2ac3a46cccc5 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.h +++ b/drivers/gpu/drm/i915/display/intel_psr.h @@ -41,7 +41,7 @@ void intel_psr_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config); void intel_psr_irq_handler(struct intel_dp *intel_dp, u32 psr_iir); void intel_psr_short_pulse(struct intel_dp *intel_dp); -void intel_psr_wait_for_idle(const struct intel_crtc_state *new_crtc_state); +void intel_psr_wait_for_idle_locked(const struct intel_crtc_state *new_crtc_state); bool intel_psr_enabled(struct intel_dp *intel_dp); int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, struct intel_crtc *crtc); @@ -55,4 +55,7 @@ void intel_psr2_disable_plane_sel_fetch(struct intel_plane *plane, void intel_psr_pause(struct intel_dp *intel_dp); void intel_psr_resume(struct intel_dp *intel_dp); +void intel_psr_lock(const struct intel_crtc_state *crtc_state); +void intel_psr_unlock(const struct intel_crtc_state *crtc_state); + #endif /* __INTEL_PSR_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_qp_tables.c b/drivers/gpu/drm/i915/display/intel_qp_tables.c index c626a24fe98f..6f8e4ec5c0fb 100644 --- a/drivers/gpu/drm/i915/display/intel_qp_tables.c +++ b/drivers/gpu/drm/i915/display/intel_qp_tables.c @@ -3,7 +3,7 @@ * Copyright © 2021 Intel Corporation */ -#include <drm/drm_dsc.h> +#include <drm/display/drm_dsc.h> #include "i915_utils.h" #include "intel_qp_tables.h" diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index 76e1188b01d4..d81855d57cdc 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -31,6 +31,7 @@ #include <linux/i2c.h> #include <linux/slab.h> +#include <drm/display/drm_hdmi_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> @@ -283,7 +284,7 @@ static bool intel_sdvo_read_byte(struct intel_sdvo *intel_sdvo, u8 addr, u8 *ch) static const struct { u8 cmd; const char *name; -} __attribute__ ((packed)) sdvo_cmd_names[] = { +} __packed sdvo_cmd_names[] = { SDVO_CMD_NAME_ENTRY(RESET), SDVO_CMD_NAME_ENTRY(GET_DEVICE_CAPS), SDVO_CMD_NAME_ENTRY(GET_FIRMWARE_REV), @@ -783,24 +784,22 @@ static bool intel_sdvo_get_input_timing(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_create_preferred_input_timing(struct intel_sdvo *intel_sdvo, struct intel_sdvo_connector *intel_sdvo_connector, - u16 clock, - u16 width, - u16 height) + const struct drm_display_mode *mode) { struct intel_sdvo_preferred_input_timing_args args; memset(&args, 0, sizeof(args)); - args.clock = clock; - args.width = width; - args.height = height; + args.clock = mode->clock / 10; + args.width = mode->hdisplay; + args.height = mode->vdisplay; args.interlace = 0; if (IS_LVDS(intel_sdvo_connector)) { const struct drm_display_mode *fixed_mode = - intel_sdvo_connector->base.panel.fixed_mode; + intel_panel_fixed_mode(&intel_sdvo_connector->base, mode); - if (fixed_mode->hdisplay != width || - fixed_mode->vdisplay != height) + if (fixed_mode->hdisplay != args.width || + fixed_mode->vdisplay != args.height) args.scaled = 1; } @@ -1236,9 +1235,7 @@ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo, if (!intel_sdvo_create_preferred_input_timing(intel_sdvo, intel_sdvo_connector, - mode->clock / 10, - mode->hdisplay, - mode->vdisplay)) + mode)) return false; if (!intel_sdvo_get_preferred_input_timing(intel_sdvo, @@ -1335,6 +1332,8 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder, adjusted_mode); pipe_config->sdvo_tv_clock = true; } else if (IS_LVDS(intel_sdvo_connector)) { + const struct drm_display_mode *fixed_mode = + intel_panel_fixed_mode(&intel_sdvo_connector->base, mode); int ret; ret = intel_panel_compute_config(&intel_sdvo_connector->base, @@ -1342,8 +1341,7 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder, if (ret) return ret; - if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, - intel_sdvo_connector->base.panel.fixed_mode)) + if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, fixed_mode)) return -EINVAL; (void) intel_sdvo_get_preferred_input_mode(intel_sdvo, @@ -1465,7 +1463,7 @@ static void intel_sdvo_pre_enable(struct intel_atomic_state *state, const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; const struct intel_sdvo_connector_state *sdvo_state = to_intel_sdvo_connector_state(conn_state); - const struct intel_sdvo_connector *intel_sdvo_connector = + struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(conn_state->connector); const struct drm_display_mode *mode = &crtc_state->hw.mode; struct intel_sdvo *intel_sdvo = to_sdvo(intel_encoder); @@ -1496,11 +1494,14 @@ static void intel_sdvo_pre_enable(struct intel_atomic_state *state, return; /* lvds has a special fixed output timing. */ - if (IS_LVDS(intel_sdvo_connector)) - intel_sdvo_get_dtd_from_mode(&output_dtd, - intel_sdvo_connector->base.panel.fixed_mode); - else + if (IS_LVDS(intel_sdvo_connector)) { + const struct drm_display_mode *fixed_mode = + intel_panel_fixed_mode(&intel_sdvo_connector->base, mode); + + intel_sdvo_get_dtd_from_mode(&output_dtd, fixed_mode); + } else { intel_sdvo_get_dtd_from_mode(&output_dtd, mode); + } if (!intel_sdvo_set_output_timing(intel_sdvo, &output_dtd)) drm_info(&dev_priv->drm, "Setting output timings on %s failed\n", @@ -2291,33 +2292,12 @@ static int intel_sdvo_get_lvds_modes(struct drm_connector *connector) { struct intel_sdvo *intel_sdvo = intel_attached_sdvo(to_intel_connector(connector)); struct drm_i915_private *dev_priv = to_i915(connector->dev); - struct drm_display_mode *newmode; int num_modes = 0; drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); - /* - * Fetch modes from VBT. For SDVO prefer the VBT mode since some - * SDVO->LVDS transcoders can't cope with the EDID mode. - */ - if (dev_priv->vbt.sdvo_lvds_vbt_mode != NULL) { - newmode = drm_mode_duplicate(connector->dev, - dev_priv->vbt.sdvo_lvds_vbt_mode); - if (newmode != NULL) { - /* Guarantee the mode is preferred */ - newmode->type = (DRM_MODE_TYPE_PREFERRED | - DRM_MODE_TYPE_DRIVER); - drm_mode_probed_add(connector, newmode); - num_modes++; - } - } - - /* - * Attempt to get the mode list from DDC. - * Assume that the preferred modes are - * arranged in priority order. - */ + num_modes += intel_panel_get_modes(to_intel_connector(connector)); num_modes += intel_ddc_get_modes(connector, &intel_sdvo->ddc); return num_modes; @@ -2747,6 +2727,8 @@ static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void) __drm_atomic_helper_connector_reset(&sdvo_connector->base.base, &conn_state->base.base); + INIT_LIST_HEAD(&sdvo_connector->base.panel.fixed_modes); + return sdvo_connector; } @@ -2890,7 +2872,6 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device) struct drm_connector *connector; struct intel_connector *intel_connector; struct intel_sdvo_connector *intel_sdvo_connector; - struct drm_display_mode *mode; DRM_DEBUG_KMS("initialising LVDS device %d\n", device); @@ -2919,20 +2900,20 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device) if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector)) goto err; - intel_sdvo_get_lvds_modes(connector); - - list_for_each_entry(mode, &connector->probed_modes, head) { - if (mode->type & DRM_MODE_TYPE_PREFERRED) { - struct drm_display_mode *fixed_mode = - drm_mode_duplicate(connector->dev, mode); + /* + * Fetch modes from VBT. For SDVO prefer the VBT mode since some + * SDVO->LVDS transcoders can't cope with the EDID mode. + */ + intel_panel_add_vbt_sdvo_fixed_mode(intel_connector); - intel_panel_init(&intel_connector->panel, - fixed_mode, NULL); - break; - } + if (!intel_panel_preferred_fixed_mode(intel_connector)) { + intel_ddc_get_modes(connector, &intel_sdvo->ddc); + intel_panel_add_edid_fixed_modes(intel_connector, false); } - if (!intel_connector->panel.fixed_mode) + intel_panel_init(intel_connector); + + if (!intel_panel_preferred_fixed_mode(intel_connector)) goto err; return true; diff --git a/drivers/gpu/drm/i915/display/intel_snps_phy.c b/drivers/gpu/drm/i915/display/intel_snps_phy.c index 7e6245b97fed..0dd4775e8195 100644 --- a/drivers/gpu/drm/i915/display/intel_snps_phy.c +++ b/drivers/gpu/drm/i915/display/intel_snps_phy.c @@ -32,10 +32,14 @@ void intel_snps_phy_wait_for_calibration(struct drm_i915_private *i915) if (!intel_phy_is_snps(i915, phy)) continue; + /* + * If calibration does not complete successfully, we'll remember + * which phy was affected and skip setup of the corresponding + * output later. + */ if (intel_de_wait_for_clear(i915, DG2_PHY_MISC(phy), DG2_PHY_DP_TX_ACK_MASK, 25)) - drm_err(&i915->drm, "SNPS PHY %c failed to calibrate after 25ms.\n", - phy_name(phy)); + i915->snps_phy_failed_calibration |= BIT(phy); } } diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c index 2d71294aaceb..7c0df80612d0 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite.c +++ b/drivers/gpu/drm/i915/display/intel_sprite.c @@ -30,6 +30,8 @@ * support. */ +#include <linux/string_helpers.h> + #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_color_mgmt.h> @@ -96,13 +98,13 @@ int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state) if (src_x % hsub || src_w % hsub) { drm_dbg_kms(&i915->drm, "src x/w (%u, %u) must be a multiple of %u (rotated: %s)\n", - src_x, src_w, hsub, yesno(rotated)); + src_x, src_w, hsub, str_yes_no(rotated)); return -EINVAL; } if (src_y % vsub || src_h % vsub) { drm_dbg_kms(&i915->drm, "src y/h (%u, %u) must be a multiple of %u (rotated: %s)\n", - src_y, src_h, vsub, yesno(rotated)); + src_y, src_h, vsub, str_yes_no(rotated)); return -EINVAL; } @@ -430,9 +432,6 @@ vlv_sprite_update_noarm(struct intel_plane *plane, int crtc_y = plane_state->uapi.dst.y1; u32 crtc_w = drm_rect_width(&plane_state->uapi.dst); u32 crtc_h = drm_rect_height(&plane_state->uapi.dst); - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, SPSTRIDE(pipe, plane_id), plane_state->view.color_plane[0].mapping_stride); @@ -440,8 +439,6 @@ vlv_sprite_update_noarm(struct intel_plane *plane, SP_POS_Y(crtc_y) | SP_POS_X(crtc_x)); intel_de_write_fw(dev_priv, SPSIZE(pipe, plane_id), SP_HEIGHT(crtc_h - 1) | SP_WIDTH(crtc_w - 1)); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -457,14 +454,11 @@ vlv_sprite_update_arm(struct intel_plane *plane, u32 x = plane_state->view.color_plane[0].x; u32 y = plane_state->view.color_plane[0].y; u32 sprctl, linear_offset; - unsigned long irqflags; sprctl = plane_state->ctl | vlv_sprite_ctl_crtc(crtc_state); linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B) chv_sprite_update_csc(plane_state); @@ -494,8 +488,6 @@ vlv_sprite_update_arm(struct intel_plane *plane, vlv_sprite_update_clrc(plane_state); vlv_sprite_update_gamma(plane_state); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -505,14 +497,9 @@ vlv_sprite_disable_arm(struct intel_plane *plane, struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; enum plane_id plane_id = plane->id; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, SPCNTR(pipe, plane_id), 0); intel_de_write_fw(dev_priv, SPSURF(pipe, plane_id), 0); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static bool @@ -862,15 +849,12 @@ ivb_sprite_update_noarm(struct intel_plane *plane, u32 src_w = drm_rect_width(&plane_state->uapi.src) >> 16; u32 src_h = drm_rect_height(&plane_state->uapi.src) >> 16; u32 sprscale = 0; - unsigned long irqflags; if (crtc_w != src_w || crtc_h != src_h) sprscale = SPRITE_SCALE_ENABLE | SPRITE_SRC_WIDTH(src_w - 1) | SPRITE_SRC_HEIGHT(src_h - 1); - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - intel_de_write_fw(dev_priv, SPRSTRIDE(pipe), plane_state->view.color_plane[0].mapping_stride); intel_de_write_fw(dev_priv, SPRPOS(pipe), @@ -879,8 +863,6 @@ ivb_sprite_update_noarm(struct intel_plane *plane, SPRITE_HEIGHT(crtc_h - 1) | SPRITE_WIDTH(crtc_w - 1)); if (IS_IVYBRIDGE(dev_priv)) intel_de_write_fw(dev_priv, SPRSCALE(pipe), sprscale); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -895,14 +877,11 @@ ivb_sprite_update_arm(struct intel_plane *plane, u32 x = plane_state->view.color_plane[0].x; u32 y = plane_state->view.color_plane[0].y; u32 sprctl, linear_offset; - unsigned long irqflags; sprctl = plane_state->ctl | ivb_sprite_ctl_crtc(crtc_state); linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - if (key->flags) { intel_de_write_fw(dev_priv, SPRKEYVAL(pipe), key->min_value); intel_de_write_fw(dev_priv, SPRKEYMSK(pipe), @@ -931,8 +910,6 @@ ivb_sprite_update_arm(struct intel_plane *plane, intel_plane_ggtt_offset(plane_state) + sprsurf_offset); ivb_sprite_update_gamma(plane_state); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -941,17 +918,12 @@ ivb_sprite_disable_arm(struct intel_plane *plane, { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, SPRCTL(pipe), 0); /* Disable the scaler */ if (IS_IVYBRIDGE(dev_priv)) intel_de_write_fw(dev_priv, SPRSCALE(pipe), 0); intel_de_write_fw(dev_priv, SPRSURF(pipe), 0); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static bool @@ -1204,15 +1176,12 @@ g4x_sprite_update_noarm(struct intel_plane *plane, u32 src_w = drm_rect_width(&plane_state->uapi.src) >> 16; u32 src_h = drm_rect_height(&plane_state->uapi.src) >> 16; u32 dvsscale = 0; - unsigned long irqflags; if (crtc_w != src_w || crtc_h != src_h) dvsscale = DVS_SCALE_ENABLE | DVS_SRC_WIDTH(src_w - 1) | DVS_SRC_HEIGHT(src_h - 1); - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - intel_de_write_fw(dev_priv, DVSSTRIDE(pipe), plane_state->view.color_plane[0].mapping_stride); intel_de_write_fw(dev_priv, DVSPOS(pipe), @@ -1220,8 +1189,6 @@ g4x_sprite_update_noarm(struct intel_plane *plane, intel_de_write_fw(dev_priv, DVSSIZE(pipe), DVS_HEIGHT(crtc_h - 1) | DVS_WIDTH(crtc_w - 1)); intel_de_write_fw(dev_priv, DVSSCALE(pipe), dvsscale); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -1236,14 +1203,11 @@ g4x_sprite_update_arm(struct intel_plane *plane, u32 x = plane_state->view.color_plane[0].x; u32 y = plane_state->view.color_plane[0].y; u32 dvscntr, linear_offset; - unsigned long irqflags; dvscntr = plane_state->ctl | g4x_sprite_ctl_crtc(crtc_state); linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - if (key->flags) { intel_de_write_fw(dev_priv, DVSKEYVAL(pipe), key->min_value); intel_de_write_fw(dev_priv, DVSKEYMSK(pipe), @@ -1267,8 +1231,6 @@ g4x_sprite_update_arm(struct intel_plane *plane, g4x_sprite_update_gamma(plane_state); else ilk_sprite_update_gamma(plane_state); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -1277,16 +1239,11 @@ g4x_sprite_disable_arm(struct intel_plane *plane, { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, DVSCNTR(pipe), 0); /* Disable the scaler */ intel_de_write_fw(dev_priv, DVSSCALE(pipe), 0); intel_de_write_fw(dev_priv, DVSSURF(pipe), 0); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static bool diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index fc037c027ea5..b8b822ea3755 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -6,6 +6,7 @@ #include "i915_drv.h" #include "i915_reg.h" #include "intel_display.h" +#include "intel_display_power_map.h" #include "intel_display_types.h" #include "intel_dp_mst.h" #include "intel_tc.h" @@ -61,10 +62,12 @@ bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port) static enum intel_display_power_domain tc_cold_get_power_domain(struct intel_digital_port *dig_port, enum tc_port_mode mode) { + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + if (mode == TC_PORT_TBT_ALT || !intel_tc_cold_requires_aux_pw(dig_port)) return POWER_DOMAIN_TC_COLD_OFF; - return intel_legacy_aux_to_power_domain(dig_port->aux_ch); + return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch); } static intel_wakeref_t diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index 8a39989b87ad..9379f3463344 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -1145,8 +1145,8 @@ intel_tv_get_config(struct intel_encoder *encoder, intel_tv_mode_to_mode(&mode, &tv_mode); - drm_dbg_kms(&dev_priv->drm, "TV mode:\n"); - drm_mode_debug_printmodeline(&mode); + drm_dbg_kms(&dev_priv->drm, "TV mode: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(&mode)); intel_tv_scale_mode_horiz(&mode, hdisplay, xpos, mode.hdisplay - xsize - xpos); @@ -1250,8 +1250,8 @@ intel_tv_compute_config(struct intel_encoder *encoder, tv_conn_state->bypass_vfilter = false; } - drm_dbg_kms(&dev_priv->drm, "TV mode:\n"); - drm_mode_debug_printmodeline(adjusted_mode); + drm_dbg_kms(&dev_priv->drm, "TV mode: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(adjusted_mode)); /* * The pipe scanline counter behaviour looks as follows when @@ -1806,8 +1806,8 @@ intel_tv_get_modes(struct drm_connector *connector) */ intel_tv_mode_to_mode(mode, tv_mode); if (count == 0) { - drm_dbg_kms(&dev_priv->drm, "TV mode:\n"); - drm_mode_debug_printmodeline(mode); + drm_dbg_kms(&dev_priv->drm, "TV mode: " DRM_MODE_FMT "\n", + DRM_MODE_ARG(mode)); } intel_tv_scale_mode_horiz(mode, input->w, 0, 0); intel_tv_scale_mode_vert(mode, input->h, 0, 0); diff --git a/drivers/gpu/drm/i915/display/intel_vbt_defs.h b/drivers/gpu/drm/i915/display/intel_vbt_defs.h index b9397d9363c5..4b98bab3b890 100644 --- a/drivers/gpu/drm/i915/display/intel_vbt_defs.h +++ b/drivers/gpu/drm/i915/display/intel_vbt_defs.h @@ -289,6 +289,9 @@ struct bdb_general_features { #define HDMI_MAX_DATA_RATE_PLATFORM 0 /* 204 */ #define HDMI_MAX_DATA_RATE_297 1 /* 204 */ #define HDMI_MAX_DATA_RATE_165 2 /* 204 */ +#define HDMI_MAX_DATA_RATE_594 3 /* 249 */ +#define HDMI_MAX_DATA_RATE_340 4 /* 249 */ +#define HDMI_MAX_DATA_RATE_300 5 /* 249 */ #define LEGACY_CHILD_DEVICE_CONFIG_SIZE 33 @@ -719,20 +722,22 @@ struct bdb_lvds_options { /* * Block 41 - LFP Data Table Pointers */ +struct lvds_lfp_data_ptr_table { + u16 offset; /* offsets are from start of bdb */ + u8 table_size; +} __packed; /* LFP pointer table contains entries to the struct below */ struct lvds_lfp_data_ptr { - u16 fp_timing_offset; /* offsets are from start of bdb */ - u8 fp_table_size; - u16 dvo_timing_offset; - u8 dvo_table_size; - u16 panel_pnp_id_offset; - u8 pnp_table_size; + struct lvds_lfp_data_ptr_table fp_timing; + struct lvds_lfp_data_ptr_table dvo_timing; + struct lvds_lfp_data_ptr_table panel_pnp_id; } __packed; struct bdb_lvds_lfp_data_ptrs { - u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */ + u8 lvds_entries; struct lvds_lfp_data_ptr ptr[16]; + struct lvds_lfp_data_ptr_table panel_name; /* 156-163? */ } __packed; /* @@ -764,6 +769,11 @@ struct lvds_pnp_id { u8 mfg_year; } __packed; +/* + * For reference only. fp_timing has variable size so + * the data must be accessed using the data table pointers. + * Do not use this directly! + */ struct lvds_lfp_data_entry { struct lvds_fp_timing fp_timing; struct lvds_dvo_timing dvo_timing; @@ -774,6 +784,27 @@ struct bdb_lvds_lfp_data { struct lvds_lfp_data_entry data[16]; } __packed; +struct lvds_lfp_panel_name { + u8 name[13]; +} __packed; + +struct lvds_lfp_black_border { + u8 top; /* 227 */ + u8 bottom; /* 227 */ + u8 left; /* 238 */ + u8 right; /* 238 */ +} __packed; + +struct bdb_lvds_lfp_data_tail { + struct lvds_lfp_panel_name panel_name[16]; /* 156-163? */ + u16 scaling_enable; /* 187 */ + u8 seamless_drrs_min_refresh_rate[16]; /* 188 */ + u8 pixel_overlap_count[16]; /* 208 */ + struct lvds_lfp_black_border black_border[16]; /* 227 */ + u16 dual_lfp_port_sync_enable; /* 231 */ + u16 gpu_dithering_for_banding_artifacts; /* 245 */ +} __packed; + /* * Block 43 - LFP Backlight Control Data Block */ diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index 545eff5bf158..43e1bbc1e303 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -7,6 +7,8 @@ */ #include <linux/limits.h> +#include <drm/display/drm_dsc_helper.h> + #include "i915_drv.h" #include "intel_crtc.h" #include "intel_de.h" @@ -378,10 +380,18 @@ calculate_rc_params(struct rc_parameters *rc, { int bpc = vdsc_cfg->bits_per_component; int bpp = vdsc_cfg->bits_per_pixel >> 4; - int ofs_und6[] = { 0, -2, -2, -4, -6, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12 }; - int ofs_und8[] = { 2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12 }; - int ofs_und12[] = { 2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12 }; - int ofs_und15[] = { 10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12 }; + static const s8 ofs_und6[] = { + 0, -2, -2, -4, -6, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12 + }; + static const s8 ofs_und8[] = { + 2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12 + }; + static const s8 ofs_und12[] = { + 2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12 + }; + static const s8 ofs_und15[] = { + 10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12 + }; int qp_bpc_modifier = (bpc - 8) * 2; u32 res, buf_i, bpp_i; @@ -579,7 +589,7 @@ static void intel_dsc_pps_configure(const struct intel_crtc_state *crtc_state) u8 num_vdsc_instances = (crtc_state->dsc.dsc_split) ? 2 : 1; int i = 0; - if (crtc_state->bigjoiner) + if (crtc_state->bigjoiner_pipes) num_vdsc_instances *= 2; /* Populate PICTURE_PARAMETER_SET_0 registers */ @@ -1113,7 +1123,7 @@ void intel_uncompressed_joiner_enable(const struct intel_crtc_state *crtc_state) struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); u32 dss_ctl1_val = 0; - if (crtc_state->bigjoiner && !crtc_state->dsc.compression_enable) { + if (crtc_state->bigjoiner_pipes && !crtc_state->dsc.compression_enable) { if (intel_crtc_is_bigjoiner_slave(crtc_state)) dss_ctl1_val |= UNCOMPRESSED_JOINER_SLAVE; else @@ -1140,7 +1150,7 @@ void intel_dsc_enable(const struct intel_crtc_state *crtc_state) dss_ctl2_val |= RIGHT_BRANCH_VDSC_ENABLE; dss_ctl1_val |= JOINER_ENABLE; } - if (crtc_state->bigjoiner) { + if (crtc_state->bigjoiner_pipes) { dss_ctl1_val |= BIG_JOINER_ENABLE; if (!intel_crtc_is_bigjoiner_slave(crtc_state)) dss_ctl1_val |= MASTER_BIG_JOINER_ENABLE; @@ -1156,7 +1166,7 @@ void intel_dsc_disable(const struct intel_crtc_state *old_crtc_state) /* Disable only if either of them is enabled */ if (old_crtc_state->dsc.compression_enable || - old_crtc_state->bigjoiner) { + old_crtc_state->bigjoiner_pipes) { intel_de_write(dev_priv, dss_ctl1_reg(crtc, old_crtc_state->cpu_transcoder), 0); intel_de_write(dev_priv, dss_ctl2_reg(crtc, old_crtc_state->cpu_transcoder), 0); } diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 139e8936edc5..396f2f994fa0 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -69,9 +69,9 @@ static int intel_vrr_vblank_exit_length(const struct intel_crtc_state *crtc_stat /* The hw imposes the extra scanline before frame start */ if (DISPLAY_VER(i915) >= 13) - return crtc_state->vrr.guardband + i915->framestart_delay + 1; + return crtc_state->vrr.guardband + crtc_state->framestart_delay + 1; else - return crtc_state->vrr.pipeline_full + i915->framestart_delay + 1; + return crtc_state->vrr.pipeline_full + crtc_state->framestart_delay + 1; } int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/skl_scaler.c b/drivers/gpu/drm/i915/display/skl_scaler.c index c2e94118566b..4092679be21e 100644 --- a/drivers/gpu/drm/i915/display/skl_scaler.c +++ b/drivers/gpu/drm/i915/display/skl_scaler.c @@ -197,7 +197,8 @@ int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state) return skl_update_scaler(crtc_state, !crtc_state->hw.active, SKL_CRTC_INDEX, &crtc_state->scaler_state.scaler_id, - crtc_state->pipe_src_w, crtc_state->pipe_src_h, + drm_rect_width(&crtc_state->pipe_src), + drm_rect_height(&crtc_state->pipe_src), width, height, NULL, 0, crtc_state->pch_pfit.enabled); } @@ -400,10 +401,6 @@ void skl_pfit_enable(const struct intel_crtc_state *crtc_state) struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); const struct intel_crtc_scaler_state *scaler_state = &crtc_state->scaler_state; - struct drm_rect src = { - .x2 = crtc_state->pipe_src_w << 16, - .y2 = crtc_state->pipe_src_h << 16, - }; const struct drm_rect *dst = &crtc_state->pch_pfit.dst; u16 uv_rgb_hphase, uv_rgb_vphase; enum pipe pipe = crtc->pipe; @@ -412,7 +409,7 @@ void skl_pfit_enable(const struct intel_crtc_state *crtc_state) int x = dst->x1; int y = dst->y1; int hscale, vscale; - unsigned long irqflags; + struct drm_rect src; int id; u32 ps_ctrl; @@ -423,6 +420,10 @@ void skl_pfit_enable(const struct intel_crtc_state *crtc_state) crtc_state->scaler_state.scaler_id < 0)) return; + drm_rect_init(&src, 0, 0, + drm_rect_width(&crtc_state->pipe_src) << 16, + drm_rect_height(&crtc_state->pipe_src) << 16); + hscale = drm_rect_calc_hscale(&src, dst, 0, INT_MAX); vscale = drm_rect_calc_vscale(&src, dst, 0, INT_MAX); @@ -434,8 +435,6 @@ void skl_pfit_enable(const struct intel_crtc_state *crtc_state) ps_ctrl = skl_scaler_get_filter_select(crtc_state->hw.scaling_filter, 0); ps_ctrl |= PS_SCALER_EN | scaler_state->scalers[id].mode; - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - skl_scaler_setup_filter(dev_priv, pipe, id, 0, crtc_state->hw.scaling_filter); @@ -449,8 +448,6 @@ void skl_pfit_enable(const struct intel_crtc_state *crtc_state) x << 16 | y); intel_de_write_fw(dev_priv, SKL_PS_WIN_SZ(pipe, id), width << 16 | height); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } void @@ -519,15 +516,10 @@ static void skl_detach_scaler(struct intel_crtc *crtc, int id) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); intel_de_write_fw(dev_priv, SKL_PS_CTRL(crtc->pipe, id), 0); intel_de_write_fw(dev_priv, SKL_PS_WIN_POS(crtc->pipe, id), 0); intel_de_write_fw(dev_priv, SKL_PS_WIN_SZ(crtc->pipe, id), 0); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } /* diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 1223075595ff..caa03324a733 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -615,9 +615,20 @@ skl_plane_disable_arm(struct intel_plane *plane, struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum plane_id plane_id = plane->id; enum pipe pipe = plane->pipe; - unsigned long irqflags; - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + skl_write_plane_wm(plane, crtc_state); + + intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), 0); + intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id), 0); +} + +static void +icl_plane_disable_arm(struct intel_plane *plane, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; if (icl_is_hdr_plane(dev_priv, plane_id)) intel_de_write_fw(dev_priv, PLANE_CUS_CTL(pipe, plane_id), 0); @@ -627,8 +638,6 @@ skl_plane_disable_arm(struct intel_plane *plane, intel_psr2_disable_plane_sel_fetch(plane, crtc_state); intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), 0); intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id), 0); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static bool @@ -762,6 +771,18 @@ static u32 skl_plane_ctl_tiling(u64 fb_modifier) return PLANE_CTL_TILED_X; case I915_FORMAT_MOD_Y_TILED: return PLANE_CTL_TILED_Y; + case I915_FORMAT_MOD_4_TILED: + return PLANE_CTL_TILED_4; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS: + return PLANE_CTL_TILED_4 | + PLANE_CTL_RENDER_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + case I915_FORMAT_MOD_4_TILED_DG2_MC_CCS: + return PLANE_CTL_TILED_4 | + PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + case I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC: + return PLANE_CTL_TILED_4 | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; case I915_FORMAT_MOD_Y_TILED_CCS: case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: return PLANE_CTL_TILED_Y | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; @@ -1065,7 +1086,7 @@ static void icl_plane_csc_load_black(struct intel_plane *plane) intel_de_write_fw(i915, PLANE_CSC_POSTOFF(pipe, plane_id, 2), 0); } -static int skl_plane_color_plane(const struct intel_plane_state *plane_state) +static int icl_plane_color_plane(const struct intel_plane_state *plane_state) { /* Program the UV plane on planar master */ if (plane_state->planar_linked_plane && !plane_state->planar_slave) @@ -1082,14 +1103,11 @@ skl_plane_update_noarm(struct intel_plane *plane, struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum plane_id plane_id = plane->id; enum pipe pipe = plane->pipe; - int color_plane = skl_plane_color_plane(plane_state); - u32 stride = skl_plane_stride(plane_state, color_plane); - const struct drm_framebuffer *fb = plane_state->hw.fb; + u32 stride = skl_plane_stride(plane_state, 0); int crtc_x = plane_state->uapi.dst.x1; int crtc_y = plane_state->uapi.dst.y1; u32 src_w = drm_rect_width(&plane_state->uapi.src) >> 16; u32 src_h = drm_rect_height(&plane_state->uapi.src) >> 16; - unsigned long irqflags; /* The scaler will handle the output position */ if (plane_state->scaler_id >= 0) { @@ -1097,14 +1115,99 @@ skl_plane_update_noarm(struct intel_plane *plane, crtc_y = 0; } - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + intel_de_write_fw(dev_priv, PLANE_STRIDE(pipe, plane_id), + PLANE_STRIDE_(stride)); + intel_de_write_fw(dev_priv, PLANE_POS(pipe, plane_id), + PLANE_POS_Y(crtc_y) | PLANE_POS_X(crtc_x)); + intel_de_write_fw(dev_priv, PLANE_SIZE(pipe, plane_id), + PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1)); + + skl_write_plane_wm(plane, crtc_state); +} + +static void +skl_plane_update_arm(struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + u32 x = plane_state->view.color_plane[0].x; + u32 y = plane_state->view.color_plane[0].y; + u32 plane_ctl, plane_color_ctl = 0; + + plane_ctl = plane_state->ctl | + skl_plane_ctl_crtc(crtc_state); + + if (DISPLAY_VER(dev_priv) >= 10) + plane_color_ctl = plane_state->color_ctl | + glk_plane_color_ctl_crtc(crtc_state); + + intel_de_write_fw(dev_priv, PLANE_KEYVAL(pipe, plane_id), skl_plane_keyval(plane_state)); + intel_de_write_fw(dev_priv, PLANE_KEYMSK(pipe, plane_id), skl_plane_keymsk(plane_state)); + intel_de_write_fw(dev_priv, PLANE_KEYMAX(pipe, plane_id), skl_plane_keymax(plane_state)); + + intel_de_write_fw(dev_priv, PLANE_OFFSET(pipe, plane_id), + PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x)); + + intel_de_write_fw(dev_priv, PLANE_AUX_DIST(pipe, plane_id), + skl_plane_aux_dist(plane_state, 0)); + + intel_de_write_fw(dev_priv, PLANE_AUX_OFFSET(pipe, plane_id), + PLANE_OFFSET_Y(plane_state->view.color_plane[1].y) | + PLANE_OFFSET_X(plane_state->view.color_plane[1].x)); + + if (DISPLAY_VER(dev_priv) >= 10) + intel_de_write_fw(dev_priv, PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl); /* - * FIXME: pxp session invalidation can hit any time even at time of commit - * or after the commit, display content will be garbage. + * Enable the scaler before the plane so that we don't + * get a catastrophic underrun even if the two operations + * end up happening in two different frames. + * + * TODO: split into noarm+arm pair */ - if (plane_state->force_black) - icl_plane_csc_load_black(plane); + if (plane_state->scaler_id >= 0) + skl_program_plane_scaler(plane, crtc_state, plane_state); + + /* + * The control register self-arms if the plane was previously + * disabled. Try to make the plane enable atomic by writing + * the control register just before the surface register. + */ + intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), plane_ctl); + intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id), + skl_plane_surf(plane_state, 0)); +} + +static void +icl_plane_update_noarm(struct intel_plane *plane, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + enum plane_id plane_id = plane->id; + enum pipe pipe = plane->pipe; + int color_plane = icl_plane_color_plane(plane_state); + u32 stride = skl_plane_stride(plane_state, color_plane); + const struct drm_framebuffer *fb = plane_state->hw.fb; + int crtc_x = plane_state->uapi.dst.x1; + int crtc_y = plane_state->uapi.dst.y1; + int x = plane_state->view.color_plane[color_plane].x; + int y = plane_state->view.color_plane[color_plane].y; + int src_w = drm_rect_width(&plane_state->uapi.src) >> 16; + int src_h = drm_rect_height(&plane_state->uapi.src) >> 16; + u32 plane_color_ctl; + + plane_color_ctl = plane_state->color_ctl | + glk_plane_color_ctl_crtc(crtc_state); + + /* The scaler will handle the output position */ + if (plane_state->scaler_id >= 0) { + crtc_x = 0; + crtc_y = 0; + } intel_de_write_fw(dev_priv, PLANE_STRIDE(pipe, plane_id), PLANE_STRIDE_(stride)); @@ -1113,6 +1216,13 @@ skl_plane_update_noarm(struct intel_plane *plane, intel_de_write_fw(dev_priv, PLANE_SIZE(pipe, plane_id), PLANE_HEIGHT(src_h - 1) | PLANE_WIDTH(src_w - 1)); + intel_de_write_fw(dev_priv, PLANE_KEYVAL(pipe, plane_id), skl_plane_keyval(plane_state)); + intel_de_write_fw(dev_priv, PLANE_KEYMSK(pipe, plane_id), skl_plane_keymsk(plane_state)); + intel_de_write_fw(dev_priv, PLANE_KEYMAX(pipe, plane_id), skl_plane_keymax(plane_state)); + + intel_de_write_fw(dev_priv, PLANE_OFFSET(pipe, plane_id), + PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x)); + if (intel_fb_is_rc_ccs_cc_modifier(fb->modifier)) { intel_de_write_fw(dev_priv, PLANE_CC_VAL(pipe, plane_id, 0), lower_32_bits(plane_state->ccval)); @@ -1120,60 +1230,45 @@ skl_plane_update_noarm(struct intel_plane *plane, upper_32_bits(plane_state->ccval)); } + /* FLAT CCS doesn't need to program AUX_DIST */ + if (!HAS_FLAT_CCS(dev_priv)) + intel_de_write_fw(dev_priv, PLANE_AUX_DIST(pipe, plane_id), + skl_plane_aux_dist(plane_state, color_plane)); + if (icl_is_hdr_plane(dev_priv, plane_id)) intel_de_write_fw(dev_priv, PLANE_CUS_CTL(pipe, plane_id), plane_state->cus_ctl); + intel_de_write_fw(dev_priv, PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl); + if (fb->format->is_yuv && icl_is_hdr_plane(dev_priv, plane_id)) icl_program_input_csc(plane, crtc_state, plane_state); skl_write_plane_wm(plane, crtc_state); - intel_psr2_program_plane_sel_fetch(plane, crtc_state, plane_state, color_plane); + /* + * FIXME: pxp session invalidation can hit any time even at time of commit + * or after the commit, display content will be garbage. + */ + if (plane_state->force_black) + icl_plane_csc_load_black(plane); - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); + intel_psr2_program_plane_sel_fetch(plane, crtc_state, plane_state, color_plane); } static void -skl_plane_update_arm(struct intel_plane *plane, +icl_plane_update_arm(struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum plane_id plane_id = plane->id; enum pipe pipe = plane->pipe; - int color_plane = skl_plane_color_plane(plane_state); - u32 x = plane_state->view.color_plane[color_plane].x; - u32 y = plane_state->view.color_plane[color_plane].y; - u32 plane_color_ctl = 0; - u32 plane_ctl = plane_state->ctl; - unsigned long irqflags; - - plane_ctl |= skl_plane_ctl_crtc(crtc_state); - - if (DISPLAY_VER(dev_priv) >= 10) - plane_color_ctl = plane_state->color_ctl | - glk_plane_color_ctl_crtc(crtc_state); - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - - intel_de_write_fw(dev_priv, PLANE_KEYVAL(pipe, plane_id), skl_plane_keyval(plane_state)); - intel_de_write_fw(dev_priv, PLANE_KEYMSK(pipe, plane_id), skl_plane_keymsk(plane_state)); - intel_de_write_fw(dev_priv, PLANE_KEYMAX(pipe, plane_id), skl_plane_keymax(plane_state)); - - intel_de_write_fw(dev_priv, PLANE_OFFSET(pipe, plane_id), - PLANE_OFFSET_Y(y) | PLANE_OFFSET_X(x)); - - intel_de_write_fw(dev_priv, PLANE_AUX_DIST(pipe, plane_id), - skl_plane_aux_dist(plane_state, color_plane)); - - if (DISPLAY_VER(dev_priv) < 11) - intel_de_write_fw(dev_priv, PLANE_AUX_OFFSET(pipe, plane_id), - PLANE_OFFSET_Y(plane_state->view.color_plane[1].y) | - PLANE_OFFSET_X(plane_state->view.color_plane[1].x)); + int color_plane = icl_plane_color_plane(plane_state); + u32 plane_ctl; - if (DISPLAY_VER(dev_priv) >= 10) - intel_de_write_fw(dev_priv, PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl); + plane_ctl = plane_state->ctl | + skl_plane_ctl_crtc(crtc_state); /* * Enable the scaler before the plane so that we don't @@ -1193,8 +1288,6 @@ skl_plane_update_arm(struct intel_plane *plane, intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), plane_ctl); intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id), skl_plane_surf(plane_state, color_plane)); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static void @@ -1204,7 +1297,6 @@ skl_plane_async_flip(struct intel_plane *plane, bool async_flip) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); - unsigned long irqflags; enum plane_id plane_id = plane->id; enum pipe pipe = plane->pipe; u32 plane_ctl = plane_state->ctl; @@ -1214,13 +1306,9 @@ skl_plane_async_flip(struct intel_plane *plane, if (async_flip) plane_ctl |= PLANE_CTL_ASYNC_FLIP; - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - intel_de_write_fw(dev_priv, PLANE_CTL(pipe, plane_id), plane_ctl); intel_de_write_fw(dev_priv, PLANE_SURF(pipe, plane_id), skl_plane_surf(plane_state, 0)); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } static bool intel_format_is_p01x(u32 format) @@ -1325,7 +1413,7 @@ static int skl_plane_check_dst_coordinates(const struct intel_crtc_state *crtc_s to_i915(plane_state->uapi.plane->dev); int crtc_x = plane_state->uapi.dst.x1; int crtc_w = drm_rect_width(&plane_state->uapi.dst); - int pipe_src_w = crtc_state->pipe_src_w; + int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); /* * Display WA #1175: glk @@ -1545,9 +1633,10 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state) /* * CCS AUX surface doesn't have its own x/y offsets, we must make sure - * they match with the main surface x/y offsets. + * they match with the main surface x/y offsets. On DG2 + * there's no aux plane on fb so skip this checking. */ - if (intel_fb_is_ccs_modifier(fb->modifier)) { + if (intel_fb_is_ccs_modifier(fb->modifier) && aux_plane) { while (!skl_check_main_ccs_coordinates(plane_state, x, y, offset, aux_plane)) { if (offset == 0) @@ -1591,6 +1680,8 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state) const struct drm_framebuffer *fb = plane_state->hw.fb; unsigned int rotation = plane_state->hw.rotation; int uv_plane = 1; + int ccs_plane = intel_fb_is_ccs_modifier(fb->modifier) ? + skl_main_to_aux_plane(fb, uv_plane) : 0; int max_width = intel_plane_max_width(plane, fb, uv_plane, rotation); int max_height = intel_plane_max_height(plane, fb, uv_plane, rotation); int x = plane_state->uapi.src.x1 >> 17; @@ -1611,8 +1702,7 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state) offset = intel_plane_compute_aligned_offset(&x, &y, plane_state, uv_plane); - if (intel_fb_is_ccs_modifier(fb->modifier)) { - int ccs_plane = main_to_ccs_plane(fb, uv_plane); + if (ccs_plane) { u32 aux_offset = plane_state->view.color_plane[ccs_plane].offset; u32 alignment = intel_surf_alignment(fb, uv_plane); @@ -2011,9 +2101,7 @@ static bool gen12_plane_format_mod_supported(struct drm_plane *_plane, case DRM_FORMAT_Y216: case DRM_FORMAT_XVYU12_16161616: case DRM_FORMAT_XVYU16161616: - if (modifier == DRM_FORMAT_MOD_LINEAR || - modifier == I915_FORMAT_MOD_X_TILED || - modifier == I915_FORMAT_MOD_Y_TILED) + if (!intel_fb_is_ccs_modifier(modifier)) return true; fallthrough; default: @@ -2094,6 +2182,10 @@ static bool gen12_plane_has_mc_ccs(struct drm_i915_private *i915, if (IS_ADLP_DISPLAY_STEP(i915, STEP_A0, STEP_B0)) return false; + /* Wa_14013215631 */ + if (IS_DG2_DISPLAY_STEP(i915, STEP_A0, STEP_C0)) + return false; + return plane_id < PLANE_SPRITE4; } @@ -2106,6 +2198,8 @@ static u8 skl_get_plane_caps(struct drm_i915_private *i915, caps |= INTEL_PLANE_CAP_TILING_Y; if (DISPLAY_VER(i915) < 12) caps |= INTEL_PLANE_CAP_TILING_Yf; + if (HAS_4TILE(i915)) + caps |= INTEL_PLANE_CAP_TILING_4; if (skl_plane_has_rc_ccs(i915, pipe, plane_id)) { caps |= INTEL_PLANE_CAP_CCS_RC; @@ -2162,9 +2256,15 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv, } plane->max_stride = skl_plane_max_stride; - plane->update_noarm = skl_plane_update_noarm; - plane->update_arm = skl_plane_update_arm; - plane->disable_arm = skl_plane_disable_arm; + if (DISPLAY_VER(dev_priv) >= 11) { + plane->update_noarm = icl_plane_update_noarm; + plane->update_arm = icl_plane_update_arm; + plane->disable_arm = icl_plane_disable_arm; + } else { + plane->update_noarm = skl_plane_update_noarm; + plane->update_arm = skl_plane_update_arm; + plane->disable_arm = skl_plane_disable_arm; + } plane->get_hw_state = skl_plane_get_hw_state; plane->check_plane = skl_plane_check; @@ -2278,13 +2378,14 @@ skl_get_initial_plane_config(struct intel_crtc *crtc, unsigned int aligned_height; struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; + static_assert(PLANE_CTL_TILED_YF == PLANE_CTL_TILED_4); if (!plane->get_hw_state(plane, &pipe)) return; drm_WARN_ON(dev, pipe != crtc->pipe); - if (crtc_state->bigjoiner) { + if (crtc_state->bigjoiner_pipes) { drm_dbg_kms(&dev_priv->drm, "Unsupported bigjoiner configuration for initial FB\n"); return; @@ -2332,19 +2433,34 @@ skl_get_initial_plane_config(struct intel_crtc *crtc, case PLANE_CTL_TILED_Y: plane_config->tiling = I915_TILING_Y; if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE) - fb->modifier = DISPLAY_VER(dev_priv) >= 12 ? - I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS : - I915_FORMAT_MOD_Y_TILED_CCS; + if (DISPLAY_VER(dev_priv) >= 12) + fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS; + else + fb->modifier = I915_FORMAT_MOD_Y_TILED_CCS; else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE) fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS; else fb->modifier = I915_FORMAT_MOD_Y_TILED; break; - case PLANE_CTL_TILED_YF: - if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE) - fb->modifier = I915_FORMAT_MOD_Yf_TILED_CCS; - else - fb->modifier = I915_FORMAT_MOD_Yf_TILED; + case PLANE_CTL_TILED_YF: /* aka PLANE_CTL_TILED_4 on XE_LPD+ */ + if (HAS_4TILE(dev_priv)) { + u32 rc_mask = PLANE_CTL_RENDER_DECOMPRESSION_ENABLE | + PLANE_CTL_CLEAR_COLOR_DISABLE; + + if ((val & rc_mask) == rc_mask) + fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS; + else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE) + fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_MC_CCS; + else if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE) + fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC; + else + fb->modifier = I915_FORMAT_MOD_4_TILED; + } else { + if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE) + fb->modifier = I915_FORMAT_MOD_Yf_TILED_CCS; + else + fb->modifier = I915_FORMAT_MOD_Yf_TILED; + } break; default: MISSING_CASE(tiling); diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index 0d936f658b3f..1954f07f0d3e 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -1660,6 +1660,8 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = { static void vlv_dsi_add_properties(struct intel_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + const struct drm_display_mode *fixed_mode = + intel_panel_preferred_fixed_mode(connector); u32 allowed_scalers; allowed_scalers = BIT(DRM_MODE_SCALE_ASPECT) | BIT(DRM_MODE_SCALE_FULLSCREEN); @@ -1673,8 +1675,8 @@ static void vlv_dsi_add_properties(struct intel_connector *connector) drm_connector_set_panel_orientation_with_quirk(&connector->base, intel_dsi_get_panel_orientation(connector), - connector->panel.fixed_mode->hdisplay, - connector->panel.fixed_mode->vdisplay); + fixed_mode->hdisplay, + fixed_mode->vdisplay); } #define NS_KHZ_RATIO 1000000 @@ -1857,7 +1859,7 @@ void vlv_dsi_init(struct drm_i915_private *dev_priv) struct drm_encoder *encoder; struct intel_connector *intel_connector; struct drm_connector *connector; - struct drm_display_mode *current_mode, *fixed_mode; + struct drm_display_mode *current_mode; enum port port; enum pipe pipe; @@ -1978,15 +1980,16 @@ void vlv_dsi_init(struct drm_i915_private *dev_priv) intel_connector_attach_encoder(intel_connector, intel_encoder); mutex_lock(&dev->mode_config.mutex); - fixed_mode = intel_panel_vbt_fixed_mode(intel_connector); + intel_panel_add_vbt_lfp_fixed_mode(intel_connector); mutex_unlock(&dev->mode_config.mutex); - if (!fixed_mode) { + if (!intel_panel_preferred_fixed_mode(intel_connector)) { drm_dbg_kms(&dev_priv->drm, "no fixed mode\n"); goto err_cleanup_connector; } - intel_panel_init(&intel_connector->panel, fixed_mode, NULL); + intel_panel_init(intel_connector); + intel_backlight_setup(intel_connector, INVALID_PIPE); vlv_dsi_add_properties(intel_connector); diff --git a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c index df880f44700a..5894b0138343 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c @@ -26,6 +26,7 @@ */ #include <linux/kernel.h> +#include <linux/string_helpers.h> #include "i915_drv.h" #include "intel_de.h" @@ -393,10 +394,7 @@ static void glk_dsi_program_esc_clock(struct drm_device *dev, /* Calculate TXESC2 divider */ div2_value = DIV_ROUND_UP(div1_value, txesc1_div); - if (div2_value < 10) - txesc2_div = div2_value; - else - txesc2_div = 10; + txesc2_div = min_t(u32, div2_value, 10); intel_de_write(dev_priv, MIPIO_TXESC_CLK_DIV1, (1 << (txesc1_div - 1)) & GLK_TX_ESC_CLK_DIV1_MASK); @@ -581,7 +579,7 @@ static void assert_dsi_pll(struct drm_i915_private *i915, bool state) I915_STATE_WARN(cur_state != state, "DSI PLL state assertion failure (expected %s, current %s)\n", - onoff(state), onoff(cur_state)); + str_on_off(state), str_on_off(cur_state)); } void assert_dsi_pll_enabled(struct drm_i915_private *i915) |