diff options
| author | Dave Airlie <airlied@redhat.com> | 2026-09-08 21:05:59 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2026-09-08 21:06:06 +1000 |
| commit | f5c6fdfa1f4460c5714cd4996c6aa280d25399ca (patch) | |
| tree | 23a2ecfed79d0786010732f0b6a4d289df27998d | |
| parent | 483ece619b64a6596bdaf69479ea0ecbbd197a00 (diff) | |
| parent | 0d63d6fc993b1fb97e314f435bdefbf0ccd947bd (diff) | |
| download | linux-next-f5c6fdfa1f4460c5714cd4996c6aa280d25399ca.tar.gz linux-next-f5c6fdfa1f4460c5714cd4996c6aa280d25399ca.zip | |
Merge tag 'drm-intel-next-2026-09-03' of https://gitlab.freedesktop.org/drm/i915/kernel into drm-next
Xe related:
- Some clean-ups around drm/xe/display (Jani)
Display in general:
- Regular clean-ups (Jani)
- Enable CMRR (Content Match Refresh Rate) in fixed-RR VRR path (Mitul)
- Switch DG2 Multi-protocol PLL (MPLLB) to the DPLL framework (Mika)
- DP-HDMI PCON DSC fixes (Ankit)
- Remove duplicated call introduced by a bad cherry-pick (Chaitanya)
- Limit invalid joiner combinations harder (Jani)
- Fix value being written to DDI_CLK_VALFREQ (Suraj)
- Harden some bit-shifting operations (Luca)
- Clear SEL_FETCH_PLANE_CTL on plane disable (Nemesa)
- Update CURSOR_BUF/WM masks (Suraj)
- CDCLK related fixes and refactor specially around PCODE interaction (Ville)
- Reduce clock_gating interface between core and display (Luca)
- Gate UHBR SST SDP splitting on sink capability (Mitul)
- Enable joiner cursor fast updates (Nemesa)
- Cear the Panel Replay error status register (Jake)
- Add HDMI TMDS audio bandwidth check (Chaitanya)
- Reset use_flipq when duplicating crtc state (Mika)
Beyond display:
- Guard against NULL driver_data (Deepanshu)
- Rename GEN6_PCODE_DATA to match spec (Michal)
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patch.msgid.link/apmWNIUJ5lmsvNzc@intel.com
51 files changed, 1540 insertions, 615 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 9d0d47c79dd1..b66c2d4ba2b3 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -276,6 +276,7 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc) crtc_state->dsb_color = NULL; crtc_state->dsb_commit = NULL; crtc_state->use_dsb = false; + crtc_state->use_flipq = false; return &crtc_state->uapi; } diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index eae76e961105..32f8c5e55319 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -697,6 +697,156 @@ static void ibx_audio_codec_enable(struct intel_encoder *encoder, mutex_unlock(&display->audio.mutex); } +#define HDMI_TMDS_AUDIO_PACKETS_LINE_MAX 18 + +static bool hdmi_audio_rate_supported(const struct intel_crtc_state *crtc_state, + int available_tmds, + int audio_rate, int channels) +{ + const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode; + int pixel_clk_max_hz; + int audio_pkt_factor; + u64 audio_pkt_rate_x4_x1000; + int audio_packets_line; + int hblank_overhead; + int hblank_audio_min; + + /* + * Part 2: Calculate TMDS clock cycles required for Audio Bandwidth + * + * Step 1: pixelclk_max = nominal_pixel_rate * (1 + 0.5%) + * crtc_clock (kHz) * 1000 * 1.005 = crtc_clock * 1005 (Hz) + */ + pixel_clk_max_hz = mode->crtc_clock * 1005; + + /* + * Steps 3-4: Audio Packet Rate. + * R_AP = (audio_rate * AP + 2 * acrrate_max) * (1 + 1000 / 1e6) + * = (audio_rate * AP + 2*1500) * 1.001 + * + * AP = 0.25 (2ch) or 1.0 (3-8ch); acrrate_max = 1500 Hz (max ACR + * packet transmission rate per HDMI spec) + * + * Scale by 4*1000 to stay integer: + * x4: eliminates AP=0.25 -> audio_pkt_factor=1(2ch) or 4(3-8ch), + * scaled acrrate_max: 2 * 1500 * 4 = 12000 + * x1000: eliminates 1.001 -> *1000*1.001 = *1001 + * + * R_AP * 4 * 1000 = (audio_rate * audio_pkt_factor + 12000) * 1001 + */ + audio_pkt_factor = (channels <= 2) ? 1 : 4; + audio_pkt_rate_x4_x1000 = (u64)(audio_rate * audio_pkt_factor + 12000) * 1001; + + /* + * Steps 2+5-6: Audio packets per line. + * AudioPackets_Line = CEIL[R_AP * htotal / f_pixelclk_max] + * + * With audio_pkt_rate_x4_x1000 = R_AP * 4 * 1000: + * = CEIL[audio_pkt_rate_x4_x1000 * htotal / (4 * 1000 * pixel_clk_max_hz)] + */ + audio_packets_line = DIV64_U64_ROUND_UP(audio_pkt_rate_x4_x1000 * mode->htotal, + (u64)4 * 1000 * pixel_clk_max_hz); + + /* + * Steps 7-9: Hblank overhead. + * Standard: 2*dip_guardband + 2*control_period + video_guardband + * = 2*2 + 2*12 + 2 = 30 + * HDCP 1.x: rekey_period + dip_guardband + control_period + video_guardband + * = 58 + 2 + 12 + 2 = 74 + * + * Always use HDCP 1.x worst case (74) since HDCP can be toggled + * via fastset without compute_config. + */ + hblank_overhead = 74; + + /* + * Step 10: Required TMDS cycles for Audio. + * 32 TMDS clock cycles per audio packet. + * Hblank_audio_min = 32 * AudioPackets_Line + Hblank_overhead + */ + hblank_audio_min = 32 * audio_packets_line + hblank_overhead; + + /* + * Part 3: audio supported if Hblank_audio_min <= TB_blank and + * audio packets per line <= Maximum allowed packets per line + */ + return hblank_audio_min <= available_tmds && + audio_packets_line <= HDMI_TMDS_AUDIO_PACKETS_LINE_MAX; +} + +static void intel_audio_hdmi_eld_compute_config(struct intel_crtc_state *crtc_state) +{ + static const int sad_freqs[] = { + 32000, 44100, 48000, 88200, 96000, 176400, 192000 + }; + const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode; + int hblank = mode->htotal - mode->hdisplay; + int bpc = crtc_state->pipe_bpp / 3; + int ycbcr_420_divider = (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) ? 2 : 1; + int available_tmds; + u8 *eld = crtc_state->eld; + int mnl = drm_eld_mnl(eld); + int sad_count = drm_eld_sad_count(eld); + int src, dst = 0; + + /* + * Part 1: Calculate available TMDS clock cycles (TB_blank). + * + * TB_blank = CEILING[hblank * K_CD / K_420] + * + * K_CD = 1 for YCbCr4:2:2, bpc / 8 otherwise. + * K_420 = 2 for YCbCr4:2:0, 1 otherwise. + * Rearranged: CEILING[hblank * bpc / (8 * K_420)] + * + * TODO: As and when support for YCbCr4:2:2 is added, set bpc = 8 + * to achieve K_CD = 1 + */ + available_tmds = DIV_ROUND_UP(hblank * bpc, 8 * ycbcr_420_divider); + + /* + * Walk all SADs once, keeping the ones with at least one supported + * rate and compacting them down to a contiguous [0, dst) range. + */ + for (src = 0; src < sad_count; src++) { + struct cea_sad sad; + u8 new_freq_mask = 0; + int channels; + int bit; + + drm_eld_sad_get(eld, src, &sad); + channels = sad.channels + 1; + + for (bit = 0; bit < 7; bit++) { + if (!(sad.freq & BIT(bit))) + continue; + if (hdmi_audio_rate_supported(crtc_state, available_tmds, + sad_freqs[bit], channels)) + new_freq_mask |= BIT(bit); + } + + if (!new_freq_mask) + continue; + + sad.freq = new_freq_mask; + drm_eld_sad_set(eld, dst, &sad); + dst++; + } + + /* Clear the now-unused tail */ + if (dst != sad_count) + memset(&eld[DRM_ELD_CEA_SAD(mnl, dst)], 0, (sad_count - dst) * 3); + + sad_count = dst; + + /* Update SAD count in ELD header */ + eld[DRM_ELD_SAD_COUNT_CONN_TYPE] &= ~DRM_ELD_SAD_COUNT_MASK; + eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= sad_count << DRM_ELD_SAD_COUNT_SHIFT; + + /* Recalculate baseline ELD length (in dwords) */ + eld[DRM_ELD_BASELINE_ELD_LEN] = + DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4); +} + static bool intel_audio_needs_cpu_transcoder_id(const struct intel_crtc_state *crtc_state) { @@ -725,6 +875,9 @@ bool intel_audio_compute_config(struct intel_encoder *encoder, BUILD_BUG_ON(sizeof(crtc_state->eld) != sizeof(connector->eld)); memcpy(crtc_state->eld, connector->eld, sizeof(crtc_state->eld)); + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) + intel_audio_hdmi_eld_compute_config(crtc_state); + crtc_state->eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2; mutex_unlock(&connector->eld_mutex); diff --git a/drivers/gpu/drm/i915/display/intel_audio_regs.h b/drivers/gpu/drm/i915/display/intel_audio_regs.h index 4c31844d21df..f3d2a99c03d0 100644 --- a/drivers/gpu/drm/i915/display/intel_audio_regs.h +++ b/drivers/gpu/drm/i915/display/intel_audio_regs.h @@ -109,12 +109,20 @@ #define _HSW_AUD_EDID_DATA_B 0x65150 #define HSW_AUD_EDID_DATA(trans) _MMIO_TRANS(trans, _HSW_AUD_EDID_DATA_A, _HSW_AUD_EDID_DATA_B) +/* + * HSW_AUD_PIN_ELD_CP_VLD has a 4-bit field per transcoder for + * TRANSCODER_A..TRANSCODER_D only (bits 0..15). Any other transcoder + * value (TRANSCODER_EDP, TRANSCODER_DSI_*, INVALID_TRANSCODER) is not + * valid here. Mask the index to the low 2 bits so the shift is + * always well-defined. + */ #define HSW_AUD_PIPE_CONV_CFG _MMIO(0x6507c) #define HSW_AUD_PIN_ELD_CP_VLD _MMIO(0x650c0) -#define AUDIO_INACTIVE(trans) ((1 << 3) << ((trans) * 4)) -#define AUDIO_OUTPUT_ENABLE(trans) ((1 << 2) << ((trans) * 4)) -#define AUDIO_CP_READY(trans) ((1 << 1) << ((trans) * 4)) -#define AUDIO_ELD_VALID(trans) ((1 << 0) << ((trans) * 4)) +#define __AUDIO_TRANS_SHIFT(trans) (((trans) & 0x3) * 4) +#define AUDIO_INACTIVE(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans) + 3) +#define AUDIO_OUTPUT_ENABLE(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans) + 2) +#define AUDIO_CP_READY(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans) + 1) +#define AUDIO_ELD_VALID(trans) REG_BIT(__AUDIO_TRANS_SHIFT(trans)) #define _AUD_TCA_DP_2DOT0_CTRL 0x650bc #define _AUD_TCB_DP_2DOT0_CTRL 0x651bc diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index a1a5720996b7..92e6fca861f6 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -163,6 +163,9 @@ struct intel_cdclk_funcs { void (*set_cdclk)(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe); + int (*pre_notify)(struct intel_display *display); + int (*post_notify)(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config); int (*modeset_calc_cdclk)(struct intel_atomic_state *state); u8 (*calc_voltage_level)(int cdclk); }; @@ -173,11 +176,45 @@ void intel_cdclk_get_cdclk(struct intel_display *display, display->cdclk.funcs->get_cdclk(display, cdclk_config); } +static int intel_cdclk_pre_notify(struct intel_display *display) +{ + int ret; + + if (!display->cdclk.funcs->pre_notify) + return 0; + + ret = display->cdclk.funcs->pre_notify(display); + if (ret) + drm_err(display->drm, + "Failed to inform system about start of CDCLK change (%d)\n", ret); + + return ret; +} + +static void intel_cdclk_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + int ret; + + if (!display->cdclk.funcs->post_notify) + return; + + ret = display->cdclk.funcs->post_notify(display, cdclk_config); + if (ret) + drm_err(display->drm, + "Failed to inform system about end of CDCLK change (%d)\n", ret); +} + static void intel_cdclk_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) { + if (intel_cdclk_pre_notify(display)) + return; + display->cdclk.funcs->set_cdclk(display, cdclk_config, pipe); + + intel_cdclk_post_notify(display, cdclk_config); } static int intel_cdclk_modeset_calc_cdclk(struct intel_atomic_state *state) @@ -872,6 +909,19 @@ static u32 bdw_cdclk_freq_sel(int cdclk) } } +static int bdw_cdclk_pcode_pre_notify(struct intel_display *display) +{ + return intel_parent_pcode_write(display, BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ, + 0x0); +} + +static int bdw_cdclk_pcode_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + return intel_parent_pcode_write(display, HSW_PCODE_DE_WRITE_FREQ_REQ, + cdclk_config->voltage_level); +} + static void bdw_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -888,13 +938,6 @@ static void bdw_set_cdclk(struct intel_display *display, "trying to change cdclk frequency with cdclk not enabled\n")) return; - ret = intel_parent_pcode_write(display, BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ, 0x0); - if (ret) { - drm_err(display->drm, - "failed to inform pcode about cdclk change\n"); - return; - } - intel_de_rmw(display, LCPLL_CTL, 0, LCPLL_CD_SOURCE_FCLK); @@ -918,9 +961,6 @@ static void bdw_set_cdclk(struct intel_display *display, if (ret) drm_err(display->drm, "Switching back to LCPLL failed\n"); - intel_parent_pcode_write(display, HSW_PCODE_DE_WRITE_FREQ_REQ, - cdclk_config->voltage_level); - intel_de_write(display, CDCLK_FREQ, DIV_ROUND_CLOSEST(cdclk, 1000) - 1); @@ -1155,6 +1195,21 @@ static u32 skl_cdclk_freq_sel(struct intel_display *display, } } +static int skl_cdclk_pcode_pre_notify(struct intel_display *display) +{ + return intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, + SKL_CDCLK_PREPARE_FOR_CHANGE, + SKL_CDCLK_READY_FOR_CHANGE, + SKL_CDCLK_READY_FOR_CHANGE, 3); +} + +static int skl_cdclk_pcode_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + return intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, + cdclk_config->voltage_level); +} + static void skl_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -1162,7 +1217,6 @@ static void skl_set_cdclk(struct intel_display *display, int cdclk = cdclk_config->cdclk; int vco = cdclk_config->vco; u32 freq_select, cdclk_ctl; - int ret; /* * Based on WA#1183 CDCLK rates 308 and 617MHz CDCLK rates are @@ -1175,16 +1229,6 @@ static void skl_set_cdclk(struct intel_display *display, drm_WARN_ON_ONCE(display->drm, display->platform.skylake && vco == 8640000); - ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, - SKL_CDCLK_PREPARE_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, 3); - if (ret) { - drm_err(display->drm, - "Failed to inform PCU about cdclk change (%d)\n", ret); - return; - } - freq_select = skl_cdclk_freq_sel(display, cdclk, vco); if (display->cdclk.hw.vco != 0 && @@ -1220,10 +1264,6 @@ static void skl_set_cdclk(struct intel_display *display, intel_de_write(display, CDCLK_CTL, cdclk_ctl); intel_de_posting_read(display, CDCLK_CTL); - /* inform PCU of the change */ - intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, - cdclk_config->voltage_level); - intel_update_cdclk(display); } @@ -1314,7 +1354,7 @@ static void skl_cdclk_init_hw(struct intel_display *display) cdclk_config.cdclk = skl_calc_cdclk(0, cdclk_config.vco); cdclk_config.voltage_level = skl_calc_voltage_level(cdclk_config.cdclk); - skl_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } static void skl_cdclk_uninit_hw(struct intel_display *display) @@ -1325,7 +1365,7 @@ static void skl_cdclk_uninit_hw(struct intel_display *display) cdclk_config.vco = 0; cdclk_config.voltage_level = skl_calc_voltage_level(cdclk_config.cdclk); - skl_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } struct intel_cdclk_vals { @@ -2177,6 +2217,11 @@ static bool pll_enable_wa_needed(struct intel_display *display) display->cdclk.hw.vco > 0; } +static bool has_cd2x_pipe_select(struct intel_display *display) +{ + return IS_DISPLAY_VER(display, 10, 20) || display->platform.broxton; +} + static u32 bxt_cdclk_ctl(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -2190,7 +2235,7 @@ static u32 bxt_cdclk_ctl(struct intel_display *display, val = bxt_cdclk_cd2x_div_sel(display, cdclk, vco, waveform); - if (DISPLAY_VER(display) < 30) + if (has_cd2x_pipe_select(display)) val |= bxt_cdclk_cd2x_pipe(display, pipe); /* @@ -2220,6 +2265,29 @@ static u32 bxt_cdclk_ctl(struct intel_display *display, return val; } +static int bxt_cdclk_pcode_pre_notify(struct intel_display *display) +{ + /* + * BSpec requires us to wait up to 150usec, but that leads to + * timeouts; the 2ms used here is based on experiment. + */ + return intel_parent_pcode_write_timeout(display, HSW_PCODE_DE_WRITE_FREQ_REQ, + 0x80000000, 2); +} + +static int bxt_cdclk_pcode_post_notify(struct intel_display *display, + const struct intel_cdclk_config *cdclk_config) +{ + /* + * The timeout isn't specified, the 2ms used here is based on + * experiment. + * FIXME: Waiting for the request completion could be delayed + * until the next PCODE request based on BSpec. + */ + return intel_parent_pcode_write_timeout(display, HSW_PCODE_DE_WRITE_FREQ_REQ, + cdclk_config->voltage_level, 2); +} + static void _bxt_set_cdclk(struct intel_display *display, const struct intel_cdclk_config *cdclk_config, enum pipe pipe) @@ -2259,36 +2327,6 @@ static void bxt_set_cdclk(struct intel_display *display, { struct intel_cdclk_config mid_cdclk_config; int cdclk = cdclk_config->cdclk; - int ret = 0; - - /* - * Inform power controller of upcoming frequency change. - * Display versions 14 and beyond do not follow the PUnit - * mailbox communication, skip - * this step. - */ - if (DISPLAY_VER(display) >= 14 || display->platform.dg2) - ; /* NOOP */ - else if (DISPLAY_VER(display) >= 11) - ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, - SKL_CDCLK_PREPARE_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, 3); - else - /* - * BSpec requires us to wait up to 150usec, but that leads to - * timeouts; the 2ms used here is based on experiment. - */ - ret = intel_parent_pcode_write_timeout(display, - HSW_PCODE_DE_WRITE_FREQ_REQ, - 0x80000000, 2); - - if (ret) { - drm_err(display->drm, - "Failed to inform PCU about cdclk change (err %d, freq %d)\n", - ret, cdclk); - return; - } if (DISPLAY_VER(display) >= 20 && cdclk < display->cdclk.hw.cdclk) xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config); @@ -2304,40 +2342,13 @@ static void bxt_set_cdclk(struct intel_display *display, if (DISPLAY_VER(display) >= 20 && cdclk > display->cdclk.hw.cdclk) xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config); - if (DISPLAY_VER(display) >= 14) - /* - * NOOP - No Pcode communication needed for - * Display versions 14 and beyond - */; - else if (DISPLAY_VER(display) >= 11 && !display->platform.dg2) - ret = intel_parent_pcode_write(display, SKL_PCODE_CDCLK_CONTROL, - cdclk_config->voltage_level); - if (DISPLAY_VER(display) < 11) { - /* - * The timeout isn't specified, the 2ms used here is based on - * experiment. - * FIXME: Waiting for the request completion could be delayed - * until the next PCODE request based on BSpec. - */ - ret = intel_parent_pcode_write_timeout(display, - HSW_PCODE_DE_WRITE_FREQ_REQ, - cdclk_config->voltage_level, 2); - } - if (ret) { - drm_err(display->drm, - "PCode CDCLK freq set failed, (err %d, freq %d)\n", - ret, cdclk); - return; - } - intel_update_cdclk(display); - if (DISPLAY_VER(display) >= 11) - /* - * Can't read out the voltage level :( - * Let's just assume everything is as expected. - */ - display->cdclk.hw.voltage_level = cdclk_config->voltage_level; + /* + * Can't read out the voltage level :( + * Let's just assume everything is as expected. + */ + display->cdclk.hw.voltage_level = cdclk_config->voltage_level; } static void bxt_sanitize_cdclk(struct intel_display *display) @@ -2381,7 +2392,7 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * dividers both syncing to an active pipe, or asynchronously * (PIPE_NONE). */ - if (DISPLAY_VER(display) < 30) { + if (has_cd2x_pipe_select(display)) { cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); } @@ -2437,7 +2448,7 @@ static void bxt_cdclk_init_hw(struct intel_display *display) cdclk_config.voltage_level = intel_cdclk_calc_voltage_level(display, cdclk_config.cdclk); - bxt_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } static void bxt_cdclk_uninit_hw(struct intel_display *display) @@ -2449,7 +2460,7 @@ static void bxt_cdclk_uninit_hw(struct intel_display *display) cdclk_config.voltage_level = intel_cdclk_calc_voltage_level(display, cdclk_config.cdclk); - bxt_set_cdclk(display, &cdclk_config, INVALID_PIPE); + intel_cdclk_set_cdclk(display, &cdclk_config, INVALID_PIPE); } /** @@ -2579,8 +2590,7 @@ static bool intel_cdclk_can_cd2x_update(struct intel_display *display, const struct intel_cdclk_config *a, const struct intel_cdclk_config *b) { - /* Older hw doesn't have the capability */ - if (DISPLAY_VER(display) < 10 && !display->platform.broxton) + if (!has_cd2x_pipe_select(display)) return false; /* @@ -2623,19 +2633,15 @@ void intel_cdclk_dump_config(struct intel_display *display, cdclk_config->voltage_level); } -static void intel_pcode_notify(struct intel_display *display, - u8 voltage_level, - u8 active_pipe_count, - u16 cdclk, - bool cdclk_update_valid, - bool pipe_count_update_valid) +static int dg2_cdclk_pcode_notify(struct intel_display *display, + u8 voltage_level, + u8 active_pipe_count, + u16 cdclk, + bool cdclk_update_valid, + bool pipe_count_update_valid) { - int ret; u32 update_mask = 0; - if (!display->platform.dg2) - return; - update_mask = DISPLAY_TO_PCODE_UPDATE_MASK(cdclk, active_pipe_count, voltage_level); if (cdclk_update_valid) @@ -2644,15 +2650,10 @@ static void intel_pcode_notify(struct intel_display *display, if (pipe_count_update_valid) update_mask |= DISPLAY_TO_PCODE_PIPE_COUNT_VALID; - ret = intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, - SKL_CDCLK_PREPARE_FOR_CHANGE | - update_mask, - SKL_CDCLK_READY_FOR_CHANGE, - SKL_CDCLK_READY_FOR_CHANGE, 3); - if (ret) - drm_err(display->drm, - "Failed to inform PCU about display config (err %d)\n", - ret); + return intel_parent_pcode_request(display, SKL_PCODE_CDCLK_CONTROL, + update_mask, + SKL_CDCLK_READY_FOR_CHANGE, + SKL_CDCLK_READY_FOR_CHANGE, 3); } static void intel_set_cdclk(struct intel_display *display, @@ -2721,15 +2722,17 @@ static int dg2_power_well_count(struct intel_display *display, return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0; } -static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) +static void dg2_cdclk_pcode_pre_notify(struct intel_atomic_state *state) { struct intel_display *display = to_intel_display(state); const struct intel_cdclk_state *old_cdclk_state = intel_atomic_get_old_cdclk_state(state); const struct intel_cdclk_state *new_cdclk_state = intel_atomic_get_new_cdclk_state(state); - unsigned int cdclk = 0; u8 voltage_level, num_active_pipes = 0; + u8 voltage_level, num_active_pipes = 0; bool change_cdclk, update_pipe_count; + unsigned int cdclk_mhz = 0; + int ret; if (!intel_cdclk_changed(&old_cdclk_state->actual, &new_cdclk_state->actual) && @@ -2750,8 +2753,12 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) * if CDCLK is decreasing or not changing, set bits 25:16 to current CDCLK, * which basically means we choose the maximum of old and new CDCLK, if we know both */ - if (change_cdclk) - cdclk = max(new_cdclk_state->actual.cdclk, old_cdclk_state->actual.cdclk); + if (change_cdclk) { + unsigned int cdclk = max(new_cdclk_state->actual.cdclk, + old_cdclk_state->actual.cdclk); + + cdclk_mhz = DIV_ROUND_UP(cdclk, 1000); + } /* * According to "Sequence For Pipe Count Change", @@ -2762,19 +2769,24 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk, - change_cdclk, update_pipe_count); + ret = dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, + change_cdclk, update_pipe_count); + if (ret) + drm_err(display->drm, + "Failed to inform PCODE about start of CDCLK change (%d)\n", ret); } -static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) +static void dg2_cdclk_pcode_post_notify(struct intel_atomic_state *state) { struct intel_display *display = to_intel_display(state); const struct intel_cdclk_state *new_cdclk_state = intel_atomic_get_new_cdclk_state(state); const struct intel_cdclk_state *old_cdclk_state = intel_atomic_get_old_cdclk_state(state); - unsigned int cdclk = 0; u8 voltage_level, num_active_pipes = 0; + u8 voltage_level, num_active_pipes = 0; bool update_cdclk, update_pipe_count; + unsigned int cdclk_mhz = 0; + int ret; /* According to "Sequence After Frequency Change", set voltage to used level */ voltage_level = new_cdclk_state->actual.voltage_level; @@ -2787,8 +2799,11 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) * According to "Sequence After Frequency Change", * set bits 25:16 to current CDCLK */ - if (update_cdclk) - cdclk = new_cdclk_state->actual.cdclk; + if (update_cdclk) { + unsigned int cdclk = new_cdclk_state->actual.cdclk; + + cdclk_mhz = DIV_ROUND_UP(cdclk, 1000); + } /* * According to "Sequence For Pipe Count Change", @@ -2799,8 +2814,11 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) if (update_pipe_count) num_active_pipes = dg2_power_well_count(display, new_cdclk_state); - intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk, - update_cdclk, update_pipe_count); + ret = dg2_cdclk_pcode_notify(display, voltage_level, num_active_pipes, cdclk_mhz, + update_cdclk, update_pipe_count); + if (ret) + drm_err(display->drm, + "Failed to inform PCODE about end of CDCLK change (%d)\n", ret); } bool intel_cdclk_is_decreasing_later(struct intel_atomic_state *state) @@ -2836,12 +2854,11 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) return; if (!intel_cdclk_changed(&old_cdclk_state->actual, - &new_cdclk_state->actual)) + &new_cdclk_state->actual) && + dg2_power_well_count(display, old_cdclk_state) == + dg2_power_well_count(display, new_cdclk_state)) return; - if (display->platform.dg2) - intel_cdclk_pcode_pre_notify(state); - if (new_cdclk_state->disable_pipes) { cdclk_config = new_cdclk_state->actual; pipe = INVALID_PIPE; @@ -2866,6 +2883,9 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) drm_WARN_ON(display->drm, !new_cdclk_state->base.changed); + if (display->platform.dg2) + dg2_cdclk_pcode_pre_notify(state); + intel_set_cdclk(display, &cdclk_config, pipe, "Pre changing CDCLK to"); } @@ -2891,12 +2911,11 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) return; if (!intel_cdclk_changed(&old_cdclk_state->actual, - &new_cdclk_state->actual)) + &new_cdclk_state->actual) && + dg2_power_well_count(display, old_cdclk_state) == + dg2_power_well_count(display, new_cdclk_state)) return; - if (display->platform.dg2) - intel_cdclk_pcode_post_notify(state); - if (!new_cdclk_state->disable_pipes && new_cdclk_state->actual.cdclk < old_cdclk_state->actual.cdclk) pipe = new_cdclk_state->pipe; @@ -2907,6 +2926,9 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) intel_set_cdclk(display, &new_cdclk_state->actual, pipe, "Post changing CDCLK to"); + + if (display->platform.dg2) + dg2_cdclk_pcode_post_notify(state); } /* pixels per CDCLK */ @@ -3942,9 +3964,25 @@ static const struct intel_cdclk_funcs xe3lpd_cdclk_funcs = { .calc_voltage_level = xe3lpd_calc_voltage_level, }; +static const struct intel_cdclk_funcs mtl_cdclk_funcs = { + .get_cdclk = bxt_get_cdclk, + .set_cdclk = bxt_set_cdclk, + .modeset_calc_cdclk = bxt_modeset_calc_cdclk, + .calc_voltage_level = rplu_calc_voltage_level, +}; + +static const struct intel_cdclk_funcs dg2_cdclk_funcs = { + .get_cdclk = bxt_get_cdclk, + .set_cdclk = bxt_set_cdclk, + .modeset_calc_cdclk = bxt_modeset_calc_cdclk, + .calc_voltage_level = tgl_calc_voltage_level, +}; + static const struct intel_cdclk_funcs rplu_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = rplu_calc_voltage_level, }; @@ -3952,6 +3990,8 @@ static const struct intel_cdclk_funcs rplu_cdclk_funcs = { static const struct intel_cdclk_funcs tgl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = tgl_calc_voltage_level, }; @@ -3959,6 +3999,8 @@ 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, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = ehl_calc_voltage_level, }; @@ -3966,6 +4008,8 @@ 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, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = icl_calc_voltage_level, }; @@ -3973,6 +4017,8 @@ 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, + .pre_notify = bxt_cdclk_pcode_pre_notify, + .post_notify = bxt_cdclk_pcode_post_notify, .modeset_calc_cdclk = bxt_modeset_calc_cdclk, .calc_voltage_level = bxt_calc_voltage_level, }; @@ -3980,12 +4026,16 @@ 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, + .pre_notify = skl_cdclk_pcode_pre_notify, + .post_notify = skl_cdclk_pcode_post_notify, .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, + .pre_notify = bdw_cdclk_pcode_pre_notify, + .post_notify = bdw_cdclk_pcode_post_notify, .modeset_calc_cdclk = bdw_modeset_calc_cdclk, }; @@ -4091,16 +4141,16 @@ void intel_init_cdclk_hooks(struct intel_display *display) display->cdclk.funcs = &xe3lpd_cdclk_funcs; display->cdclk.table = xe3lpd_cdclk_table; } else if (DISPLAY_VER(display) >= 20) { - display->cdclk.funcs = &rplu_cdclk_funcs; + display->cdclk.funcs = &mtl_cdclk_funcs; display->cdclk.table = xe2lpd_cdclk_table; } else if (DISPLAY_VERx100(display) >= 1401) { - display->cdclk.funcs = &rplu_cdclk_funcs; + display->cdclk.funcs = &mtl_cdclk_funcs; display->cdclk.table = xe2hpd_cdclk_table; } else if (DISPLAY_VER(display) >= 14) { - display->cdclk.funcs = &rplu_cdclk_funcs; + display->cdclk.funcs = &mtl_cdclk_funcs; display->cdclk.table = mtl_cdclk_table; } else if (display->platform.dg2) { - display->cdclk.funcs = &tgl_cdclk_funcs; + display->cdclk.funcs = &dg2_cdclk_funcs; display->cdclk.table = dg2_cdclk_table; } else if (display->platform.alderlake_p) { /* Wa_22011320316:adl-p[a0] */ diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index c8e0f90af910..3c0f5b2fb2e7 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -326,7 +326,7 @@ void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state) XELPD_VRR_CTL_VRR_GUARDBAND(crtc_state->vrr.guardband); /* TODO: The code below may need to be revisited once CMRR is enabled */ - if (crtc_state->cmrr.enable) + if (crtc_state->vrr.cmrr.enable) vrr_ctl |= VRR_CTL_CMRR_ENABLE; intel_de_write(display, TRANS_VRR_CTL(display, cmtg_transcoder), vrr_ctl); diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c index 4493483f10a9..ad4f362e0c09 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c +++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c @@ -311,6 +311,9 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config, pipe_config->vrr.dc_balance.max_increase, pipe_config->vrr.dc_balance.max_decrease, pipe_config->vrr.dc_balance.vblank_target); + drm_printf(&p, "cmrr: %s, cmrr_m: %llu, cmrr_n: %llu\n", + str_yes_no(pipe_config->vrr.cmrr.enable), + pipe_config->vrr.cmrr.cmrr_m, pipe_config->vrr.cmrr.cmrr_n); drm_printf(&p, "requested mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&pipe_config->hw.mode)); diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 86bb96ac449b..cce041e1da51 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -13,6 +13,7 @@ #include <drm/drm_vblank.h> #include "intel_atomic.h" +#include "intel_crtc.h" #include "intel_cursor.h" #include "intel_cursor_regs.h" #include "intel_de.h" @@ -801,6 +802,88 @@ void intel_cursor_unpin_work(struct kthread_work *base) intel_plane_destroy_state(&plane->base, &plane_state->uapi); } +static int intel_cursor_lock_joined_planes(struct intel_display *display, + const struct intel_crtc_state *crtc_state, + struct drm_modeset_acquire_ctx *ctx) +{ + struct intel_crtc *pipe_crtc; + int ret; + + for_each_intel_crtc_in_pipe_mask(display, pipe_crtc, + intel_crtc_joined_pipe_mask(crtc_state)) { + struct intel_plane *pipe_plane = + intel_crtc_get_plane(pipe_crtc, PLANE_CURSOR); + + ret = drm_modeset_lock(&pipe_crtc->base.mutex, ctx); + if (ret) + return ret; + + ret = drm_modeset_lock(&pipe_plane->base.mutex, ctx); + if (ret) + return ret; + } + return 0; +} + +static bool +intel_cursor_joiner_commits_idle(struct intel_display *display, + const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *pipe_crtc; + + for_each_intel_crtc_in_pipe_mask(display, pipe_crtc, + intel_crtc_joined_pipe_mask(crtc_state)) { + struct intel_plane *pipe_plane = + intel_crtc_get_plane(pipe_crtc, PLANE_CURSOR); + struct intel_plane_state *pipe_plane_state = + to_intel_plane_state(pipe_plane->base.state); + + if (pipe_plane_state->uapi.commit && + !try_wait_for_completion(&pipe_plane_state->uapi.commit->hw_done)) + return false; + } + + return true; +} + +static void +intel_cursor_fastpath_update_plane_state(struct intel_plane_state *plane_state, + const struct intel_plane_state *from_plane_state, + struct drm_framebuffer *fb, + struct intel_crtc *hw_crtc, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + u32 src_x, u32 src_y, + u32 src_w, u32 src_h) +{ + /* + * Only the primary owns its uapi state; a secondary mirrors it, so + * its uapi.crtc/fb stay NULL and hw.crtc comes from hw_crtc. + */ + if (plane_state == from_plane_state) { + drm_atomic_set_fb_for_plane(&plane_state->uapi, fb); + + plane_state->uapi.src_x = src_x; + plane_state->uapi.src_y = src_y; + plane_state->uapi.src_w = src_w; + plane_state->uapi.src_h = src_h; + plane_state->uapi.crtc_x = crtc_x; + plane_state->uapi.crtc_y = crtc_y; + plane_state->uapi.crtc_w = crtc_w; + plane_state->uapi.crtc_h = crtc_h; + } + + intel_plane_copy_uapi_to_hw_state(NULL, plane_state, from_plane_state, hw_crtc); +} + +struct intel_cursor_joiner_state { + struct intel_plane *plane; + struct intel_crtc *crtc; + struct intel_crtc_state *crtc_state; + struct intel_plane_state *old_plane_state; + struct intel_plane_state *new_plane_state; +}; + static int intel_legacy_cursor_update(struct drm_plane *_plane, struct drm_crtc *_crtc, @@ -816,11 +899,13 @@ intel_legacy_cursor_update(struct drm_plane *_plane, struct intel_display *display = to_intel_display(plane); struct intel_plane_state *old_plane_state = to_intel_plane_state(plane->base.state); - struct intel_plane_state *new_plane_state; struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); - struct intel_crtc_state *new_crtc_state; struct intel_vblank_evade_ctx evade; + struct intel_cursor_joiner_state joined_pipe_state[I915_MAX_PIPES] = {}; + struct intel_crtc *pipe_crtc; + int num_pipes = 0; + u32 start_vbl_count, end_vbl_count; int ret; /* @@ -829,22 +914,10 @@ intel_legacy_cursor_update(struct drm_plane *_plane, * PSR2 selective fetch also requires the slow path as * PSR2 plane and transcoder registers can only be updated during * vblank. - * - * FIXME joiner fastpath would be good */ if (!crtc_state->hw.active || intel_crtc_needs_modeset(crtc_state) || - intel_crtc_needs_fastset(crtc_state) || - crtc_state->joiner_pipes) - goto slow; - - /* - * Don't do an async update if there is an outstanding commit modifying - * the plane. This prevents our async update's changes from getting - * overridden by a previous synchronous update's state. - */ - if (old_plane_state->uapi.commit && - !try_wait_for_completion(&old_plane_state->uapi.commit->hw_done)) + intel_crtc_needs_fastset(crtc_state)) goto slow; /* @@ -860,46 +933,89 @@ intel_legacy_cursor_update(struct drm_plane *_plane, !old_plane_state->uapi.fb != !fb) goto slow; - new_plane_state = to_intel_plane_state(intel_plane_duplicate_state(&plane->base)); - if (!new_plane_state) - return -ENOMEM; - - new_crtc_state = to_intel_crtc_state(intel_crtc_duplicate_state(&crtc->base)); - if (!new_crtc_state) { - ret = -ENOMEM; - goto out_free; - } + ret = intel_cursor_lock_joined_planes(display, crtc_state, ctx); + if (ret == -EDEADLK) + return ret; + if (ret) + goto slow; - drm_atomic_set_fb_for_plane(&new_plane_state->uapi, fb); + /* + * Don't do an async update if there is an outstanding commit modifying + * any of the joined cursor planes. This prevents our async update's + * changes from getting overridden by a previous synchronous update's + * state. + */ + if (!intel_cursor_joiner_commits_idle(display, crtc_state)) + goto slow; - new_plane_state->uapi.src_x = src_x; - new_plane_state->uapi.src_y = src_y; - new_plane_state->uapi.src_w = src_w; - new_plane_state->uapi.src_h = src_h; - new_plane_state->uapi.crtc_x = crtc_x; - new_plane_state->uapi.crtc_y = crtc_y; - new_plane_state->uapi.crtc_w = crtc_w; - new_plane_state->uapi.crtc_h = crtc_h; + /* + * Iterate over all joined pipes (primary and secondary) uniformly. + * The joined pipe mask includes both the primary pipe and all + * secondary joiner pipes, allowing us to handle them all the same way. + */ + for_each_intel_crtc_in_pipe_mask(display, pipe_crtc, + intel_crtc_joined_pipe_mask(crtc_state)) { + struct intel_cursor_joiner_state *j = &joined_pipe_state[num_pipes]; + + j->plane = intel_crtc_get_plane(pipe_crtc, PLANE_CURSOR); + j->crtc = pipe_crtc; + j->crtc_state = to_intel_crtc_state(pipe_crtc->base.state); + j->old_plane_state = to_intel_plane_state(j->plane->base.state); + j->new_plane_state = + to_intel_plane_state(intel_plane_duplicate_state(&j->plane->base)); + + if (!j->new_plane_state) { + ret = -ENOMEM; + goto out_free; + } - intel_plane_copy_uapi_to_hw_state(NULL, new_plane_state, new_plane_state, crtc); + intel_cursor_fastpath_update_plane_state(j->new_plane_state, + joined_pipe_state[0].new_plane_state, + fb, pipe_crtc, + crtc_x, crtc_y, + crtc_w, crtc_h, + src_x, src_y, + src_w, src_h); + + ret = j->plane->check_plane(j->crtc_state, j->new_plane_state); + if (ret) { + intel_plane_destroy_state(&j->plane->base, + &j->new_plane_state->uapi); + goto out_free; + } - ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state, - old_plane_state, new_plane_state); - if (ret) - goto out_free; + ret = intel_plane_pin_fb(j->new_plane_state, j->old_plane_state); + if (ret) { + intel_plane_destroy_state(&j->plane->base, + &j->new_plane_state->uapi); + goto out_free; + } - ret = intel_plane_pin_fb(new_plane_state, old_plane_state); - if (ret) - goto out_free; + num_pipes++; + } - intel_frontbuffer_flush(to_intel_frontbuffer(new_plane_state->hw.fb), + intel_frontbuffer_flush(to_intel_frontbuffer(joined_pipe_state[0].new_plane_state->hw.fb), ORIGIN_CURSOR_UPDATE); - intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->hw.fb), - to_intel_frontbuffer(new_plane_state->hw.fb), - plane->frontbuffer_bit); - /* Swap plane state */ - plane->base.state = &new_plane_state->uapi; + for (int i = 0; i < num_pipes; i++) { + struct intel_frontbuffer *old_front = + to_intel_frontbuffer(joined_pipe_state[i].old_plane_state->hw.fb); + struct intel_frontbuffer *new_front = + to_intel_frontbuffer(joined_pipe_state[i].new_plane_state->hw.fb); + + intel_frontbuffer_track(old_front, new_front, + joined_pipe_state[i].plane->frontbuffer_bit); + } + + for (int i = 0; i < num_pipes; i++) { + joined_pipe_state[i].plane->base.state = + &joined_pipe_state[i].new_plane_state->uapi; + + if (joined_pipe_state[i].new_plane_state->uapi.visible) + joined_pipe_state[i].crtc_state->active_planes |= BIT(PLANE_CURSOR); + else + joined_pipe_state[i].crtc_state->active_planes &= ~BIT(PLANE_CURSOR); + } /* * We cannot swap crtc_state as it may be in use by an atomic commit or @@ -911,7 +1027,6 @@ intel_legacy_cursor_update(struct drm_plane *_plane, * planes atomically. If the cursor was part of the atomic update then * we would have taken the slowpath. */ - crtc_state->active_planes = new_crtc_state->active_planes; intel_vblank_evade_init(crtc_state, crtc_state, &evade); @@ -933,37 +1048,74 @@ intel_legacy_cursor_update(struct drm_plane *_plane, local_irq_disable(); } - if (new_plane_state->uapi.visible) { - intel_plane_update_noarm(NULL, plane, crtc_state, new_plane_state); - intel_plane_update_arm(NULL, plane, crtc_state, new_plane_state); - } else { - intel_plane_disable_arm(NULL, plane, crtc_state); + /* + * Joiner pipes are vblank-synchronized, so sampling only the primary + * pipe is sufficient to detect a straddle across all joined pipes. + * The vblank evasion above also operates on the primary pipe only. + */ + start_vbl_count = intel_crtc_get_vblank_counter(joined_pipe_state[0].crtc); + + for (int i = 0; i < num_pipes; i++) { + if (joined_pipe_state[i].new_plane_state->uapi.visible) { + intel_plane_update_noarm(NULL, joined_pipe_state[i].plane, + joined_pipe_state[i].crtc_state, + joined_pipe_state[i].new_plane_state); + intel_plane_update_arm(NULL, joined_pipe_state[i].plane, + joined_pipe_state[i].crtc_state, + joined_pipe_state[i].new_plane_state); + } else { + intel_plane_disable_arm(NULL, joined_pipe_state[i].plane, + joined_pipe_state[i].crtc_state); + } } - local_irq_enable(); + end_vbl_count = intel_crtc_get_vblank_counter(joined_pipe_state[0].crtc); - intel_psr_unlock(crtc_state); + local_irq_enable(); - if (old_plane_state->ggtt_vma != new_plane_state->ggtt_vma) { - drm_vblank_work_init(&old_plane_state->unpin_work, &crtc->base, - intel_cursor_unpin_work); + if (start_vbl_count != end_vbl_count) + drm_err(display->drm, + "Atomic update failure on pipe %c (start=%u end=%u)\n", + pipe_name(joined_pipe_state[0].crtc->pipe), + start_vbl_count, end_vbl_count); - drm_vblank_work_schedule(&old_plane_state->unpin_work, - drm_crtc_accurate_vblank_count(&crtc->base) + 1, - false); + intel_psr_unlock(crtc_state); - old_plane_state = NULL; - } else { - intel_plane_unpin_fb(old_plane_state); + /* + * Schedule or immediately unpin old framebuffers. + * Protect against concurrent access. + */ + for (int i = 0; i < num_pipes; i++) { + struct intel_plane_state *old = joined_pipe_state[i].old_plane_state; + + if (old->ggtt_vma != joined_pipe_state[i].new_plane_state->ggtt_vma) { + drm_vblank_work_init(&old->unpin_work, &crtc->base, + intel_cursor_unpin_work); + drm_vblank_work_schedule(&old->unpin_work, + drm_crtc_accurate_vblank_count(&crtc->base) + 1, + false); + joined_pipe_state[i].old_plane_state = NULL; + } else { + intel_plane_unpin_fb(old); + } } out_free: - if (new_crtc_state) - intel_crtc_destroy_state(&crtc->base, &new_crtc_state->uapi); - if (ret) - intel_plane_destroy_state(&plane->base, &new_plane_state->uapi); - else if (old_plane_state) - intel_plane_destroy_state(&plane->base, &old_plane_state->uapi); + if (ret) { + for (int i = 0; i < num_pipes; i++) { + intel_plane_unpin_fb(joined_pipe_state[i].new_plane_state); + intel_plane_destroy_state(&joined_pipe_state[i].plane->base, + &joined_pipe_state[i].new_plane_state->uapi); + } + } else { + for (int i = 0; i < num_pipes; i++) { + if (!joined_pipe_state[i].old_plane_state) + continue; + + intel_plane_destroy_state(&joined_pipe_state[i].plane->base, + &joined_pipe_state[i].old_plane_state->uapi); + } + } return ret; slow: diff --git a/drivers/gpu/drm/i915/display/intel_cursor_regs.h b/drivers/gpu/drm/i915/display/intel_cursor_regs.h index aaa66331063e..da2f9630ae2d 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cursor_regs.h @@ -81,7 +81,7 @@ #define CUR_WM_EN REG_BIT(31) #define CUR_WM_IGNORE_LINES REG_BIT(30) #define CUR_WM_LINES_MASK REG_GENMASK(26, 14) -#define CUR_WM_BLOCKS_MASK REG_GENMASK(11, 0) +#define CUR_WM_BLOCKS_MASK REG_GENMASK(12, 0) #define _CUR_WM_SAGV_A 0x70158 #define _CUR_WM_SAGV_B 0x71158 @@ -98,10 +98,10 @@ #define _CUR_BUF_CFG_A 0x7017c #define _CUR_BUF_CFG_B 0x7117c #define CUR_BUF_CFG(pipe) _MMIO_PIPE((pipe), _CUR_BUF_CFG_A, _CUR_BUF_CFG_B) -/* skl+: 10 bits, icl+ 11 bits, adlp+ 12 bits */ -#define CUR_BUF_END_MASK REG_GENMASK(27, 16) +/* skl+: 10 bits, icl+ 11 bits, adlp+ 12 bits, xe3plpd+ 13 bits */ +#define CUR_BUF_END_MASK REG_GENMASK(28, 16) #define CUR_BUF_END(end) REG_FIELD_PREP(CUR_BUF_END_MASK, (end)) -#define CUR_BUF_START_MASK REG_GENMASK(11, 0) +#define CUR_BUF_START_MASK REG_GENMASK(12, 0) #define CUR_BUF_START(start) REG_FIELD_PREP(CUR_BUF_START_MASK, (start)) /* tgl+ */ diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 02a53c9848e1..9b3b526e5e55 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4325,11 +4325,33 @@ static void mtl_ddi_tc_phy_get_config(struct intel_encoder *encoder, mtl_port_to_pll_id(display, encoder->port)); } +static struct intel_dpll *dg2_ddi_get_pll(struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + + return intel_get_dpll_by_id(display, dg2_port_to_pll_id(encoder->port)); +} + static void dg2_ddi_get_config(struct intel_encoder *encoder, - struct intel_crtc_state *crtc_state) + struct intel_crtc_state *crtc_state) { - intel_mpllb_readout_hw_state(encoder, &crtc_state->dpll_hw_state.mpllb); - crtc_state->port_clock = intel_mpllb_calc_port_clock(encoder, &crtc_state->dpll_hw_state.mpllb); + struct icl_port_dpll *port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + struct intel_dpll *pll = dg2_ddi_get_pll(encoder); + + if (pll) + intel_ddi_get_clock(encoder, crtc_state, pll); + + /* + * Keep the hw readout robust against unexpected NULL PLL lookups, + * so modeset verify always has intel_dpll populated for DG2. + */ + if (!crtc_state->intel_dpll) { + port_dpll->pll = pll; + intel_mpllb_readout_hw_state(encoder, &port_dpll->hw_state.mpllb); + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + crtc_state->port_clock = intel_mpllb_calc_port_clock(encoder, + &port_dpll->hw_state.mpllb); + } intel_ddi_get_config(encoder, crtc_state); } @@ -4657,8 +4679,8 @@ static int intel_ddi_compute_config_late(struct intel_atomic_state *state, if (crtc_state->master_transcoder == crtc_state->cpu_transcoder) { crtc_state->master_transcoder = INVALID_TRANSCODER; - crtc_state->sync_mode_slaves_mask = - port_sync_transcoders & ~BIT(crtc_state->cpu_transcoder); + crtc_state->sync_mode_slaves_mask = port_sync_transcoders & + ~REG_BIT(crtc_state->cpu_transcoder); } return 0; @@ -5343,8 +5365,6 @@ void intel_ddi_init(struct intel_display *display, else encoder->get_config = mtl_ddi_non_tc_phy_get_config; } else if (display->platform.dg2) { - encoder->enable_clock = intel_mpllb_enable; - encoder->disable_clock = intel_mpllb_disable; encoder->get_config = dg2_ddi_get_config; } else if (display->platform.alderlake_s) { encoder->enable_clock = adls_ddi_enable_clock; diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 9dfd095f325f..fc30a455bed3 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -60,7 +60,6 @@ #include "intel_bo.h" #include "intel_bw.h" #include "intel_cdclk.h" -#include "intel_clock_gating.h" #include "intel_cmtg.h" #include "intel_color.h" #include "intel_crt.h" @@ -107,6 +106,7 @@ #include "intel_modeset_verify.h" #include "intel_overlay.h" #include "intel_panel.h" +#include "intel_parent.h" #include "intel_pch_display.h" #include "intel_pch_refclk.h" #include "intel_pfit.h" @@ -119,7 +119,6 @@ #include "intel_sdvo.h" #include "intel_snps_phy.h" #include "intel_tc.h" -#include "intel_tdf.h" #include "intel_tv.h" #include "intel_vblank.h" #include "intel_vdsc.h" @@ -193,6 +192,53 @@ is_trans_port_sync_mode(const struct intel_crtc_state *crtc_state) is_trans_port_sync_slave(crtc_state); } +/* + * Return a bitmask of all the start indices of consecutive bitfields of size + * width in mask. + */ +static unsigned long find_consecutive_bits(unsigned long mask, int width) +{ + unsigned long bit, out_mask = 0; + + if (!width) + return 0; + + for_each_set_bit(bit, &mask, BITS_PER_TYPE(mask)) { + /* For each set bit, see if the following bits are set also */ + unsigned long bitfield = GENMASK(bit + width - 1, bit); + + if ((mask & bitfield) == bitfield) + out_mask |= BIT(bit); + } + + return out_mask; +} + +/* + * Return a bitmask of all valid joiner primary pipes for joining + * num_joined_pipes pipes. For completeness, return all valid pipes for + * num_joined_pipes == 1. + * + * Return 0 if the platform doesn't support joining for the requested number of + * pipes, or there are not enough consecutive pipes available. + */ +u8 intel_joiner_valid_primary_pipe_mask(struct intel_display *display, int num_joined_pipes) +{ + if (num_joined_pipes == 1) { + return DISPLAY_RUNTIME_INFO(display)->pipe_mask; + } else if (num_joined_pipes == 2) { + if (!HAS_UNCOMPRESSED_JOINER(display) && !HAS_BIGJOINER(display)) + return 0; + } else if (num_joined_pipes == 4) { + if (!HAS_ULTRAJOINER(display)) + return 0; + } else { + return 0; + } + + return find_consecutive_bits(DISPLAY_RUNTIME_INFO(display)->pipe_mask, num_joined_pipes); +} + static enum pipe joiner_primary_pipe(const struct intel_crtc_state *crtc_state) { return ffs(crtc_state->joiner_pipes) - 1; @@ -950,8 +996,8 @@ static bool vrr_params_changed(const struct intel_crtc_state *old_crtc_state, static bool cmrr_params_changed(const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *new_crtc_state) { - return old_crtc_state->cmrr.cmrr_m != new_crtc_state->cmrr.cmrr_m || - old_crtc_state->cmrr.cmrr_n != new_crtc_state->cmrr.cmrr_n; + return old_crtc_state->vrr.cmrr.cmrr_m != new_crtc_state->vrr.cmrr.cmrr_m || + old_crtc_state->vrr.cmrr.cmrr_n != new_crtc_state->vrr.cmrr.cmrr_n; } static bool intel_crtc_vrr_enabling(struct intel_atomic_state *state, @@ -1817,9 +1863,7 @@ static void hsw_crtc_disable(struct intel_atomic_state *state, /* Prefer intel_encoder_is_combo() */ bool intel_phy_is_combo(struct intel_display *display, enum phy phy) { - if (phy == PHY_NONE) - return false; - else if (display->platform.alderlake_s) + if (display->platform.alderlake_s) return phy <= PHY_E; else if (display->platform.dg1 || display->platform.rocketlake) return phy <= PHY_D; @@ -1873,7 +1917,7 @@ bool intel_phy_is_snps(struct intel_display *display, enum phy phy) * For DG2, and for DG2 only, all four "combo" ports and the TC1 port * (PHY E) use Synopsis PHYs. See intel_phy_is_tc(). */ - return display->platform.dg2 && phy > PHY_NONE && phy <= PHY_E; + return display->platform.dg2 && phy <= PHY_E; } /* Prefer intel_encoder_to_phy() */ @@ -1891,6 +1935,10 @@ enum phy intel_port_to_phy(struct intel_display *display, enum port port) port == PORT_D) return PHY_A; + if (drm_WARN(display->drm, port < 0, + "PHY is invalid if port < 0 (%d), assuming PHY_A\n", port)) + return PHY_A; + return PHY_A + port - PORT_A; } @@ -5536,9 +5584,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(vrr.flipline); PIPE_CONF_CHECK_I(vrr.vsync_start); PIPE_CONF_CHECK_I(vrr.vsync_end); - PIPE_CONF_CHECK_LLI(cmrr.cmrr_m); - PIPE_CONF_CHECK_LLI(cmrr.cmrr_n); - PIPE_CONF_CHECK_BOOL(cmrr.enable); + PIPE_CONF_CHECK_LLI(vrr.cmrr.cmrr_m); + PIPE_CONF_CHECK_LLI(vrr.cmrr.cmrr_n); + PIPE_CONF_CHECK_BOOL(vrr.cmrr.enable); PIPE_CONF_CHECK_I(vrr.dc_balance.vmin); PIPE_CONF_CHECK_I(vrr.dc_balance.vmax); PIPE_CONF_CHECK_I(vrr.dc_balance.guardband); @@ -7506,7 +7554,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) intel_atomic_commit_fence_wait(state); - intel_td_flush(display); + intel_parent_transient_data_flush(display); intel_atomic_prepare_plane_clear_colors(state); @@ -8113,9 +8161,9 @@ static int max_dotclock(struct intel_display *display) { int max_dotclock = display->cdclk.max_dotclk_freq; - if (HAS_ULTRAJOINER(display)) + if (intel_joiner_valid_primary_pipe_mask(display, 4)) max_dotclock *= 4; - else if (HAS_UNCOMPRESSED_JOINER(display) || HAS_BIGJOINER(display)) + else if (intel_joiner_valid_primary_pipe_mask(display, 2)) max_dotclock *= 2; return max_dotclock; diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 57ea4f2edf2a..eecafff167d3 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -136,8 +136,6 @@ enum tc_port { }; enum phy { - PHY_NONE = -1, - PHY_A = 0, PHY_B, PHY_C, @@ -373,6 +371,7 @@ intel_cpu_transcoder_mode_valid(struct intel_display *display, enum phy intel_port_to_phy(struct intel_display *display, enum port port); bool is_trans_port_sync_mode(const struct intel_crtc_state *state); bool is_trans_port_sync_master(const struct intel_crtc_state *state); +u8 intel_joiner_valid_primary_pipe_mask(struct intel_display *display, int num_joined_pipes); u8 intel_crtc_joined_pipe_mask(const struct intel_crtc_state *crtc_state); bool intel_crtc_is_joiner_secondary(const struct intel_crtc_state *crtc_state); bool intel_crtc_is_joiner_primary(const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display_clock_gating.c b/drivers/gpu/drm/i915/display/intel_display_clock_gating.c index d036c7e5863d..ef1ee72494df 100644 --- a/drivers/gpu/drm/i915/display/intel_display_clock_gating.c +++ b/drivers/gpu/drm/i915/display/intel_display_clock_gating.c @@ -27,7 +27,7 @@ static void intel_display_gen9_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_MEMORY_WAKE); } -void intel_display_skl_init_clock_gating(struct intel_display *display) +static void intel_display_skl_init_clock_gating(struct intel_display *display) { /* * WaCompressedResourceDisplayNewHashMode:skl,kbl @@ -47,7 +47,7 @@ void intel_display_skl_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_kbl_init_clock_gating(struct intel_display *display) +static void intel_display_kbl_init_clock_gating(struct intel_display *display) { /* * WaCompressedResourceDisplayNewHashMode:skl,kbl @@ -67,7 +67,7 @@ void intel_display_kbl_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_cfl_init_clock_gating(struct intel_display *display) +static void intel_display_cfl_init_clock_gating(struct intel_display *display) { /* * WaCompressedResourceDisplayNewHashMode:skl,kbl (and cfl, cml) @@ -93,7 +93,7 @@ void intel_display_cfl_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_bxt_init_clock_gating(struct intel_display *display) +static void intel_display_bxt_init_clock_gating(struct intel_display *display) { intel_display_gen9_init_clock_gating(display); @@ -120,7 +120,7 @@ void intel_display_bxt_init_clock_gating(struct intel_display *display) intel_de_rmw(display, DISP_ARB_CTL, 0, DISP_FBC_WM_DIS); } -void intel_display_glk_init_clock_gating(struct intel_display *display) +static void intel_display_glk_init_clock_gating(struct intel_display *display) { intel_display_gen9_init_clock_gating(display); @@ -134,13 +134,13 @@ void intel_display_glk_init_clock_gating(struct intel_display *display) PWM1_GATING_DIS | PWM2_GATING_DIS); } -void intel_display_bdw_clock_gating_disable_fbcq(struct intel_display *display) +static void intel_display_bdw_clock_gating_disable_fbcq(struct intel_display *display) { /* WaFbcAsynchFlipDisableFbcQueue:hsw,bdw */ intel_de_rmw(display, CHICKEN_PIPESL_1(PIPE_A), 0, HSW_FBCQ_DIS); } -void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display) +static void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display) { enum pipe pipe; @@ -154,14 +154,21 @@ void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display) } } -void intel_display_bdw_clock_gating_kvm_notif(struct intel_display *display) +static void intel_display_bdw_clock_gating_kvm_notif(struct intel_display *display) { /* WaKVMNotificationOnConfigChange:bdw */ intel_de_rmw(display, CHICKEN_PAR2_1, 0, KVM_CONFIG_CHANGE_NOTIFICATION_SELECT); } -void intel_display_hsw_init_clock_gating(struct intel_display *display) +static void intel_display_bdw_init_clock_gating(struct intel_display *display) +{ + intel_display_bdw_clock_gating_disable_fbcq(display); + intel_display_bdw_clock_gating_vblank_in_srd(display); + intel_display_bdw_clock_gating_kvm_notif(display); +} + +static void intel_display_hsw_init_clock_gating(struct intel_display *display) { enum pipe pipe; @@ -178,7 +185,7 @@ void intel_display_hsw_init_clock_gating(struct intel_display *display) } } -void intel_display_disable_trickle_feed(struct intel_display *display) +static void intel_display_disable_trickle_feed(struct intel_display *display) { enum pipe pipe; @@ -191,7 +198,7 @@ void intel_display_disable_trickle_feed(struct intel_display *display) } } -void intel_display_ilk_init_clock_gating(struct intel_display *display) +static void intel_display_ilk_init_clock_gating(struct intel_display *display) { u32 dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; @@ -223,7 +230,7 @@ void intel_display_ilk_init_clock_gating(struct intel_display *display) intel_display_disable_trickle_feed(display); } -void intel_display_gen6_init_clock_gating(struct intel_display *display) +static void intel_display_gen6_init_clock_gating(struct intel_display *display) { u32 dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; @@ -244,13 +251,15 @@ void intel_display_gen6_init_clock_gating(struct intel_display *display) intel_display_disable_trickle_feed(display); } -void intel_display_ivb_init_clock_gating(struct intel_display *display) +static void intel_display_ivb_init_clock_gating(struct intel_display *display) { intel_de_write(display, ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); intel_de_rmw(display, ILK_DISPLAY_CHICKEN1, 0, ILK_FBCQ_DIS); + + intel_display_disable_trickle_feed(display); } -void intel_display_g4x_init_clock_gating(struct intel_display *display) +static void intel_display_g4x_init_clock_gating(struct intel_display *display) { u32 dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE | OVRUNIT_CLOCK_GATE_DISABLE | @@ -264,7 +273,35 @@ void intel_display_g4x_init_clock_gating(struct intel_display *display) intel_display_disable_trickle_feed(display); } -void intel_display_i965gm_init_clock_gating(struct intel_display *display) +static void intel_display_i965gm_init_clock_gating(struct intel_display *display) { intel_de_write(display, DSPCLK_GATE_D, 0); } + +void intel_display_init_clock_gating(struct intel_display *display) +{ + if (display->platform.skylake) + intel_display_skl_init_clock_gating(display); + else if (display->platform.kabylake) + intel_display_kbl_init_clock_gating(display); + else if (display->platform.coffeelake || display->platform.cometlake) + intel_display_cfl_init_clock_gating(display); + else if (display->platform.broxton) + intel_display_bxt_init_clock_gating(display); + else if (display->platform.geminilake) + intel_display_glk_init_clock_gating(display); + else if (display->platform.broadwell) + intel_display_bdw_init_clock_gating(display); + else if (display->platform.haswell) + intel_display_hsw_init_clock_gating(display); + else if (display->platform.ivybridge) + intel_display_ivb_init_clock_gating(display); + else if (display->platform.sandybridge) + intel_display_gen6_init_clock_gating(display); + else if (display->platform.ironlake) + intel_display_ilk_init_clock_gating(display); + else if (display->platform.g4x) + intel_display_g4x_init_clock_gating(display); + else if (display->platform.i965gm) + intel_display_i965gm_init_clock_gating(display); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_clock_gating.h b/drivers/gpu/drm/i915/display/intel_display_clock_gating.h index b6dd34ca92dd..dbfa5892cffe 100644 --- a/drivers/gpu/drm/i915/display/intel_display_clock_gating.h +++ b/drivers/gpu/drm/i915/display/intel_display_clock_gating.h @@ -8,20 +8,6 @@ struct intel_display; -void intel_display_skl_init_clock_gating(struct intel_display *display); -void intel_display_kbl_init_clock_gating(struct intel_display *display); -void intel_display_cfl_init_clock_gating(struct intel_display *display); -void intel_display_bxt_init_clock_gating(struct intel_display *display); -void intel_display_glk_init_clock_gating(struct intel_display *display); -void intel_display_bdw_clock_gating_disable_fbcq(struct intel_display *display); -void intel_display_bdw_clock_gating_vblank_in_srd(struct intel_display *display); -void intel_display_bdw_clock_gating_kvm_notif(struct intel_display *display); -void intel_display_hsw_init_clock_gating(struct intel_display *display); -void intel_display_disable_trickle_feed(struct intel_display *display); -void intel_display_ilk_init_clock_gating(struct intel_display *display); -void intel_display_gen6_init_clock_gating(struct intel_display *display); -void intel_display_ivb_init_clock_gating(struct intel_display *display); -void intel_display_g4x_init_clock_gating(struct intel_display *display); -void intel_display_i965gm_init_clock_gating(struct intel_display *display); +void intel_display_init_clock_gating(struct intel_display *display); #endif /* __INTEL_DISPLAY_CLOCK_GATING_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 3f02868ef105..3e302f23f247 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -49,6 +49,7 @@ #include "intel_psr.h" #include "intel_psr_regs.h" #include "intel_vdsc.h" +#include "intel_vrr.h" #include "intel_wm.h" #include "intel_tc.h" @@ -1284,22 +1285,11 @@ static ssize_t i915_joiner_write(struct file *file, if (ret < 0) return ret; - switch (force_joined_pipes) { - case 0: - case 1: - case 2: - connector->force_joined_pipes = force_joined_pipes; - break; - case 4: - if (HAS_ULTRAJOINER(display)) { - connector->force_joined_pipes = force_joined_pipes; - break; - } - - fallthrough; - default: + if (force_joined_pipes && + !intel_joiner_valid_primary_pipe_mask(display, force_joined_pipes)) return -EINVAL; - } + + connector->force_joined_pipes = force_joined_pipes; *offp += len; @@ -1395,6 +1385,7 @@ void intel_crtc_debugfs_add(struct intel_crtc *crtc) intel_drrs_crtc_debugfs_add(crtc); intel_fbc_crtc_debugfs_add(crtc); hsw_ips_crtc_debugfs_add(crtc); + intel_vrr_crtc_debugfs_add(crtc); debugfs_create_file("i915_current_bpc", 0444, root, crtc, &i915_current_bpc_fops); diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 02cb4d800e23..233465a8306e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -325,7 +325,11 @@ static enum phy icl_aux_pw_to_phy(struct intel_display *display, { struct intel_encoder *encoder = icl_aux_pw_to_encoder(display, power_well); - return encoder ? intel_encoder_to_phy(encoder) : PHY_NONE; + if (drm_WARN(display->drm, !encoder, + "PHY is invalid if encoder is NULL, assuming PHY_A\n")) + return PHY_A; + + return intel_encoder_to_phy(encoder); } static bool icl_aux_pw_is_tc_phy(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 0f5018482497..8251ec15190b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -2788,7 +2788,7 @@ enum skl_power_gate { #define BXT_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 20) /* bxt/glk */ #define BXT_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, (pipe)) #define BXT_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, 3) -#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl+ */ +#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl-lnl */ #define ICL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1) #define ICL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, 7) #define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) /* pre-icl */ @@ -2874,9 +2874,19 @@ enum skl_power_gate { #define ICL_DPCLKA_CFGCR0_TC_CLK_OFF(tc_port) (1 << ((tc_port) < TC_PORT_4 ? \ (tc_port) + 12 : \ (tc_port) - TC_PORT_4 + 21)) -#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) ((phy) * 2) -#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(phy) (3 << ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) -#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL(pll, phy) ((pll) << ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) + +/* + * ICL_DPCLKA_CFGCR0 has a 2-bit DDI_CLK_SEL field per combo PHY, for + * PHY_A..PHY_D only. Any other phy value (PHY_NONE, TypeC/SNPS PHYs) + * is not valid here. + */ +#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) _PICK(phy & 0x3, 0, 2, 4, 6) +#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(phy) \ + REG_GENMASK(ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) + 1, \ + ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) +#define ICL_DPCLKA_CFGCR0_DDI_CLK_SEL(pll, phy) \ + ((pll) << ICL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) + #define RKL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy) _PICK(phy, 0, 2, 4, 27) #define RKL_DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(phy) \ (3 << RKL_DPCLKA_CFGCR0_DDI_CLK_SEL_SHIFT(phy)) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b7cc361fd955..5f0fe18c0614 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1408,13 +1408,13 @@ struct intel_crtc_state { u16 max_increase, max_decrease; u16 vblank_target; } dc_balance; - } vrr; - /* Content Match Refresh Rate state */ - struct { - bool enable; - u64 cmrr_n, cmrr_m; - } cmrr; + /* Content Match Refresh Rate state */ + struct { + bool enable; + u64 cmrr_n, cmrr_m; + } cmrr; + } vrr; /* Stream Splitter for eDP MSO */ struct { @@ -1553,6 +1553,11 @@ struct intel_crtc { u64 flip_count; } dc_balance; + struct { + u32 numerator; + u32 denominator; + } force_cmrr; + int scanline_offset; struct { @@ -1946,6 +1951,8 @@ struct intel_dp { bool colorimetry_support; + bool sst_split_sdp_support; + struct { enum transcoder transcoder; struct mutex lock; diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.c b/drivers/gpu/drm/i915/display/intel_display_wa.c index 3662e0f17c69..888676693f5e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_wa.c +++ b/drivers/gpu/drm/i915/display/intel_display_wa.c @@ -4,6 +4,7 @@ */ #include <drm/drm_print.h> +#include <drm/intel/display_parent_interface.h> #include <drm/intel/step.h> #include "intel_de.h" @@ -131,7 +132,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa, case INTEL_DISPLAY_WA_16011863758: return DISPLAY_VER(display) >= 11; case INTEL_DISPLAY_WA_16023588340: - return intel_display_needs_wa_16023588340(display); + /* This is a GT workaround. */ + return display->parent->wa && display->parent->wa->wa_16023588340(display->drm); case INTEL_DISPLAY_WA_16025573575: return intel_display_needs_wa_16025573575(display); case INTEL_DISPLAY_WA_16025596647: diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.h b/drivers/gpu/drm/i915/display/intel_display_wa.h index 338b32e4162d..425a300840aa 100644 --- a/drivers/gpu/drm/i915/display/intel_display_wa.h +++ b/drivers/gpu/drm/i915/display/intel_display_wa.h @@ -12,15 +12,6 @@ struct intel_display; void intel_display_wa_apply(struct intel_display *display); -#ifdef I915 -static inline bool intel_display_needs_wa_16023588340(struct intel_display *display) -{ - return false; -} -#else -bool intel_display_needs_wa_16023588340(struct intel_display *display); -#endif - /* * This enum lists display workarounds; each entry here must have a * corresponding case in __intel_display_wa(). Keep both sorted by lineage diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 113d767e62e9..0cd5e6b5034c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1274,17 +1274,7 @@ bool intel_dp_can_join(struct intel_dp *intel_dp, if (num_joined_pipes > 1 && !intel_dp_has_joiner(intel_dp)) return false; - switch (num_joined_pipes) { - case 1: - return true; - case 2: - return HAS_BIGJOINER(display) || - HAS_UNCOMPRESSED_JOINER(display); - case 4: - return HAS_ULTRAJOINER(display); - default: - return false; - } + return intel_joiner_valid_primary_pipe_mask(display, num_joined_pipes); } bool intel_dp_dotclk_valid(struct intel_display *display, @@ -2920,6 +2910,7 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, struct drm_connector_state *conn_state, bool respect_downstream_limits) { + struct intel_display *display = to_intel_display(encoder); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct intel_connector *connector = to_intel_connector(conn_state->connector); @@ -2934,6 +2925,10 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, return -EINVAL; for_each_joiner_candidate(connector, adjusted_mode, num_joined_pipes) { + /* If the pipe can't be a joiner primary, skip early. */ + if (!(intel_joiner_valid_primary_pipe_mask(display, num_joined_pipes) & BIT(crtc->pipe))) + continue; + /* * NOTE: * The crtc_state->joiner_pipes should have been set at the end @@ -3125,6 +3120,7 @@ static void intel_dp_compute_as_sdp(struct intel_dp *intel_dp, struct drm_dp_as_sdp *as_sdp = &crtc_state->infoframes.as_sdp; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); /* * #FIXME: SDP/infoframe updates aren’t truly atomic, and with the new @@ -3142,10 +3138,14 @@ static void intel_dp_compute_as_sdp(struct intel_dp *intel_dp, as_sdp->revision = 0x2; as_sdp->vtotal = intel_vrr_vmin_vtotal(crtc_state); - if (crtc_state->cmrr.enable) { + if (crtc_state->vrr.cmrr.enable) { as_sdp->mode = DP_AS_SDP_FAVT_TRR_REACHED; as_sdp->target_rr = drm_mode_vrefresh(adjusted_mode); - as_sdp->target_rr_divider = true; + + if (crtc->force_cmrr.denominator == 1001) + as_sdp->target_rr_divider = true; + else + as_sdp->target_rr_divider = false; } else if (crtc_state->vrr.enable) { as_sdp->mode = DP_AS_SDP_AVT_DYNAMIC_VTOTAL; } else { @@ -3409,12 +3409,22 @@ intel_dp_audio_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + pipe_config->has_audio = intel_dp_has_audio(encoder, conn_state) && intel_audio_compute_config(encoder, pipe_config, conn_state); pipe_config->sdp_split_enable = pipe_config->has_audio && intel_dp_is_uhbr(pipe_config); + + /* + * SDP splitting for UHBR audio requires explicit sink capability in + * SST mode, whereas in MST mode it is inherently supported. + */ + if (pipe_config->sdp_split_enable && + !intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) + pipe_config->sdp_split_enable = intel_dp->sst_split_sdp_support; } void @@ -4129,14 +4139,9 @@ static int intel_dp_pcon_set_frl_mask(int max_frl) static int intel_dp_hdmi_sink_max_frl(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_frl_rate; - int max_lanes, rate_per_lane; - int max_dsc_lanes, dsc_rate_per_lane; - - max_lanes = info->hdmi.max_lanes; - rate_per_lane = info->hdmi.max_frl_rate_per_lane; - max_frl_rate = max_lanes * rate_per_lane; + struct drm_connector *drm_connector = &connector->base; + int max_frl_rate = intel_hdmi_sink_max_frl_rate(drm_connector); + int dsc_max_frl_rate = intel_hdmi_sink_dsc_max_frl_rate(drm_connector); /* * The sink's DSC max FRL rate only applies to compressed video @@ -4145,12 +4150,8 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) * the regular max FRL rate is the limit. */ if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && - info->hdmi.dsc_cap.v_1p2) { - max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; - dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; - if (max_dsc_lanes && dsc_rate_per_lane) - max_frl_rate = min(max_frl_rate, max_dsc_lanes * dsc_rate_per_lane); - } + dsc_max_frl_rate) + return min(max_frl_rate, dsc_max_frl_rate); return max_frl_rate; } @@ -4316,7 +4317,9 @@ intel_dp_pcon_dsc_enc_slices(struct intel_dp *intel_dp, int pcon_max_slices = drm_dp_pcon_dsc_max_slices(intel_dp->pcon_dsc_dpcd); int pcon_max_slice_width = drm_dp_pcon_dsc_max_slice_width(intel_dp->pcon_dsc_dpcd); - return intel_hdmi_dsc_get_num_slices(crtc_state, pcon_max_slices, + return intel_hdmi_dsc_get_num_slices(&crtc_state->hw.adjusted_mode, + crtc_state->output_format, + pcon_max_slices, pcon_max_slice_width, hdmi_max_slices, hdmi_throughput); } @@ -4333,9 +4336,10 @@ intel_dp_pcon_dsc_enc_bpp(struct intel_dp *intel_dp, int pcon_fractional_bpp = drm_dp_pcon_dsc_bpp_incr(intel_dp->pcon_dsc_dpcd); int hdmi_max_chunk_bytes = info->hdmi.dsc_cap.total_chunk_kbytes * 1024; + int bpc = crtc_state->pipe_bpp / 3; return intel_hdmi_dsc_get_bpp(pcon_fractional_bpp, slice_width, - num_slices, output_format, hdmi_all_bpp, + num_slices, output_format, bpc, hdmi_all_bpp, hdmi_max_chunk_bytes); } @@ -4462,16 +4466,25 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, str_enable_disable(tmp)); } -static bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp) +static u8 intel_dp_read_dprx_feature_enum(struct intel_dp *intel_dp) { u8 dprx = 0; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST, - &dprx) != 1) - return false; + drm_dp_dpcd_read_data(&intel_dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST, + &dprx, sizeof(dprx)); + return dprx; +} + +static bool intel_dp_get_colorimetry_status(u8 dprx) +{ return dprx & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED; } +static bool intel_dp_get_sst_split_sdp_status(u8 dprx) +{ + return dprx & DP_SST_SPLIT_SDP_CAP; +} + static int intel_dp_read_dsc_dpcd(struct drm_dp_aux *aux, u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) { @@ -4771,6 +4784,7 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector { struct intel_display *display = to_intel_display(intel_dp); int ret; + u8 dprx; /* this function is meant to be called only once */ drm_WARN_ON(display->drm, intel_dp->dpcd[DP_DPCD_REV] != 0); @@ -4782,8 +4796,13 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector drm_dp_is_branch(intel_dp->dpcd)); intel_init_dpcd_quirks(intel_dp, &intel_dp->desc.ident); + dprx = intel_dp_read_dprx_feature_enum(intel_dp); + intel_dp->colorimetry_support = - intel_dp_get_colorimetry_status(intel_dp); + intel_dp_get_colorimetry_status(dprx); + + intel_dp->sst_split_sdp_support = + intel_dp_get_sst_split_sdp_status(dprx); /* * Read the eDP display control registers. @@ -4874,13 +4893,20 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) * the OUI/ID since we know it won't change. */ if (!intel_dp_is_edp(intel_dp)) { + u8 dprx; + drm_dp_read_desc(&intel_dp->aux, &intel_dp->desc, drm_dp_is_branch(intel_dp->dpcd)); intel_init_dpcd_quirks(intel_dp, &intel_dp->desc.ident); + dprx = intel_dp_read_dprx_feature_enum(intel_dp); + intel_dp->colorimetry_support = - intel_dp_get_colorimetry_status(intel_dp); + intel_dp_get_colorimetry_status(dprx); + + intel_dp->sst_split_sdp_support = + intel_dp_get_sst_split_sdp_status(dprx); intel_dp_update_sink_caps(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 57daed0b0b36..6a869d0f6ffc 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -766,6 +766,10 @@ static int mst_stream_compute_config(struct intel_atomic_state *state, return ret; for_each_joiner_candidate(connector, adjusted_mode, num_joined_pipes) { + /* If the pipe can't be a joiner primary, skip early. */ + if (!(intel_joiner_valid_primary_pipe_mask(display, num_joined_pipes) & BIT(crtc->pipe))) + continue; + if (num_joined_pipes > 1) pipe_config->joiner_pipes = GENMASK(crtc->pipe + num_joined_pipes - 1, crtc->pipe); diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index f40807a5566b..20fd091cb9db 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -22,7 +22,6 @@ #include "intel_lvds_regs.h" #include "intel_panel.h" #include "intel_pps.h" -#include "intel_snps_phy.h" #include "vlv_dpio_phy_regs.h" #include "vlv_sideband.h" @@ -1194,24 +1193,6 @@ static int hsw_crtc_get_dpll(struct intel_atomic_state *state, return intel_dpll_reserve(state, crtc, encoder); } -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); - int ret; - - ret = intel_mpllb_calc_state(crtc_state, encoder); - if (ret) - return ret; - - crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state); - - return 0; -} - static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); @@ -1682,7 +1663,8 @@ static const struct intel_dpll_global_funcs mtl_dpll_funcs = { }; static const struct intel_dpll_global_funcs dg2_dpll_funcs = { - .crtc_compute_clock = dg2_crtc_compute_clock, + .crtc_compute_clock = hsw_crtc_compute_clock, + .crtc_get_dpll = hsw_crtc_get_dpll, }; static const struct intel_dpll_global_funcs hsw_dpll_funcs = { diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 0f933101b7dd..bfd6f0a19725 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -42,6 +42,7 @@ #include "intel_lt_phy.h" #include "intel_mg_phy_regs.h" #include "intel_pch_refclk.h" +#include "intel_snps_phy.h" #include "intel_tc.h" /** @@ -4735,6 +4736,214 @@ static const struct intel_dpll_mgr xe3plpd_pll_mgr = { .compare_hw_state = xe3plpd_compare_hw_state, }; +enum intel_dpll_id dg2_port_to_pll_id(enum port port) +{ + switch (port) { + case PORT_A: + return DPLL_ID_DG2_DPLL_A; + case PORT_B: + return DPLL_ID_DG2_DPLL_B; + case PORT_C: + return DPLL_ID_DG2_DPLL_C; + case PORT_D_XELPD: + return DPLL_ID_DG2_DPLL_D; + case PORT_TC1: + return DPLL_ID_DG2_DPLL_E; + default: + MISSING_CASE(port); + return DPLL_ID_DG2_DPLL_A; + } +} + +static struct intel_encoder *dg2_get_intel_encoder(struct intel_display *display, + const struct intel_dpll *pll) +{ + struct intel_encoder *encoder; + + for_each_intel_encoder(display->drm, encoder) { + if (dg2_port_to_pll_id(encoder->port) == pll->info->id && + intel_encoder_is_dig_port(encoder)) + return encoder; + } + + return NULL; +} + +static void dg2_mpllb_enable(struct intel_display *display, + struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_mpllb_enable_phy(encoder, &dpll_hw_state->mpllb); +} + +static void dg2_mpllb_disable(struct intel_display *display, + struct intel_dpll *pll) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return; + + intel_mpllb_disable(encoder); +} + +static bool dg2_mpllb_get_hw_state(struct intel_display *display, + struct intel_dpll *pll, + struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + enum phy phy; + i915_reg_t enable_reg; + struct ref_tracker *wakeref; + bool ret = false; + u32 val; + + if (!encoder) + return false; + + wakeref = intel_display_power_get_if_enabled(display, + POWER_DOMAIN_DISPLAY_CORE); + if (!wakeref) + return false; + + phy = intel_encoder_to_phy(encoder); + enable_reg = (phy <= PHY_D ? DG2_PLL_ENABLE(phy) : MG_PLL_ENABLE(0)); + + val = intel_de_read(display, enable_reg); + if (!(val & PLL_ENABLE)) + goto out; + + intel_mpllb_readout_hw_state(encoder, &dpll_hw_state->mpllb); + ret = true; + +out: + intel_display_power_put(display, POWER_DOMAIN_DISPLAY_CORE, wakeref); + return ret; +} + +static int dg2_mpllb_get_freq(struct intel_display *display, + const struct intel_dpll *pll, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + struct intel_encoder *encoder = dg2_get_intel_encoder(display, pll); + + if (drm_WARN_ON(display->drm, !encoder)) + return 0; + + return intel_mpllb_calc_port_clock(encoder, &dpll_hw_state->mpllb); +} + +static const struct intel_dpll_funcs mpllb_pll_funcs = { + .enable = dg2_mpllb_enable, + .disable = dg2_mpllb_disable, + .get_hw_state = dg2_mpllb_get_hw_state, + .get_freq = dg2_mpllb_get_freq, +}; + +static const struct dpll_info dg2_plls[] = { + { .name = "MPLLB A", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_A, }, + { .name = "MPLLB B", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_B, }, + { .name = "MPLLB C", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_C, }, + { .name = "MPLLB D", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_D, }, + { .name = "MPLLB E", .funcs = &mpllb_pll_funcs, .id = DPLL_ID_DG2_DPLL_E, }, + {} +}; + +static int dg2_compute_dplls(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 icl_port_dpll *port_dpll = + &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + int ret; + + ret = intel_mpllb_calc_state(crtc_state, encoder); + if (ret) + return ret; + + port_dpll->hw_state = crtc_state->dpll_hw_state; + + /* this is mainly for the fastset check */ + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + + crtc_state->port_clock = intel_mpllb_calc_port_clock(encoder, + &port_dpll->hw_state.mpllb); + + return 0; +} + +static int dg2_get_dplls(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 icl_port_dpll *port_dpll = + &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; + enum intel_dpll_id dpll_id = dg2_port_to_pll_id(encoder->port); + + port_dpll->pll = intel_find_dpll(state, crtc, + &port_dpll->hw_state, + BIT(dpll_id)); + if (!port_dpll->pll) + return -EINVAL; + + intel_reference_dpll(state, crtc, + port_dpll->pll, &port_dpll->hw_state); + + icl_set_active_port_dpll(crtc_state, ICL_PORT_DPLL_DEFAULT); + + return 0; +} + +static void dg2_dump_hw_state(struct drm_printer *p, + const struct intel_dpll_hw_state *dpll_hw_state) +{ + const struct intel_mpllb_state *hw_state = &dpll_hw_state->mpllb; + + drm_printf(p, "dpll_hw_state: mpllb_cp: 0x%x, mpllb_div: 0x%x, " + "mpllb_div2: 0x%x, mpllb_fracn1: 0x%x, " + "mpllb_fracn2: 0x%x, mpllb_sscen: 0x%x, " + "mpllb_sscstep: 0x%x, ref_control: 0x%x\n", + hw_state->mpllb_cp, hw_state->mpllb_div, + hw_state->mpllb_div2, hw_state->mpllb_fracn1, + hw_state->mpllb_fracn2, hw_state->mpllb_sscen, + hw_state->mpllb_sscstep, hw_state->ref_control); +} + +static bool dg2_compare_hw_state(const struct intel_dpll_hw_state *_a, + const struct intel_dpll_hw_state *_b) +{ + const struct intel_mpllb_state *a = &_a->mpllb; + const struct intel_mpllb_state *b = &_b->mpllb; + + return a->mpllb_cp == b->mpllb_cp && + a->mpllb_div == b->mpllb_div && + a->mpllb_div2 == b->mpllb_div2 && + a->mpllb_fracn1 == b->mpllb_fracn1 && + a->mpllb_fracn2 == b->mpllb_fracn2 && + a->mpllb_sscen == b->mpllb_sscen && + a->mpllb_sscstep == b->mpllb_sscstep; +} + +static const struct intel_dpll_mgr dg2_pll_mgr = { + .dpll_info = dg2_plls, + .compute_dplls = dg2_compute_dplls, + .get_dplls = dg2_get_dplls, + .put_dplls = icl_put_dplls, + .update_active_dpll = icl_update_active_dpll, + .update_ref_clks = icl_update_dpll_ref_clks, + .dump_hw_state = dg2_dump_hw_state, + .compare_hw_state = dg2_compare_hw_state, +}; + /** * intel_dpll_init - Initialize DPLLs * @display: intel_display device @@ -4750,8 +4959,7 @@ void intel_dpll_init(struct intel_display *display) mutex_init(&display->dpll.lock); if (display->platform.dg2) - /* No shared DPLLs on DG2; port PLLs are part of the PHY */ - dpll_mgr = NULL; + dpll_mgr = &dg2_pll_mgr; else if (DISPLAY_VER(display) >= 35) dpll_mgr = &xe3plpd_pll_mgr; else if (DISPLAY_VER(display) >= 14) @@ -5120,8 +5328,13 @@ verify_single_dpll_state(struct intel_display *display, if (pll->on) { const struct intel_dpll_mgr *dpll_mgr = display->dpll.mgr; - - if (HAS_LT_PHY(display)) + /* + * Avoid direct struct comparison here. Some hw state fields, such + * as DG2 MPLLB ref_control or LT PHY config[1], are written by + * firmware and may differ from the software state without indicating + * a real mismatch. + */ + if (HAS_LT_PHY(display) || display->platform.dg2) pll_mismatch = !dpll_mgr->compare_hw_state(&pll->state.hw_state, &dpll_hw_state); else diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index d408ccf6f902..f4b84733a58c 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -169,9 +169,31 @@ enum intel_dpll_id { * @DPLL_ID_DG1_DPLL3: DG1 combo PHY DPLL3 */ DPLL_ID_DG1_DPLL3 = 3, + + /** + * @DPLL_ID_DG2_DPLL_A: DG2 port PLL for PHY A (PORT_A) + */ + DPLL_ID_DG2_DPLL_A = 0, + /** + * @DPLL_ID_DG2_DPLL_B: DG2 port PLL for PHY B (PORT_B) + */ + DPLL_ID_DG2_DPLL_B = 1, + /** + * @DPLL_ID_DG2_DPLL_C: DG2 port PLL for PHY C (PORT_C) + */ + DPLL_ID_DG2_DPLL_C = 2, + /** + * @DPLL_ID_DG2_DPLL_D: DG2 port PLL for PHY D (PORT_D_XELPD) + */ + DPLL_ID_DG2_DPLL_D = 3, + /** + * @DPLL_ID_DG2_DPLL_E: DG2 port PLL for PHY F (PORT_TC1) + */ + DPLL_ID_DG2_DPLL_E = 4, }; #define I915_NUM_PLLS 9 +enum intel_dpll_id dg2_port_to_pll_id(enum port port); enum icl_port_dpll_id { ICL_PORT_DPLL_DEFAULT, diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c index 705742e117ca..70aae6ffa3fe 100644 --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -66,7 +66,6 @@ #include "intel_frontbuffer.h" #include "intel_parent.h" #include "intel_psr.h" -#include "intel_tdf.h" /** * frontbuffer_flush - flush frontbuffer @@ -95,7 +94,7 @@ static void frontbuffer_flush(struct intel_display *display, trace_intel_frontbuffer_flush(display, frontbuffer_bits, origin); might_sleep(); - intel_td_flush(display); + intel_parent_transient_data_flush(display); intel_drrs_flush(display, frontbuffer_bits); intel_psr_flush(display, frontbuffer_bits, origin); intel_fbc_flush(display, frontbuffer_bits, origin); diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 8a019d3574df..b6fa1a1558b8 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2399,10 +2399,6 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder, if (!intel_link_bw_compute_pipe_bpp(pipe_config)) return -EINVAL; - pipe_config->has_audio = - intel_hdmi_has_audio(encoder, pipe_config, conn_state) && - intel_audio_compute_config(encoder, pipe_config, conn_state); - /* * Try to respect downstream TMDS clock limits first, if * that fails assume the user might know something we don't. @@ -2417,6 +2413,10 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder, return ret; } + pipe_config->has_audio = + intel_hdmi_has_audio(encoder, pipe_config, conn_state) && + intel_audio_compute_config(encoder, pipe_config, conn_state); + ret = intel_pfit_compute_config(pipe_config, conn_state); if (ret) return ret; @@ -3216,7 +3216,8 @@ int intel_hdmi_dsc_get_slice_height(int vactive) * intel_hdmi_dsc_get_num_slices - get no. of dsc slices based on dsc encoder * and dsc decoder capabilities * - * @crtc_state: intel crtc_state + * @mode: drm_display_mode for which num of slices are needed + * @output_format : pipe output format * @src_max_slices: maximum slices supported by the DSC encoder * @src_max_slice_width: maximum slice width supported by DSC encoder * @hdmi_max_slices: maximum slices supported by sink DSC decoder @@ -3226,7 +3227,8 @@ int intel_hdmi_dsc_get_slice_height(int vactive) * and decoder. */ int -intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, +intel_hdmi_dsc_get_num_slices(const struct drm_display_mode *mode, + enum intel_output_format output_format, int src_max_slices, int src_max_slice_width, int hdmi_max_slices, int hdmi_throughput) { @@ -3248,7 +3250,7 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, int max_throughput; /* max clock freq. in khz per slice */ int max_slice_width; int slice_width; - int pixel_clock = crtc_state->hw.adjusted_mode.crtc_clock; + int pixel_clock = mode->crtc_clock; if (!hdmi_throughput) return 0; @@ -3259,8 +3261,8 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, * for 4:4:4 is 1.0. Multiplying these factors by 10 and later * dividing adjusted clock value by 10. */ - if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 || - crtc_state->output_format == INTEL_OUTPUT_FORMAT_RGB) + if (output_format == INTEL_OUTPUT_FORMAT_YCBCR444 || + output_format == INTEL_OUTPUT_FORMAT_RGB) kslice_adjust = 10; else kslice_adjust = 5; @@ -3315,7 +3317,7 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, else return 0; - slice_width = DIV_ROUND_UP(crtc_state->hw.adjusted_mode.hdisplay, target_slices); + slice_width = DIV_ROUND_UP(mode->hdisplay, target_slices); if (slice_width >= max_slice_width) min_slices = target_slices + 1; } while (slice_width >= max_slice_width); @@ -3331,6 +3333,7 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, * @slice_width: dsc slice width supported by the source and sink * @num_slices: num of slices supported by the source and sink * @output_format: video output format + * @bpc: bits per color * @hdmi_all_bpp: sink supports decoding of 1/16th bpp setting * @hdmi_max_chunk_bytes: max bytes in a line of chunks supported by sink * @@ -3338,8 +3341,8 @@ intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, */ int intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, - int output_format, bool hdmi_all_bpp, - int hdmi_max_chunk_bytes) + enum intel_output_format output_format, int bpc, + bool hdmi_all_bpp, int hdmi_max_chunk_bytes) { int max_dsc_bpp, min_dsc_bpp; int target_bytes; @@ -3356,18 +3359,17 @@ intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, * for each bpp we check if no of bytes can be supported by HDMI sink */ - /* Assuming: bpc as 8*/ if (output_format == INTEL_OUTPUT_FORMAT_YCBCR420) { min_dsc_bpp = 6; - max_dsc_bpp = 3 * 4; /* 3*bpc/2 */ + max_dsc_bpp = 3 * bpc / 2; } else if (output_format == INTEL_OUTPUT_FORMAT_YCBCR444 || output_format == INTEL_OUTPUT_FORMAT_RGB) { min_dsc_bpp = 8; - max_dsc_bpp = 3 * 8; /* 3*bpc */ + max_dsc_bpp = 3 * bpc; } else { /* Assuming 4:2:2 encoding */ min_dsc_bpp = 7; - max_dsc_bpp = 2 * 8; /* 2*bpc */ + max_dsc_bpp = 2 * bpc; } /* @@ -3420,3 +3422,22 @@ intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices, return 0; } + +int intel_hdmi_sink_max_frl_rate(struct drm_connector *connector) +{ + int max_lanes = connector->display_info.hdmi.max_lanes; + int rate_per_lane = connector->display_info.hdmi.max_frl_rate_per_lane; + + return max_lanes * rate_per_lane; +} + +int intel_hdmi_sink_dsc_max_frl_rate(struct drm_connector *connector) +{ + int max_lanes = connector->display_info.hdmi.dsc_cap.max_lanes; + int rate_per_lane = connector->display_info.hdmi.dsc_cap.max_frl_rate_per_lane; + + if (!max_lanes || !rate_per_lane) + return 0; + + return max_lanes * rate_per_lane; +} diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index 0fa3661568e8..c95ed37bcc0c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -13,6 +13,7 @@ enum intel_output_format; enum port; struct drm_connector; struct drm_connector_state; +struct drm_display_mode; struct drm_encoder; struct intel_connector; struct intel_crtc_state; @@ -54,9 +55,10 @@ bool intel_hdmi_bpc_possible(const struct intel_crtc_state *crtc_state, int bpc, bool has_hdmi_sink); int intel_hdmi_tmds_clock(int clock, int bpc, enum intel_output_format sink_format); int intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, - int num_slices, int output_format, bool hdmi_all_bpp, - int hdmi_max_chunk_bytes); -int intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, + int num_slices, enum intel_output_format output_format, + int bpc, bool hdmi_all_bpp, int hdmi_max_chunk_bytes); +int intel_hdmi_dsc_get_num_slices(const struct drm_display_mode *mode, + enum intel_output_format output_format, int src_max_slices, int src_max_slice_width, int hdmi_max_slices, int hdmi_throughput); int intel_hdmi_dsc_get_slice_height(int vactive); @@ -73,4 +75,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, struct drm_connector *_connector); +int intel_hdmi_sink_max_frl_rate(struct drm_connector *connector); +int intel_hdmi_sink_dsc_max_frl_rate(struct drm_connector *connector); + #endif /* __INTEL_HDMI_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_hti.c b/drivers/gpu/drm/i915/display/intel_hti.c index dc454420c134..56602240ceff 100644 --- a/drivers/gpu/drm/i915/display/intel_hti.c +++ b/drivers/gpu/drm/i915/display/intel_hti.c @@ -23,9 +23,6 @@ void intel_hti_init(struct intel_display *display) bool intel_hti_uses_phy(struct intel_display *display, enum phy phy) { - if (drm_WARN_ON(display->drm, phy == PHY_NONE)) - return false; - return display->hti.state & HDPORT_ENABLED && display->hti.state & HDPORT_DDI_USED(phy); } diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c index 2ec17c2bfe0f..f0761d8a7437 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c @@ -245,7 +245,6 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state, verify_connector_state(state, crtc); verify_crtc_state(state, crtc); intel_dpll_state_verify(state, crtc); - intel_mpllb_state_verify(state, crtc); } void intel_modeset_verify_disabled(struct intel_atomic_state *state) diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index a5e41ea66921..99cebb17763a 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -281,9 +281,9 @@ void intel_parent_pc8_unblock(struct intel_display *display) } /* pcode */ -int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val, u32 *val1) +int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val0, u32 *val1) { - return display->parent->pcode->read(display->drm, mbox, val, val1); + return display->parent->pcode->read(display->drm, mbox, val0, val1); } int intel_parent_pcode_write_timeout(struct intel_display *display, u32 mbox, u32 val, int timeout_ms) @@ -451,6 +451,12 @@ bool intel_parent_has_fenced_regions(struct intel_display *display) return display->parent->has_fenced_regions && display->parent->has_fenced_regions(display->drm); } +void intel_parent_transient_data_flush(struct intel_display *display) +{ + if (display->parent->transient_data_flush) + display->parent->transient_data_flush(display->drm); +} + bool intel_parent_vgpu_active(struct intel_display *display) { return display->parent->vgpu_active && display->parent->vgpu_active(display->drm); diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 595d4148b8eb..cc4a58f63166 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -115,7 +115,7 @@ void intel_parent_pc8_block(struct intel_display *display); void intel_parent_pc8_unblock(struct intel_display *display); /* pcode */ -int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val, u32 *val1); +int intel_parent_pcode_read(struct intel_display *display, u32 mbox, u32 *val0, u32 *val1); int intel_parent_pcode_write_timeout(struct intel_display *display, u32 mbox, u32 val, int timeout_ms); int intel_parent_pcode_write(struct intel_display *display, u32 mbox, u32 val); int intel_parent_pcode_request(struct intel_display *display, u32 mbox, u32 request, @@ -155,6 +155,7 @@ int intel_parent_vlv_iosf_write(struct intel_display *display, enum vlv_iosf_sb_ /* generic */ bool intel_parent_has_auxccs(struct intel_display *display); bool intel_parent_has_fenced_regions(struct intel_display *display); +void intel_parent_transient_data_flush(struct intel_display *display); bool intel_parent_vgpu_active(struct intel_display *display); void intel_parent_fence_priority_display(struct intel_display *display, struct dma_fence *fence); diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 40e3d7095996..f490beb66629 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1491,6 +1491,12 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, int crtc_vdisplay = crtc_state->hw.adjusted_mode.crtc_vdisplay; int psr_max_h = 0, psr_max_v = 0, max_bpp = 0; + if (crtc_state->vrr.cmrr.enable) { + drm_dbg_kms(display->drm, + "PSR2 cannot be enabled when CMRR is enabled\n"); + return false; + } + if (!connector->dp.psr_caps.su_support || display->params.enable_psr == 1) return false; @@ -3840,6 +3846,7 @@ void intel_psr_short_pulse(struct intel_dp *intel_dp) struct intel_display *display = to_intel_display(intel_dp); struct intel_psr *psr = &intel_dp->psr; u8 status, error_status; + bool panel_replay_enabled; const u8 errors = DP_PSR_RFB_STORAGE_ERROR | DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR | DP_PSR_LINK_CRC_ERROR; @@ -3860,6 +3867,12 @@ void intel_psr_short_pulse(struct intel_dp *intel_dp) goto exit; } + /* + * Save this before intel_psr_disable_locked() clears it; the error + * status is acknowledged to a different DPCD address depending on it. + */ + panel_replay_enabled = psr->panel_replay_enabled; + if ((!psr->panel_replay_enabled && status == DP_PSR_SINK_INTERNAL_ERROR) || (error_status & errors)) { intel_psr_disable_locked(intel_dp); @@ -3885,7 +3898,10 @@ void intel_psr_short_pulse(struct intel_dp *intel_dp) "PSR_ERROR_STATUS unhandled errors %x\n", error_status & ~errors); /* clear status register */ - drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_ERROR_STATUS, error_status); + drm_dp_dpcd_writeb(&intel_dp->aux, + panel_replay_enabled ? + DP_PANEL_REPLAY_ERROR_STATUS : DP_PSR_ERROR_STATUS, + error_status); if (!psr->panel_replay_enabled) { psr_alpm_check(intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_snps_phy.c b/drivers/gpu/drm/i915/display/intel_snps_phy.c index bf9df566630f..ce94ac689064 100644 --- a/drivers/gpu/drm/i915/display/intel_snps_phy.c +++ b/drivers/gpu/drm/i915/display/intel_snps_phy.c @@ -1816,11 +1816,10 @@ int intel_mpllb_calc_state(struct intel_crtc_state *crtc_state, return -EINVAL; } -void intel_mpllb_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +void intel_mpllb_enable_phy(struct intel_encoder *encoder, + const struct intel_mpllb_state *pll_state) { struct intel_display *display = to_intel_display(encoder); - const struct intel_mpllb_state *pll_state = &crtc_state->dpll_hw_state.mpllb; enum phy phy = intel_encoder_to_phy(encoder); intel_reg_t enable_reg = (phy <= PHY_D ? DG2_PLL_ENABLE(phy) : MG_PLL_ENABLE(0)); @@ -1875,6 +1874,12 @@ void intel_mpllb_enable(struct intel_encoder *encoder, */ } +void intel_mpllb_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + intel_mpllb_enable_phy(encoder, &crtc_state->dpll_hw_state.mpllb); +} + void intel_mpllb_disable(struct intel_encoder *encoder) { struct intel_display *display = to_intel_display(encoder); @@ -1977,52 +1982,3 @@ void intel_mpllb_readout_hw_state(struct intel_encoder *encoder, */ pll_state->mpllb_div &= ~SNPS_PHY_MPLLB_FORCE_EN; } - -void intel_mpllb_state_verify(struct intel_atomic_state *state, - struct intel_crtc *crtc) -{ - struct intel_display *display = to_intel_display(state); - const struct intel_crtc_state *new_crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_mpllb_state mpllb_hw_state = {}; - const struct intel_mpllb_state *mpllb_sw_state = &new_crtc_state->dpll_hw_state.mpllb; - struct intel_encoder *encoder; - - if (!display->platform.dg2) - return; - - if (!new_crtc_state->hw.active) - return; - - /* intel_get_crtc_new_encoder() only works for modeset/fastset commits */ - if (!intel_crtc_needs_modeset(new_crtc_state) && - !intel_crtc_needs_fastset(new_crtc_state)) - return; - - encoder = intel_get_crtc_new_encoder(state, new_crtc_state); - intel_mpllb_readout_hw_state(encoder, &mpllb_hw_state); - -#define MPLLB_CHECK(__name) \ - INTEL_DISPLAY_STATE_WARN(display, mpllb_sw_state->__name != mpllb_hw_state.__name, \ - "[CRTC:%d:%s] mismatch in MPLLB: %s (expected 0x%08x, found 0x%08x)", \ - crtc->base.base.id, crtc->base.name, \ - __stringify(__name), \ - mpllb_sw_state->__name, mpllb_hw_state.__name) - - MPLLB_CHECK(mpllb_cp); - MPLLB_CHECK(mpllb_div); - MPLLB_CHECK(mpllb_div2); - MPLLB_CHECK(mpllb_fracn1); - MPLLB_CHECK(mpllb_fracn2); - MPLLB_CHECK(mpllb_sscen); - MPLLB_CHECK(mpllb_sscstep); - - /* - * ref_control is handled by the hardware/firemware and never - * programmed by the software, but the proper values are supplied - * in the bspec for verification purposes. - */ - MPLLB_CHECK(ref_control); - -#undef MPLLB_CHECK -} diff --git a/drivers/gpu/drm/i915/display/intel_snps_phy.h b/drivers/gpu/drm/i915/display/intel_snps_phy.h index 7f96da22d028..2c7a5f2040f1 100644 --- a/drivers/gpu/drm/i915/display/intel_snps_phy.h +++ b/drivers/gpu/drm/i915/display/intel_snps_phy.h @@ -22,6 +22,8 @@ void intel_snps_phy_update_psr_power_state(struct intel_encoder *encoder, int intel_mpllb_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder); +void intel_mpllb_enable_phy(struct intel_encoder *encoder, + const struct intel_mpllb_state *pll_state); void intel_mpllb_enable(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_mpllb_disable(struct intel_encoder *encoder); diff --git a/drivers/gpu/drm/i915/display/intel_tdf.h b/drivers/gpu/drm/i915/display/intel_tdf.h deleted file mode 100644 index 0862c2bfd9cd..000000000000 --- a/drivers/gpu/drm/i915/display/intel_tdf.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2024 Intel Corporation - */ - -#ifndef __INTEL_TDF_H__ -#define __INTEL_TDF_H__ - -/* - * TDF (Transient-Data-Flush) is needed for Xe2+ where special L3:XD caching can - * be enabled through various PAT index modes. Idea is to use this caching mode - * when for example rendering onto the display surface, with the promise that - * KMD will ensure transient cache entries are always flushed by the time we do - * the display flip, since display engine is never coherent with CPU/GPU caches. - */ - -struct intel_display; - -#ifdef I915 -static inline void intel_td_flush(struct intel_display *display) {} -#else -void intel_td_flush(struct intel_display *display); -#endif - -#endif diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 51e4f3309b8b..e36db1174440 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -4,10 +4,16 @@ * */ +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/string.h> + +#include <drm/drm_atomic.h> #include <drm/drm_print.h> #include <drm/intel/step.h> #include "intel_alpm.h" +#include "intel_atomic.h" #include "intel_cmtg.h" #include "intel_crtc.h" #include "intel_de.h" @@ -23,9 +29,6 @@ #include "skl_prefill.h" #include "skl_watermark.h" -#define FIXED_POINT_PRECISION 100 -#define CMRR_PRECISION_TOLERANCE 10 - /* * Tunable parameters for DC Balance correction. * These are captured based on experimentations. @@ -187,69 +190,87 @@ int intel_vrr_vmax_vblank_start(const struct intel_crtc_state *crtc_state) return intel_vrr_vmax_vtotal(crtc_state) - crtc_state->vrr.guardband; } -static bool -is_cmrr_frac_required(struct intel_crtc_state *crtc_state) +static bool intel_vrr_cmrr_possible(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); - int calculated_refresh_k, actual_refresh_k, pixel_clock_per_line; - struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - - /* Avoid CMRR for now till we have VRR with fixed timings working */ - if (!HAS_CMRR(display) || true) - return false; - - actual_refresh_k = - drm_mode_vrefresh(adjusted_mode) * FIXED_POINT_PRECISION; - pixel_clock_per_line = - adjusted_mode->crtc_clock * 1000 / adjusted_mode->crtc_htotal; - calculated_refresh_k = - pixel_clock_per_line * FIXED_POINT_PRECISION / adjusted_mode->crtc_vtotal; - - if ((actual_refresh_k - calculated_refresh_k) < CMRR_PRECISION_TOLERANCE) - return false; - return true; + return HAS_CMRR(display) && intel_vrr_always_use_vrr_tg(display); } -static unsigned int -cmrr_get_vtotal(struct intel_crtc_state *crtc_state, bool video_mode_required) +static void +intel_vrr_cmrr_compute_config(struct intel_crtc_state *crtc_state) { - int multiplier_m = 1, multiplier_n = 1, vtotal, desired_refresh_rate; - u64 adjusted_pixel_rate; + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + u64 dividend; + u32 mode_rate_mhz; + u32 requested_rate_mhz = crtc->force_cmrr.numerator; + bool video_mode = crtc->force_cmrr.denominator != 1000; + int rr_multiplier = video_mode ? 1000 : 1; + int rr_divider = video_mode ? 1001 : 1; + + if (!intel_vrr_cmrr_possible(crtc_state)) + return; - desired_refresh_rate = drm_mode_vrefresh(adjusted_mode); + /* No CMRR ratio configured through debugfs */ + if (!requested_rate_mhz) + return; - if (video_mode_required) { - multiplier_m = 1001; - multiplier_n = 1000; + /* Requested rate must match the mode's nominal (integer) refresh rate */ + if (DIV_ROUND_CLOSEST(requested_rate_mhz, 1000) != + drm_mode_vrefresh(adjusted_mode)) { + drm_dbg_kms(display->drm, + "[CRTC:%d:%s] CMRR requested %u.%03u Hz doesn't match mode %d Hz\n", + crtc->base.base.id, crtc->base.name, + requested_rate_mhz / 1000, requested_rate_mhz % 1000, + drm_mode_vrefresh(adjusted_mode)); + return; } - crtc_state->cmrr.cmrr_n = mul_u32_u32(desired_refresh_rate * adjusted_mode->crtc_htotal, - multiplier_n); - vtotal = DIV_ROUND_UP_ULL(mul_u32_u32(adjusted_mode->crtc_clock * 1000, multiplier_n), - crtc_state->cmrr.cmrr_n); - adjusted_pixel_rate = mul_u32_u32(adjusted_mode->crtc_clock * 1000, multiplier_m); - crtc_state->cmrr.cmrr_m = do_div(adjusted_pixel_rate, crtc_state->cmrr.cmrr_n); + /* Actual rate produced by the current timings, in milli-Hz */ + mode_rate_mhz = + DIV_ROUND_CLOSEST_ULL((u64)adjusted_mode->crtc_clock * 1000 * 1000, + adjusted_mode->crtc_vtotal * + adjusted_mode->crtc_htotal); - return vtotal; -} + /* + * 1:1 request already satisfied by the mode -> CMRR not needed. + */ + if (!video_mode && DIV_ROUND_CLOSEST(mode_rate_mhz, 10) == + DIV_ROUND_CLOSEST(requested_rate_mhz, 10)) { + drm_dbg_kms(display->drm, + "[CRTC:%d:%s] %u.%03u Hz can be driven without CMRR\n", + crtc->base.base.id, crtc->base.name, + requested_rate_mhz / 1000, requested_rate_mhz % 1000); + return; + } -static -void intel_vrr_compute_cmrr_timings(struct intel_crtc_state *crtc_state) -{ /* - * TODO: Compute precise target refresh rate to determine - * if video_mode_required should be true. Currently set to - * false due to uncertainty about the precise target - * refresh Rate. + * Let pixel_clock_hz = adjusted_mode->crtc_clock * 1000. + * + * cmrr_n = (requested_rate_mhz x htotal x rr_multiplier) / 1000 + * cmrr_m = (pixel_clock_hz x rr_divider) % cmrr_n + * + * where requested_rate_mhz is the requested refresh rate in milli-Hz + * and rr_multiplier/rr_divider = 1000/1001 when the video timing + * is required, else 1/1. The integer vtotal term is tracked in SW + * (it is the programmed mode vtotal) while the fractional part + * represented by cmrr_m/cmrr_n is tracked in HW. + * + * TODO: Using the actual desired rate for cmrr_n in video + * mode produces more aggressive vtotal dithering than + * expected; revisit once the Bspec algorithm is clarified. */ - crtc_state->vrr.vmax = cmrr_get_vtotal(crtc_state, false); - crtc_state->vrr.vmin = crtc_state->vrr.vmax; - crtc_state->vrr.flipline = crtc_state->vrr.vmin; + crtc_state->vrr.cmrr.cmrr_n = + div64_u64((u64)requested_rate_mhz * + adjusted_mode->crtc_htotal * rr_multiplier, 1000); + dividend = (u64)adjusted_mode->crtc_clock * rr_divider * 1000; + adjusted_mode->crtc_vtotal = div64_u64_rem(dividend, + crtc_state->vrr.cmrr.cmrr_n, + &crtc_state->vrr.cmrr.cmrr_m); - crtc_state->cmrr.enable = true; - crtc_state->mode_flags |= I915_MODE_FLAG_VRR; + crtc_state->vrr.cmrr.enable = true; } static @@ -325,6 +346,36 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state) return intel_vrr_fixed_rr_hw_vtotal(crtc_state); } +static void +intel_vrr_set_cmrr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) +{ + struct intel_display *display = to_intel_display(crtc_state); + + if (!intel_vrr_cmrr_possible(crtc_state)) + return; + + intel_de_write(display, TRANS_CMRR_M_HI(display, transcoder), + upper_32_bits(crtc_state->vrr.cmrr.cmrr_m)); + intel_de_write(display, TRANS_CMRR_M_LO(display, transcoder), + lower_32_bits(crtc_state->vrr.cmrr.cmrr_m)); + intel_de_write(display, TRANS_CMRR_N_LO(display, transcoder), + lower_32_bits(crtc_state->vrr.cmrr.cmrr_n)); + intel_de_write(display, TRANS_CMRR_N_HI(display, transcoder), + upper_32_bits(crtc_state->vrr.cmrr.cmrr_n)); + + /* + * On always-VRR-TG platforms the fastset path does not rewrite the + * whole TRANS_VRR_CTL, so RMW only the CMRR enable bit here. On a + * modeset the authoritative writes (intel_vrr_tg_enable(), + * intel_cmtg_set_vrr_ctl()) carry the same bit, so this stays + * consistent. + */ + intel_de_rmw(display, TRANS_VRR_CTL(display, transcoder), + VRR_CTL_CMRR_ENABLE, + crtc_state->vrr.cmrr.enable ? VRR_CTL_CMRR_ENABLE : 0); +} + void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, enum transcoder transcoder) { @@ -333,6 +384,8 @@ void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, if (!intel_vrr_possible(crtc_state)) return; + intel_vrr_set_cmrr_timings(crtc_state, transcoder); + intel_de_write(display, TRANS_VRR_VMIN(display, transcoder), intel_vrr_fixed_rr_hw_vmin(crtc_state) - 1); intel_de_write(display, TRANS_VRR_VMAX(display, transcoder), @@ -425,8 +478,6 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state, struct intel_display *display = to_intel_display(crtc_state); struct intel_connector *connector = to_intel_connector(conn_state->connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - bool is_edp = intel_dp_is_edp(intel_dp); struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; int vmin, vmax; @@ -460,12 +511,17 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state, vmax = vmin; } - if (crtc_state->uapi.vrr_enabled && vmin < vmax) + if (crtc_state->uapi.vrr_enabled && vmin < vmax) { intel_vrr_compute_vrr_timings(crtc_state, vmin, vmax); - else if (is_cmrr_frac_required(crtc_state) && is_edp) - intel_vrr_compute_cmrr_timings(crtc_state); - else + } else { + /* + * CMRR is a fixed average Vtotal mode and is only computed on + * the fixed refresh rate path. It is generic across transcoders + * and gated on platform support and a valid debugfs ratio. + */ + intel_vrr_cmrr_compute_config(crtc_state); intel_vrr_compute_fixed_rr_timings(crtc_state); + } if (HAS_AS_SDP(display)) { crtc_state->vrr.vsync_start = @@ -641,17 +697,6 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) return; } - if (crtc_state->cmrr.enable) { - intel_de_write(display, TRANS_CMRR_M_HI(display, cpu_transcoder), - upper_32_bits(crtc_state->cmrr.cmrr_m)); - intel_de_write(display, TRANS_CMRR_M_LO(display, cpu_transcoder), - lower_32_bits(crtc_state->cmrr.cmrr_m)); - intel_de_write(display, TRANS_CMRR_N_HI(display, cpu_transcoder), - upper_32_bits(crtc_state->cmrr.cmrr_n)); - intel_de_write(display, TRANS_CMRR_N_LO(display, cpu_transcoder), - lower_32_bits(crtc_state->cmrr.cmrr_n)); - } - intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder); intel_cmtg_set_vrr_timings(crtc_state); @@ -917,8 +962,7 @@ intel_vrr_disable_dc_balancing(const struct intel_crtc_state *old_crtc_state) intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl); } -static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state, - bool cmrr_enable) +static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; @@ -930,11 +974,12 @@ static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state, vrr_ctl = VRR_CTL_VRR_ENABLE | trans_vrr_ctl(crtc_state); /* - * FIXME this might be broken as bspec seems to imply that - * even VRR_CTL_CMRR_ENABLE is armed by TRANS_CMRR_N_HI - * when enabling CMRR (but not when disabling CMRR?). + * This full TRANS_VRR_CTL write is the authoritative one, so it must + * carry VRR_CTL_CMRR_ENABLE when CMRR is in use. Writing TRANS_CMRR_N_HI + * arms the bit in hardware, but this later write would otherwise clear + * it again. */ - if (cmrr_enable) + if (crtc_state->vrr.cmrr.enable) vrr_ctl |= VRR_CTL_CMRR_ENABLE; intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl); @@ -970,7 +1015,7 @@ void intel_vrr_enable(const struct intel_crtc_state *crtc_state) intel_vrr_enable_dc_balancing(crtc_state); if (!intel_vrr_always_use_vrr_tg(display)) - intel_vrr_tg_enable(crtc_state, crtc_state->cmrr.enable); + intel_vrr_tg_enable(crtc_state); } void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) @@ -997,7 +1042,7 @@ void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state) return; if (intel_vrr_always_use_vrr_tg(display)) - intel_vrr_tg_enable(crtc_state, false); + intel_vrr_tg_enable(crtc_state); } void intel_vrr_transcoder_disable(const struct intel_crtc_state *old_crtc_state) @@ -1066,13 +1111,13 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) trans_vrr_ctl = intel_de_read(display, TRANS_VRR_CTL(display, cpu_transcoder)); - if (HAS_CMRR(display)) - crtc_state->cmrr.enable = (trans_vrr_ctl & VRR_CTL_CMRR_ENABLE); + if (intel_vrr_cmrr_possible(crtc_state)) + crtc_state->vrr.cmrr.enable = (trans_vrr_ctl & VRR_CTL_CMRR_ENABLE); - if (crtc_state->cmrr.enable) { - crtc_state->cmrr.cmrr_n = + if (crtc_state->vrr.cmrr.enable) { + crtc_state->vrr.cmrr.cmrr_n = intel_de_read64_2x32(display, TRANS_CMRR_N_LO(display, cpu_transcoder)); - crtc_state->cmrr.cmrr_m = + crtc_state->vrr.cmrr.cmrr_m = intel_de_read64_2x32(display, TRANS_CMRR_M_LO(display, cpu_transcoder)); } @@ -1132,11 +1177,6 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) intel_vrr_get_dc_balance_config(crtc_state); - /* - * #TODO: For Both VRR and CMRR the flag I915_MODE_FLAG_VRR is set for mode_flags. - * Since CMRR is currently disabled, set this flag for VRR for now. - * Need to keep this in mind while re-enabling CMRR. - */ if (crtc_state->vrr.enable) crtc_state->mode_flags |= I915_MODE_FLAG_VRR; @@ -1231,3 +1271,186 @@ int intel_vrr_dcb_vmax_vblank_start_final(const struct intel_crtc_state *crtc_st return intel_vrr_vblank_start(crtc_state, VRR_DCB_VMAX(tmp) + 1); } + +static +int intel_vrr_cmrr_parse_ratio(char *str, u32 *numerator, u32 *denominator) +{ + char *sep; + int ret; + + /* + * Parse a "numerator/denominator" CMRR ratio string. The numerator + * is the requested refresh rate in milli-Hz (refresh rate in Hz * 1000) + * and the denominator selects the timing: 1000 for a 1:1 ratio + * (no video timing) or 1001 for the 1000/1001 video timing. + */ + + sep = strchr(str, '/'); + if (!sep) + return -EINVAL; + + *sep = '\0'; + + ret = kstrtou32(strim(str), 10, numerator); + if (ret) + return ret; + + ret = kstrtou32(strim(sep + 1), 10, denominator); + if (ret) + return ret; + /* + * "0/0" clears any previously configured CMRR override. + * A zero numerator already means "CMRR not requested" in + * intel_vrr_cmrr_compute_config(), so just let it through. + */ + if (*numerator == 0 && *denominator == 0) + return 0; + + if (*numerator == 0) + return -EINVAL; + + if (*denominator != 1000 && *denominator != 1001) + return -EINVAL; + + return 0; +} + +static int intel_vrr_debugfs_target_rr_show(struct seq_file *m, void *data) +{ + struct intel_crtc *crtc = m->private; + + seq_printf(m, "%u/%u\n", crtc->force_cmrr.numerator, crtc->force_cmrr.denominator); + + return 0; +} + +static int intel_vrr_debugfs_target_rr_open(struct inode *inode, struct file *file) +{ + return single_open(file, intel_vrr_debugfs_target_rr_show, inode->i_private); +} + +/* + * Force an internal commit on @crtc so that a CMRR ratio programmed + * via debugfs gets recomputed and latched into hardware. + * + * CMRR only alters the (average) vtotal. Depending on whether the + * computed vblank value (with LRR disabled) changes, this commit may or + * may not be downgraded to a fastset. + */ +static int intel_vrr_cmrr_commit_force(struct intel_crtc *crtc) +{ + struct intel_display *display = to_intel_display(crtc); + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_commit *state; + struct intel_crtc_state *crtc_state; + int ret = 0; + + state = drm_atomic_commit_alloc(display->drm); + if (!state) + return -ENOMEM; + + drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); + + state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; + +retry: + crtc_state = intel_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto out; + } + + if (!crtc_state->hw.active) + goto out; + + /* Mark mode as changed to trigger a pipe recompute + update() */ + crtc_state->uapi.mode_changed = true; + + ret = drm_atomic_commit(state); +out: + if (ret == -EDEADLK) { + drm_atomic_commit_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + drm_atomic_commit_put(state); + + return ret; +} + +static ssize_t intel_vrr_debugfs_target_rr_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct intel_crtc *crtc = m->private; + u32 numerator, denominator; + u32 old_numerator, old_denominator; + char kbuf[32]; + int ret; + + if (len >= sizeof(kbuf)) + return -EINVAL; + + if (copy_from_user(kbuf, ubuf, len)) + return -EFAULT; + + kbuf[len] = '\0'; + + ret = intel_vrr_cmrr_parse_ratio(kbuf, &numerator, &denominator); + if (ret) + return ret; + + if (crtc->force_cmrr.numerator == numerator && + crtc->force_cmrr.denominator == denominator) + return len; + + old_numerator = crtc->force_cmrr.numerator; + old_denominator = crtc->force_cmrr.denominator; + + crtc->force_cmrr.numerator = numerator; + crtc->force_cmrr.denominator = denominator; + + /* + * The debugfs value is a side channel that is not tracked by the atomic + * state, so kick an internal commit to recompute and latch the + * new CMRR parameters. + */ + ret = intel_vrr_cmrr_commit_force(crtc); + if (ret) { + /* + * Restore the last known good ratio so that force_cmrr does not + * hold on to bad values, which would make all subsequent commits + * fail. + */ + crtc->force_cmrr.numerator = old_numerator; + crtc->force_cmrr.denominator = old_denominator; + return ret; + } + + return len; +} + +static const struct file_operations intel_vrr_debugfs_target_rr_fops = { + .owner = THIS_MODULE, + .open = intel_vrr_debugfs_target_rr_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = intel_vrr_debugfs_target_rr_write, +}; + +void intel_vrr_crtc_debugfs_add(struct intel_crtc *crtc) +{ + struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); + + if (!intel_vrr_cmrr_possible(crtc_state)) + return; + + debugfs_create_file("intel_vrr_target_refresh_rate", 0600, crtc->base.debugfs_entry, + crtc, &intel_vrr_debugfs_target_rr_fops); +} diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h index 55e9c429f579..19c7990be1b2 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.h +++ b/drivers/gpu/drm/i915/display/intel_vrr.h @@ -56,4 +56,6 @@ int intel_vrr_dcb_vmax_vblank_start_next(const struct intel_crtc_state *crtc_sta int intel_vrr_dcb_vmin_vblank_start_final(const struct intel_crtc_state *crtc_state); int intel_vrr_dcb_vmax_vblank_start_final(const struct intel_crtc_state *crtc_state); +void intel_vrr_crtc_debugfs_add(struct intel_crtc *crtc); + #endif /* __INTEL_VRR_H__ */ diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 9f61867e2478..22dd0179863e 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1706,7 +1706,7 @@ static int mailbox_write(struct intel_vgpu *vgpu, unsigned int offset, { u32 value = *(u32 *)p_data; u32 cmd = value & 0xff; - u32 *data0 = &vgpu_vreg_t(vgpu, GEN6_PCODE_DATA); + u32 *data0 = &vgpu_vreg_t(vgpu, GEN6_PCODE_DATA0); switch (cmd) { case GEN9_PCODE_READ_MEM_LATENCY: diff --git a/drivers/gpu/drm/i915/i915_dpt.c b/drivers/gpu/drm/i915/i915_dpt.c index fcd7cced771d..e01dc4de1788 100644 --- a/drivers/gpu/drm/i915/i915_dpt.c +++ b/drivers/gpu/drm/i915/i915_dpt.c @@ -6,7 +6,6 @@ #include <drm/drm_print.h> #include <drm/intel/display_parent_interface.h> -#include "display/intel_display_core.h" #include "gem/i915_gem_domain.h" #include "gem/i915_gem_internal.h" #include "gem/i915_gem_lmem.h" diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 64e906380131..d873072b7a92 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -697,7 +697,7 @@ #define EDRAM_WAYS_IDX(cap) (((cap) >> 5) & 0x7) #define EDRAM_SETS_IDX(cap) (((cap) >> 8) & 0x3) -#define GEN6_PCODE_DATA _MMIO(0x138128) +#define GEN6_PCODE_DATA0 _MMIO(0x138128) #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 #define GEN6_PCODE_FREQ_RING_RATIO_SHIFT 16 #define GEN6_PCODE_DATA1 _MMIO(0x13812C) diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 86bdeb20f427..c5c4441f3a61 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -57,12 +57,12 @@ static void bxt_init_clock_gating(struct drm_i915_private *i915) */ intel_uncore_rmw(&i915->uncore, GEN8_UCGCTL6, 0, GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ); - intel_display_bxt_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void glk_init_clock_gating(struct drm_i915_private *i915) { - intel_display_glk_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void ilk_init_clock_gating(struct drm_i915_private *i915) @@ -73,7 +73,7 @@ static void ilk_init_clock_gating(struct drm_i915_private *i915) intel_uncore_write(&i915->uncore, PCH_3DCGDIS1, VFMUNIT_CLOCK_GATE_DISABLE); - intel_display_ilk_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); intel_pch_init_clock_gating(i915->display); } @@ -90,7 +90,7 @@ static void gen6_check_mch_setup(struct drm_i915_private *i915) static void gen6_init_clock_gating(struct drm_i915_private *i915) { - intel_display_gen6_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); intel_uncore_write(&i915->uncore, GEN6_UCGCTL1, intel_uncore_read(&i915->uncore, GEN6_UCGCTL1) | @@ -170,7 +170,7 @@ static void cfl_init_clock_gating(struct drm_i915_private *i915) /* WAC6entrylatency:cfl */ intel_uncore_rmw(&i915->uncore, FBC_LLC_READ_CTRL, 0, FBC_LLC_FULLY_OPEN); - intel_display_cfl_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void kbl_init_clock_gating(struct drm_i915_private *i915) @@ -188,7 +188,7 @@ static void kbl_init_clock_gating(struct drm_i915_private *i915) intel_uncore_rmw(&i915->uncore, GEN6_UCGCTL1, 0, GEN6_GAMUNIT_CLOCK_GATE_DISABLE); - intel_display_kbl_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void skl_init_clock_gating(struct drm_i915_private *i915) @@ -200,18 +200,16 @@ static void skl_init_clock_gating(struct drm_i915_private *i915) /* WAC6entrylatency:skl */ intel_uncore_rmw(&i915->uncore, FBC_LLC_READ_CTRL, 0, FBC_LLC_FULLY_OPEN); - intel_display_skl_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void bdw_init_clock_gating(struct drm_i915_private *i915) { - intel_display_bdw_clock_gating_disable_fbcq(i915->display); + intel_display_init_clock_gating(i915->display); /* WaSwitchSolVfFArbitrationPriority:bdw */ intel_uncore_rmw(&i915->uncore, GAM_ECOCHK, 0, HSW_ECOCHK_ARB_PRIO_SOL); - intel_display_bdw_clock_gating_vblank_in_srd(i915->display); - /* WaVSRefCountFullforceMissDisable:bdw */ /* WaDSRefCountFullforceMissDisable:bdw */ intel_uncore_rmw(&i915->uncore, GEN7_FF_THREAD_MODE, @@ -226,8 +224,6 @@ static void bdw_init_clock_gating(struct drm_i915_private *i915) /* WaProgramL3SqcReg1Default:bdw */ gen8_set_l3sqc_credits(i915, 30, 2); - intel_display_bdw_clock_gating_kvm_notif(i915->display); - intel_pch_init_clock_gating(i915->display); /* WaDisableDopClockGating:bdw @@ -240,7 +236,7 @@ static void bdw_init_clock_gating(struct drm_i915_private *i915) static void hsw_init_clock_gating(struct drm_i915_private *i915) { - intel_display_hsw_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); /* This is required by WaCatErrorRejectionIssue:hsw */ intel_uncore_rmw(&i915->uncore, GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, @@ -256,7 +252,7 @@ static void ivb_init_clock_gating(struct drm_i915_private *i915) { struct intel_display *display = i915->display; - intel_display_ivb_init_clock_gating(display); + intel_display_init_clock_gating(display); /* WaDisableBackToBackFlipFix:ivb */ intel_uncore_write(&i915->uncore, IVB_CHICKEN3, @@ -285,8 +281,6 @@ static void ivb_init_clock_gating(struct drm_i915_private *i915) intel_uncore_rmw(&i915->uncore, GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, 0, GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - intel_display_disable_trickle_feed(display); - intel_uncore_rmw(&i915->uncore, GEN6_MBCUNIT_SNPCR, GEN6_MBC_SNPCR_MASK, GEN6_MBC_SNPCR_MED); @@ -362,7 +356,7 @@ static void g4x_init_clock_gating(struct drm_i915_private *i915) GS_UNIT_CLOCK_GATE_DISABLE | CL_UNIT_CLOCK_GATE_DISABLE); intel_uncore_write(&i915->uncore, RAMCLK_GATE_D, 0); - intel_display_g4x_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); } static void i965gm_init_clock_gating(struct drm_i915_private *i915) @@ -371,7 +365,7 @@ static void i965gm_init_clock_gating(struct drm_i915_private *i915) intel_uncore_write(uncore, RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE); intel_uncore_write(uncore, RENCLK_GATE_D2, 0); - intel_display_i965gm_init_clock_gating(i915->display); + intel_display_init_clock_gating(i915->display); intel_uncore_write(uncore, RAMCLK_GATE_D, 0); intel_uncore_write16(uncore, DEUC, 0); intel_uncore_write(uncore, diff --git a/drivers/gpu/drm/i915/intel_gvt_mmio_table.c b/drivers/gpu/drm/i915/intel_gvt_mmio_table.c index de118fae0a49..25b72b20e709 100644 --- a/drivers/gpu/drm/i915/intel_gvt_mmio_table.c +++ b/drivers/gpu/drm/i915/intel_gvt_mmio_table.c @@ -611,7 +611,7 @@ static int iterate_generic_mmio(struct intel_gvt_mmio_table_iter *iter) MMIO_D(GEN6_UCGCTL1); MMIO_D(GEN6_UCGCTL2); MMIO_F(_MMIO(0x4f000), 0x90); - MMIO_D(GEN6_PCODE_DATA); + MMIO_D(GEN6_PCODE_DATA0); MMIO_D(_MMIO(0x13812c)); MMIO_D(GEN7_ERR_INT); MMIO_D(HSW_EDRAM_CAP); diff --git a/drivers/gpu/drm/i915/intel_pcode.c b/drivers/gpu/drm/i915/intel_pcode.c index c07d48fc1b35..1c9d1f413757 100644 --- a/drivers/gpu/drm/i915/intel_pcode.c +++ b/drivers/gpu/drm/i915/intel_pcode.c @@ -58,7 +58,7 @@ static int gen7_check_mailbox_status(u32 mbox) } static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, - u32 *val, u32 *val1, + u32 *val0, u32 *val1, int fast_timeout_us, int slow_timeout_ms, bool is_read) { @@ -73,7 +73,7 @@ static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, if (intel_uncore_read_fw(uncore, GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) return -EAGAIN; - intel_uncore_write_fw(uncore, GEN6_PCODE_DATA, *val); + intel_uncore_write_fw(uncore, GEN6_PCODE_DATA0, *val0); intel_uncore_write_fw(uncore, GEN6_PCODE_DATA1, val1 ? *val1 : 0); intel_uncore_write_fw(uncore, GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox); @@ -87,7 +87,7 @@ static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, return -ETIMEDOUT; if (is_read) - *val = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA); + *val0 = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA0); if (is_read && val1) *val1 = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA1); @@ -97,12 +97,12 @@ static int __snb_pcode_rw(struct intel_uncore *uncore, u32 mbox, return gen6_check_mailbox_status(mbox); } -int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val, u32 *val1) +int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val0, u32 *val1) { int err; mutex_lock(&uncore->i915->sb_lock); - err = __snb_pcode_rw(uncore, mbox, val, val1, 500, 20, true); + err = __snb_pcode_rw(uncore, mbox, val0, val1, 500, 20, true); mutex_unlock(&uncore->i915->sb_lock); if (err) { @@ -278,11 +278,11 @@ int snb_pcode_write_p(struct intel_uncore *uncore, u32 mbcmd, u32 p1, u32 p2, u3 return err; } -static int intel_pcode_read(struct drm_device *drm, u32 mbox, u32 *val, u32 *val1) +static int intel_pcode_read(struct drm_device *drm, u32 mbox, u32 *val0, u32 *val1) { struct drm_i915_private *i915 = to_i915(drm); - return snb_pcode_read(&i915->uncore, mbox, val, val1); + return snb_pcode_read(&i915->uncore, mbox, val0, val1); } static int intel_pcode_write_timeout(struct drm_device *drm, u32 mbox, u32 val, int timeout_ms) diff --git a/drivers/gpu/drm/i915/intel_pcode.h b/drivers/gpu/drm/i915/intel_pcode.h index 19795ea8172e..deecc64bc1b3 100644 --- a/drivers/gpu/drm/i915/intel_pcode.h +++ b/drivers/gpu/drm/i915/intel_pcode.h @@ -11,7 +11,7 @@ struct drm_device; struct intel_uncore; -int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val, u32 *val1); +int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val0, u32 *val1); int snb_pcode_write_timeout(struct intel_uncore *uncore, u32 mbox, u32 val, int timeout_ms); #define snb_pcode_write(uncore, mbox, val) \ snb_pcode_write_timeout((uncore), (mbox), (val), 1) diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index adc2de37e768..67b8b5477639 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -231,8 +231,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ display/xe_hdcp_gsc.o \ display/xe_initial_plane.o \ display/xe_panic.o \ - display/xe_stolen.o \ - display/xe_tdf.o + display/xe_stolen.o # Display code shared with i915 xe-$(CONFIG_DRM_XE_DISPLAY) += \ diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 8da4c457a4a0..7b25c0814674 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -18,7 +18,6 @@ #include <uapi/drm/xe_drm.h> #include "intel_acpi.h" -#include "intel_audio.h" #include "intel_display.h" #include "intel_display_core.h" #include "intel_display_device.h" @@ -29,7 +28,6 @@ #include "intel_dmc_wl.h" #include "intel_dp.h" #include "intel_fbdev.h" -#include "intel_hdcp.h" #include "intel_hotplug.h" #include "intel_opregion.h" #include "skl_watermark.h" @@ -37,6 +35,7 @@ #include "xe_display_bo.h" #include "xe_display_pcode.h" #include "xe_display_rpm.h" +#include "xe_display_wa.h" #include "xe_dsb_buffer.h" #include "xe_fb_pin.h" #include "xe_frontbuffer.h" @@ -129,9 +128,6 @@ static void xe_display_fini(void *arg) struct xe_device *xe = arg; struct intel_display *display = xe->display; - intel_hpd_poll_fini(display); - intel_hdcp_component_fini(display); - intel_audio_deinit(display); intel_display_driver_remove(display); } @@ -446,6 +442,20 @@ static bool has_auxccs(struct drm_device *drm) return xe->info.platform == XE_ALDERLAKE_P; } +/* + * TDF (Transient-Data-Flush) is needed for Xe2+ where special L3:XD caching can + * be enabled through various PAT index modes. Idea is to use this caching mode + * when for example rendering onto the display surface, with the promise that + * KMD will ensure transient cache entries are always flushed by the time we do + * the display flip, since display engine is never coherent with CPU/GPU caches. + */ +static void transient_data_flush(struct drm_device *drm) +{ + struct xe_device *xe = to_xe_device(drm); + + xe_device_td_flush(xe); +} + static const struct intel_display_parent_interface parent = { .bo = &xe_display_bo_interface, .dsb = &xe_display_dsb_interface, @@ -458,7 +468,9 @@ static const struct intel_display_parent_interface parent = { .pcode = &xe_display_pcode_interface, .rpm = &xe_display_rpm_interface, .stolen = &xe_display_stolen_interface, + .wa = &xe_display_wa_interface, .has_auxccs = has_auxccs, + .transient_data_flush = transient_data_flush, }; /** diff --git a/drivers/gpu/drm/xe/display/xe_display_rpm.c b/drivers/gpu/drm/xe/display/xe_display_rpm.c index b3db40035499..548b503aa024 100644 --- a/drivers/gpu/drm/xe/display/xe_display_rpm.c +++ b/drivers/gpu/drm/xe/display/xe_display_rpm.c @@ -3,8 +3,6 @@ #include <drm/intel/display_parent_interface.h> -#include "intel_display_core.h" -#include "intel_display_rpm.h" #include "xe_device.h" #include "xe_device_types.h" #include "xe_pm.h" diff --git a/drivers/gpu/drm/xe/display/xe_display_wa.c b/drivers/gpu/drm/xe/display/xe_display_wa.c index 2aa1b8c03411..46e41e8a3304 100644 --- a/drivers/gpu/drm/xe/display/xe_display_wa.c +++ b/drivers/gpu/drm/xe/display/xe_display_wa.c @@ -3,17 +3,22 @@ * Copyright © 2024 Intel Corporation */ -#include "intel_display_core.h" -#include "intel_display_wa.h" +#include <drm/intel/display_parent_interface.h> + #include "xe_device.h" +#include "xe_display_wa.h" #include "xe_wa.h" #include <generated/xe_wa_oob.h> -bool intel_display_needs_wa_16023588340(struct intel_display *display) +static bool intel_display_needs_wa_16023588340(struct drm_device *drm) { - struct xe_device *xe = to_xe_device(display->drm); + struct xe_device *xe = to_xe_device(drm); struct xe_gt *wa_gt = xe_root_mmio_gt(xe); return wa_gt && XE_GT_WA(wa_gt, 16023588340); } + +const struct intel_display_wa_interface xe_display_wa_interface = { + .wa_16023588340 = intel_display_needs_wa_16023588340, +}; diff --git a/drivers/gpu/drm/xe/display/xe_display_wa.h b/drivers/gpu/drm/xe/display/xe_display_wa.h new file mode 100644 index 000000000000..0262b463916d --- /dev/null +++ b/drivers/gpu/drm/xe/display/xe_display_wa.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __XE_DISPLAY_WA_H__ +#define __XE_DISPLAY_WA_H__ + +extern const struct intel_display_wa_interface xe_display_wa_interface; + +#endif diff --git a/drivers/gpu/drm/xe/display/xe_tdf.c b/drivers/gpu/drm/xe/display/xe_tdf.c deleted file mode 100644 index 78bda4c47874..000000000000 --- a/drivers/gpu/drm/xe/display/xe_tdf.c +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * Copyright © 2024 Intel Corporation - */ - -#include "intel_display_core.h" -#include "intel_tdf.h" -#include "xe_device.h" - -void intel_td_flush(struct intel_display *display) -{ - struct xe_device *xe = to_xe_device(display->drm); - - xe_device_td_flush(xe); -} diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index de395df9ca30..5e44c022d1ae 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -179,7 +179,7 @@ struct intel_display_pc8_interface { }; struct intel_display_pcode_interface { - int (*read)(struct drm_device *drm, u32 mbox, u32 *val, u32 *val1); + int (*read)(struct drm_device *drm, u32 mbox, u32 *val0, u32 *val1); int (*write)(struct drm_device *drm, u32 mbox, u32 val, int timeout_ms); int (*request)(struct drm_device *drm, u32 mbox, u32 request, u32 reply_mask, u32 reply, int timeout_base_ms); @@ -230,6 +230,10 @@ struct intel_display_vlv_iosf_interface { int (*write)(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr, u32 val); }; +struct intel_display_wa_interface { + bool (*wa_16023588340)(struct drm_device *drm); +}; + /** * struct intel_display_parent_interface - services parent driver provides to display * @@ -291,6 +295,12 @@ struct intel_display_parent_interface { /** @vlv_iosf: VLV IOSF sideband. Optional. */ const struct intel_display_vlv_iosf_interface *vlv_iosf; + /** + * @wa: Display workarounds query. Use only for workarounds that require + * information only available to the parent driver. Optional. + */ + const struct intel_display_wa_interface *wa; + /* Generic independent functions */ struct { /** @fence_priority_display: Set display priority. Optional. */ @@ -302,6 +312,9 @@ struct intel_display_parent_interface { /** @has_fenced_regions: Support legacy fencing? Optional. */ bool (*has_fenced_regions)(struct drm_device *drm); + /** @transient_data_flush: Transient data flush. Optional. */ + void (*transient_data_flush)(struct drm_device *drm); + /** @vgpu_active: Is vGPU active? Optional. */ bool (*vgpu_active)(struct drm_device *drm); }; |
