Merge tag 'drm-intel-next-2026-07-02' of https://gitlab.freedesktop.org/drm/i915/kernel into drm-next

drm/i915 feature pull for v7.3:

Features and functionality:
- Novalake (NVL, display versio 35) Common Mode Timing Generator (CMTG) enabling (Animesh)
- NVL DC3CO enabling (Dibin)

Refactoring and cleanups:
- Refactor and document DP link recovery and capability tracking (Imre)
- i915 and xe driver display probe/remove/suspend/resume/shutdown cleanup and unification (Jani)
- Clean up bandwidth/SAGV code (Ville)
- Streamline Pre/Post-CSC LUT loops (Jani)
- Remove unused TGL DC3DO support (Dibin)
- Make read-only array rates static const (Colin Ian King)
- CDCLK sanitization improvements and cleanups (Ville)
- Break i915 and xe panic dependency on struct intel_framebuffer (Jani)
- GPIO pin renames and cleanups (Ville)
- Unify generic irq handler error messages (Jonathan)

Fixes:
- Various color pipeline fixes (Chaitanya)
- Fix step discontinuity in Pre/Post-CSC Gamma LUT (Sean Paul)
- Improve refresh rate changes without full modeset on VRR capable eDP (Ville)
- Display suspend/resume fixes (Imre)
- Fix LPE audio irq for PREEMPT-RT (Maarten)
- Fix HDMI scrambling enable (Jerome Tollet)
- LNL bandwidth buddy programming update (Vinod)
- Streamline display register wait timeouts (Ville)
- Fix DP MSA VTotal (Mitul)
- Fix LTPHY SSC enabling (Suraj)
- Fix LOBF requirements with optimized guardband (Ankit)
- Avoid full modeset for LRR vsync changes (Ankit)
- Fix vtotal-vsync distance when adjusting vtotal for lower refresh rate (Ankit)
- Extend VRR safe window wait for default VRR timing generator usage (Ankit)

Merges:
- Backmerge for v7.2-rc1 (Jani)

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Jani Nikula <jani.nikula@intel.com>
Link: https://patch.msgid.link/934cacdbf2a37856eb5cd6337d8774b449405bd8@intel.com
This commit is contained in:
Dave Airlie
2026-07-07 11:15:46 +10:00
91 changed files with 3696 additions and 1995 deletions

View File

@@ -0,0 +1,8 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
DisplayPort Link Training
=========================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_training.c
:doc: DisplayPort link training

View File

@@ -38,6 +38,7 @@ driver. The display driver isn't an independent driver in that sense.
fifo-underrun
frontbuffer
hotplug
dp-link-training
plane
psr
snps-phy

View File

@@ -1469,6 +1469,25 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_mode_duplicate);
static bool drm_mode_match_timings_vrr(const struct drm_display_mode *mode1,
const struct drm_display_mode *mode2)
{
int mode1_vsync_start_offset = mode1->vtotal - mode1->vsync_start;
int mode1_vsync_end_offset = mode1->vtotal - mode1->vsync_end;
int mode2_vsync_start_offset = mode2->vtotal - mode2->vsync_start;
int mode2_vsync_end_offset = mode2->vtotal - mode2->vsync_end;
return mode1->hdisplay == mode2->hdisplay &&
mode1->hsync_start == mode2->hsync_start &&
mode1->hsync_end == mode2->hsync_end &&
mode1->htotal == mode2->htotal &&
mode1->hskew == mode2->hskew &&
mode1->vdisplay == mode2->vdisplay &&
mode1_vsync_start_offset == mode2_vsync_start_offset &&
mode1_vsync_end_offset == mode2_vsync_end_offset &&
mode1->vscan == mode2->vscan;
}
static bool drm_mode_match_timings(const struct drm_display_mode *mode1,
const struct drm_display_mode *mode2)
{
@@ -1538,6 +1557,10 @@ bool drm_mode_match(const struct drm_display_mode *mode1,
if (!mode1 || !mode2)
return false;
if (match_flags & DRM_MODE_MATCH_TIMINGS_VRR &&
!drm_mode_match_timings_vrr(mode1, mode2))
return false;
if (match_flags & DRM_MODE_MATCH_TIMINGS &&
!drm_mode_match_timings(mode1, mode2))
return false;

View File

@@ -83,8 +83,7 @@ i915-y += \
i915_fb_pin.o \
i915_hdcp_gsc.o \
i915_initial_plane.o \
i915_overlay.o \
i915_panic.o
i915_overlay.o
# "Graphics Technology" (aka we talk to the gpu)
gt-y += \
@@ -167,6 +166,7 @@ gem-y += \
gem/i915_gem_object.o \
gem/i915_gem_object_frontbuffer.o \
gem/i915_gem_pages.o \
gem/i915_gem_panic.o \
gem/i915_gem_phys.o \
gem/i915_gem_pm.o \
gem/i915_gem_region.o \
@@ -356,6 +356,7 @@ i915-y += \
display/intel_dp_aux.o \
display/intel_dp_aux_backlight.o \
display/intel_dp_hdcp.o \
display/intel_dp_link_caps.o \
display/intel_dp_link_training.o \
display/intel_dp_mst.o \
display/intel_dp_test.o \

View File

@@ -1222,7 +1222,8 @@ static bool ilk_digital_port_connected(struct intel_encoder *encoder)
return intel_de_read(display, DEISR) & bit;
}
static int g4x_dp_compute_config(struct intel_encoder *encoder,
static int g4x_dp_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -1232,7 +1233,7 @@ static int g4x_dp_compute_config(struct intel_encoder *encoder,
if (HAS_PCH_SPLIT(display) && encoder->port != PORT_A)
crtc_state->has_pch_encoder = true;
ret = intel_dp_compute_config(encoder, crtc_state, conn_state);
ret = intel_dp_compute_config(state, encoder, crtc_state, conn_state);
if (ret)
return ret;
@@ -1252,10 +1253,13 @@ static void g4x_dp_suspend_complete(struct intel_encoder *encoder)
static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
{
struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder));
intel_dp_encoder_flush_work(encoder);
drm_encoder_cleanup(encoder);
kfree(enc_to_dig_port(to_intel_encoder(encoder)));
intel_dp_link_cleanup(&dig_port->dp);
kfree(dig_port);
}
static void intel_dp_encoder_reset(struct drm_encoder *encoder)
@@ -1350,6 +1354,9 @@ bool g4x_dp_init(struct intel_display *display,
intel_encoder->audio_enable = g4x_dp_audio_enable;
intel_encoder->audio_disable = g4x_dp_audio_disable;
if (intel_dp_link_init(&dig_port->dp) != 0)
goto err_dp_init;
if ((display->platform.ivybridge && port == PORT_A) ||
(HAS_PCH_CPT(display) && port != PORT_A)) {
dig_port->dp.set_link_train = cpt_set_link_train;
@@ -1419,6 +1426,8 @@ bool g4x_dp_init(struct intel_display *display,
return true;
err_init_connector:
intel_dp_link_cleanup(&dig_port->dp);
err_dp_init:
drm_encoder_cleanup(encoder);
err_encoder_init:
kfree(intel_connector);

View File

@@ -126,12 +126,12 @@ static bool g4x_compute_has_hdmi_sink(struct intel_atomic_state *state,
return false;
}
static int g4x_hdmi_compute_config(struct intel_encoder *encoder,
static int g4x_hdmi_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
if (HAS_PCH_SPLIT(display))

View File

@@ -1657,7 +1657,8 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder,
return 0;
}
static int gen11_dsi_compute_config(struct intel_encoder *encoder,
static int gen11_dsi_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -1671,7 +1672,7 @@ static int gen11_dsi_compute_config(struct intel_encoder *encoder,
pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
ret = intel_panel_compute_config(intel_connector, adjusted_mode);
ret = intel_panel_compute_config(state, pipe_config, intel_connector);
if (ret)
return ret;

View File

@@ -291,7 +291,9 @@ void intel_alpm_lobf_compute_config_late(struct intel_dp *intel_dp,
if (!crtc_state->has_lobf)
return;
if (!intel_alpm_lobf_is_window1_sufficient(crtc_state)) {
if (crtc_state->has_psr ||
!intel_vrr_is_fixed_rr(crtc_state) ||
!intel_alpm_lobf_is_window1_sufficient(crtc_state)) {
crtc_state->has_lobf = false;
return;
}
@@ -343,11 +345,7 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
if (!intel_dp->as_sdp_supported)
return;
if (crtc_state->has_psr)
return;
if (!intel_vrr_always_use_vrr_tg(display) ||
!intel_vrr_is_fixed_rr(crtc_state))
if (!intel_vrr_always_use_vrr_tg(display))
return;
if (!(intel_alpm_aux_wake_supported(intel_dp) ||
@@ -407,6 +405,11 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
if (crtc_state->disable_as_sdp_when_pr_active)
pr_alpm_ctl |= PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE;
if (intel_display_power_dc3co_allowed(display))
pr_alpm_ctl |= PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL;
else
pr_alpm_ctl &= ~PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL;
intel_de_write(display, PR_ALPM_CTL(display, cpu_transcoder),
pr_alpm_ctl);
}

View File

@@ -2217,52 +2217,52 @@ static u8 translate_iboost(struct intel_display *display, u8 val)
static const u8 cnp_ddc_pin_map[] = {
[0] = 0, /* N/A */
[GMBUS_PIN_1_BXT] = DDC_BUS_DDI_B,
[GMBUS_PIN_2_BXT] = DDC_BUS_DDI_C,
[GMBUS_PIN_4_CNP] = DDC_BUS_DDI_D, /* sic */
[GMBUS_PIN_3_BXT] = DDC_BUS_DDI_F, /* sic */
[GMBUS_PIN_1] = DDC_BUS_DDI_B,
[GMBUS_PIN_2] = DDC_BUS_DDI_C,
[GMBUS_PIN_4] = DDC_BUS_DDI_D, /* sic */
[GMBUS_PIN_3] = DDC_BUS_DDI_F, /* sic */
};
static const u8 icp_ddc_pin_map[] = {
[GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B,
[GMBUS_PIN_3_BXT] = TGL_DDC_BUS_DDI_C,
[GMBUS_PIN_9_TC1_ICP] = ICL_DDC_BUS_PORT_1,
[GMBUS_PIN_10_TC2_ICP] = ICL_DDC_BUS_PORT_2,
[GMBUS_PIN_11_TC3_ICP] = ICL_DDC_BUS_PORT_3,
[GMBUS_PIN_12_TC4_ICP] = ICL_DDC_BUS_PORT_4,
[GMBUS_PIN_13_TC5_TGP] = TGL_DDC_BUS_PORT_5,
[GMBUS_PIN_14_TC6_TGP] = TGL_DDC_BUS_PORT_6,
[GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B,
[GMBUS_PIN_3] = TGL_DDC_BUS_DDI_C,
[GMBUS_PIN_9_TC1] = ICL_DDC_BUS_PORT_1,
[GMBUS_PIN_10_TC2] = ICL_DDC_BUS_PORT_2,
[GMBUS_PIN_11_TC3] = ICL_DDC_BUS_PORT_3,
[GMBUS_PIN_12_TC4] = ICL_DDC_BUS_PORT_4,
[GMBUS_PIN_13_TC5] = TGL_DDC_BUS_PORT_5,
[GMBUS_PIN_14_TC6] = TGL_DDC_BUS_PORT_6,
};
static const u8 rkl_pch_tgp_ddc_pin_map[] = {
[GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B,
[GMBUS_PIN_9_TC1_ICP] = RKL_DDC_BUS_DDI_D,
[GMBUS_PIN_10_TC2_ICP] = RKL_DDC_BUS_DDI_E,
[GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B,
[GMBUS_PIN_9_TC1] = RKL_DDC_BUS_DDI_D,
[GMBUS_PIN_10_TC2] = RKL_DDC_BUS_DDI_E,
};
static const u8 adls_ddc_pin_map[] = {
[GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_9_TC1_ICP] = ADLS_DDC_BUS_PORT_TC1,
[GMBUS_PIN_10_TC2_ICP] = ADLS_DDC_BUS_PORT_TC2,
[GMBUS_PIN_11_TC3_ICP] = ADLS_DDC_BUS_PORT_TC3,
[GMBUS_PIN_12_TC4_ICP] = ADLS_DDC_BUS_PORT_TC4,
[GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_9_TC1] = ADLS_DDC_BUS_PORT_TC1,
[GMBUS_PIN_10_TC2] = ADLS_DDC_BUS_PORT_TC2,
[GMBUS_PIN_11_TC3] = ADLS_DDC_BUS_PORT_TC3,
[GMBUS_PIN_12_TC4] = ADLS_DDC_BUS_PORT_TC4,
};
static const u8 gen9bc_tgp_ddc_pin_map[] = {
[GMBUS_PIN_2_BXT] = DDC_BUS_DDI_B,
[GMBUS_PIN_9_TC1_ICP] = DDC_BUS_DDI_C,
[GMBUS_PIN_10_TC2_ICP] = DDC_BUS_DDI_D,
[GMBUS_PIN_2] = DDC_BUS_DDI_B,
[GMBUS_PIN_9_TC1] = DDC_BUS_DDI_C,
[GMBUS_PIN_10_TC2] = DDC_BUS_DDI_D,
};
static const u8 adlp_ddc_pin_map[] = {
[GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B,
[GMBUS_PIN_9_TC1_ICP] = ADLP_DDC_BUS_PORT_TC1,
[GMBUS_PIN_10_TC2_ICP] = ADLP_DDC_BUS_PORT_TC2,
[GMBUS_PIN_11_TC3_ICP] = ADLP_DDC_BUS_PORT_TC3,
[GMBUS_PIN_12_TC4_ICP] = ADLP_DDC_BUS_PORT_TC4,
[GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A,
[GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B,
[GMBUS_PIN_9_TC1] = ADLP_DDC_BUS_PORT_TC1,
[GMBUS_PIN_10_TC2] = ADLP_DDC_BUS_PORT_TC2,
[GMBUS_PIN_11_TC3] = ADLP_DDC_BUS_PORT_TC3,
[GMBUS_PIN_12_TC4] = ADLP_DDC_BUS_PORT_TC4,
};
static u8 map_ddc_pin(struct intel_display *display, u8 vbt_pin)

View File

@@ -59,7 +59,7 @@ struct intel_psf_gv_point {
struct intel_qgv_info {
struct intel_qgv_point points[I915_NUM_QGV_POINTS];
struct intel_psf_gv_point psf_points[I915_NUM_PSF_GV_POINTS];
u8 num_points;
u8 num_qgv_points;
u8 num_psf_points;
u8 t_bl;
u8 max_numchannels;
@@ -155,8 +155,8 @@ static int adls_pcode_read_psf_gv_point_info(struct intel_display *display,
static u16 icl_qgv_points_mask(struct intel_display *display)
{
unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points;
unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
unsigned int num_psf_gv_points = display->bw.num_psf_gv_points;
unsigned int num_qgv_points = display->bw.num_qgv_points;
u16 qgv_points = 0, psf_points = 0;
/*
@@ -252,7 +252,7 @@ static int icl_get_qgv_points(struct intel_display *display,
{
int i, ret;
qi->num_points = dram_info->num_qgv_points;
qi->num_qgv_points = dram_info->num_qgv_points;
qi->num_psf_points = dram_info->num_psf_gv_points;
if (DISPLAY_VER(display) >= 14) {
@@ -324,10 +324,10 @@ static int icl_get_qgv_points(struct intel_display *display,
}
if (drm_WARN_ON(display->drm,
qi->num_points > ARRAY_SIZE(qi->points)))
qi->num_points = ARRAY_SIZE(qi->points);
qi->num_qgv_points > ARRAY_SIZE(qi->points)))
qi->num_qgv_points = ARRAY_SIZE(qi->points);
for (i = 0; i < qi->num_points; i++) {
for (i = 0; i < qi->num_qgv_points; i++) {
struct intel_qgv_point *sp = &qi->points[i];
ret = intel_read_qgv_point_info(display, sp, i);
@@ -373,7 +373,7 @@ static int icl_sagv_max_dclk(const struct intel_qgv_info *qi)
u16 dclk = 0;
int i;
for (i = 0; i < qi->num_points; i++)
for (i = 0; i < qi->num_qgv_points; i++)
dclk = max(dclk, qi->points[i].dclk);
return dclk;
@@ -536,6 +536,9 @@ static int icl_get_bw_info(struct intel_display *display,
ipqdepth = min(ipqdepthpch, display_bw_params->displayrtids / num_channels);
qi.deinterleave = DIV_ROUND_UP(num_channels, is_y_tile(display) ? 4 : 2);
display->bw.num_qgv_points = qi.num_qgv_points;
display->bw.num_psf_gv_points = qi.num_psf_points;
for (i = 0; i < num_groups; i++) {
struct intel_bw_info *bi = &display->bw.max[i];
int clpchgroup;
@@ -544,10 +547,7 @@ static int icl_get_bw_info(struct intel_display *display,
clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i;
bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1;
bi->num_qgv_points = qi.num_points;
bi->num_psf_gv_points = qi.num_psf_points;
for (j = 0; j < qi.num_points; j++) {
for (j = 0; j < qi.num_qgv_points; j++) {
const struct intel_qgv_point *sp = &qi.points[j];
int ct, bw;
@@ -574,7 +574,7 @@ static int icl_get_bw_info(struct intel_display *display,
* SAGV point, but we can't send PCode commands to restrict it
* as it will fail and pointless anyway.
*/
if (qi.num_points == 1)
if (qi.num_qgv_points == 1)
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
else
display->sagv.status = I915_SAGV_ENABLED;
@@ -629,16 +629,20 @@ static int tgl_get_bw_info(struct intel_display *display,
*/
clperchgroup = 4 * (8 / num_channels) * qi.deinterleave;
display->bw.num_qgv_points = qi.num_qgv_points;
display->bw.num_psf_gv_points = qi.num_psf_points;
display->bw.max[0].num_planes = U8_MAX;
for (i = 0; i < num_groups; i++) {
struct intel_bw_info *bi = &display->bw.max[i];
struct intel_bw_info *bi_next;
int clpchgroup;
int j;
clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i;
if (i < num_groups - 1) {
bi_next = &display->bw.max[i + 1];
struct intel_bw_info *bi_next = &display->bw.max[i + 1];
if (clpchgroup < clperchgroup)
bi_next->num_planes = (ipqdepth - clpchgroup) / clpchgroup;
@@ -646,10 +650,7 @@ static int tgl_get_bw_info(struct intel_display *display,
bi_next->num_planes = 0;
}
bi->num_qgv_points = qi.num_points;
bi->num_psf_gv_points = qi.num_psf_points;
for (j = 0; j < qi.num_points; j++) {
for (j = 0; j < qi.num_qgv_points; j++) {
const struct intel_qgv_point *sp = &qi.points[j];
int ct, bw;
@@ -665,23 +666,27 @@ static int tgl_get_bw_info(struct intel_display *display,
bi->deratedbw[j] = min(maxdebw,
bw * (100 - soc_bw_params->derating) / 100);
bi->peakbw[j] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk);
drm_dbg_kms(display->drm,
"BW%d / QGV %d: num_planes=%d deratedbw=%u peakbw: %u\n",
i, j, bi->num_planes, bi->deratedbw[j],
bi->peakbw[j]);
"BW%d / QGV %d: num_planes=%d deratedbw=%u\n",
i, j, bi->num_planes, bi->deratedbw[j]);
}
}
for (j = 0; j < qi.num_psf_points; j++) {
const struct intel_psf_gv_point *sp = &qi.psf_points[j];
for (i = 0; i < qi.num_qgv_points; i++) {
const struct intel_qgv_point *sp = &qi.points[i];
bi->psf_bw[j] = adl_calc_psf_bw(sp->clk);
display->bw.peakbw[i] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk);
drm_dbg_kms(display->drm,
"BW%d / PSF GV %d: num_planes=%d bw=%u\n",
i, j, bi->num_planes, bi->psf_bw[j]);
}
drm_dbg_kms(display->drm, "QGV %d: peakbw=%u\n", i, display->bw.peakbw[i]);
}
for (i = 0; i < qi.num_psf_points; i++) {
const struct intel_psf_gv_point *sp = &qi.psf_points[i];
display->bw.psf_bw[i] = adl_calc_psf_bw(sp->clk);
drm_dbg_kms(display->drm, "PSF GV %d: bw=%u\n", i, display->bw.psf_bw[i]);
}
/*
@@ -689,7 +694,7 @@ static int tgl_get_bw_info(struct intel_display *display,
* SAGV point, but we can't send PCode commands to restrict it
* as it will fail and pointless anyway.
*/
if (qi.num_points == 1)
if (qi.num_qgv_points == 1)
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
else
display->sagv.status = I915_SAGV_ENABLED;
@@ -699,25 +704,20 @@ static int tgl_get_bw_info(struct intel_display *display,
static void dg2_get_bw_info(struct intel_display *display)
{
unsigned int deratedbw = display->platform.dg2_g11 ? 38000 : 50000;
int num_groups = ARRAY_SIZE(display->bw.max);
int i;
/*
* DG2 doesn't have SAGV or QGV points, just a constant max bandwidth
* that doesn't depend on the number of planes enabled. So fill all the
* plane group with constant bw information for uniformity with other
* platforms. DG2-G10 platforms have a constant 50 GB/s bandwidth,
* whereas DG2-G11 platforms have 38 GB/s.
*/
for (i = 0; i < num_groups; i++) {
struct intel_bw_info *bi = &display->bw.max[i];
display->bw.num_qgv_points = 1;
bi->num_planes = 1;
/* Need only one dummy QGV point per group */
bi->num_qgv_points = 1;
bi->deratedbw[0] = deratedbw;
}
display->bw.max[0].num_planes = U8_MAX;
display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000;
drm_dbg_kms(display->drm,
"QGV 0: deratedbw=%u\n",
display->bw.max[0].deratedbw[0]);
/* Bandwidth does not depend on # of planes; set all groups the same */
for (i = 1; i < ARRAY_SIZE(display->bw.max); i++)
display->bw.max[i] = display->bw.max[0];
display->sagv.status = I915_SAGV_NOT_CONTROLLED;
}
@@ -741,31 +741,32 @@ static int xe2_hpd_get_bw_info(struct intel_display *display,
peakbw = tgl_peakbw(num_channels, qi.channel_width, icl_sagv_max_dclk(&qi));
maxdebw = min(soc_bw_params->deprogbwlimit * 1000, peakbw * DEPROGBWPCLIMIT / 100);
for (i = 0; i < qi.num_points; i++) {
display->bw.num_qgv_points = qi.num_qgv_points;
display->bw.max[0].num_planes = U8_MAX;
for (i = 0; i < qi.num_qgv_points; i++) {
const struct intel_qgv_point *sp = &qi.points[i];
int bw = tgl_peakbw(num_channels, qi.channel_width, sp->dclk);
display->bw.max[0].deratedbw[i] =
min(maxdebw, (100 - soc_bw_params->derating) * bw / 100);
display->bw.max[0].peakbw[i] = bw;
drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw: %u\n",
i, display->bw.max[0].deratedbw[i],
display->bw.max[0].peakbw[i]);
display->bw.peakbw[i] = bw;
drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw=%u\n",
i, display->bw.max[0].deratedbw[i], display->bw.peakbw[i]);
}
/* Bandwidth does not depend on # of planes; set all groups the same */
display->bw.max[0].num_planes = 1;
display->bw.max[0].num_qgv_points = qi.num_points;
for (i = 1; i < ARRAY_SIZE(display->bw.max); i++)
memcpy(&display->bw.max[i], &display->bw.max[0],
sizeof(display->bw.max[0]));
display->bw.max[i] = display->bw.max[0];
/*
* Xe2_HPD should always have exactly two QGV points representing
* battery and plugged-in operation.
*/
drm_WARN_ON(display->drm, qi.num_points != 2);
drm_WARN_ON(display->drm, qi.num_qgv_points != 2);
display->sagv.status = I915_SAGV_ENABLED;
return 0;
@@ -776,6 +777,9 @@ static unsigned int icl_max_bw_index(struct intel_display *display,
{
int i;
if (qgv_point >= display->bw.num_qgv_points)
return UINT_MAX;
/*
* Let's return max bw for 0 planes
*/
@@ -785,13 +789,6 @@ static unsigned int icl_max_bw_index(struct intel_display *display,
const struct intel_bw_info *bi =
&display->bw.max[i];
/*
* Pcode will not expose all QGV points when
* SAGV is forced to off/min/med/max.
*/
if (qgv_point >= bi->num_qgv_points)
return UINT_MAX;
if (num_planes >= bi->num_planes)
return i;
}
@@ -804,31 +801,24 @@ static unsigned int tgl_max_bw_index(struct intel_display *display,
{
int i;
if (qgv_point >= display->bw.num_qgv_points)
return UINT_MAX;
for (i = ARRAY_SIZE(display->bw.max) - 1; i >= 0; i--) {
const struct intel_bw_info *bi =
&display->bw.max[i];
/*
* Pcode will not expose all QGV points when
* SAGV is forced to off/min/med/max.
*/
if (qgv_point >= bi->num_qgv_points)
return UINT_MAX;
if (num_planes <= bi->num_planes)
return i;
}
return 0;
return UINT_MAX;
}
static unsigned int adl_psf_bw(struct intel_display *display,
int psf_gv_point)
{
const struct intel_bw_info *bi =
&display->bw.max[0];
return bi->psf_bw[psf_gv_point];
return display->bw.psf_bw[psf_gv_point];
}
static unsigned int icl_qgv_bw(struct intel_display *display,
@@ -950,7 +940,7 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state)
static unsigned int icl_max_bw_qgv_point_mask(struct intel_display *display,
int num_active_planes)
{
unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
unsigned int num_qgv_points = display->bw.num_qgv_points;
unsigned int max_bw_point = 0;
unsigned int max_bw = 0;
int i;
@@ -986,7 +976,7 @@ static u16 icl_prepare_qgv_points_mask(struct intel_display *display,
static unsigned int icl_max_bw_psf_gv_point_mask(struct intel_display *display)
{
unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points;
unsigned int num_psf_gv_points = display->bw.num_psf_gv_points;
unsigned int max_bw_point_mask = 0;
unsigned int max_bw = 0;
int i;
@@ -1091,7 +1081,7 @@ static int mtl_find_qgv_points(struct intel_display *display,
struct intel_bw_state *new_bw_state)
{
unsigned int best_rate = UINT_MAX;
unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
unsigned int num_qgv_points = display->bw.num_qgv_points;
unsigned int qgv_peak_bw = 0;
int i;
int ret;
@@ -1116,21 +1106,15 @@ static int mtl_find_qgv_points(struct intel_display *display,
* offered per plane group
*/
for (i = 0; i < num_qgv_points; i++) {
unsigned int bw_index =
tgl_max_bw_index(display, num_active_planes, i);
unsigned int max_data_rate;
if (bw_index >= ARRAY_SIZE(display->bw.max))
continue;
max_data_rate = display->bw.max[bw_index].deratedbw[i];
unsigned int max_data_rate =
icl_qgv_bw(display, num_active_planes, i);
if (max_data_rate < data_rate)
continue;
if (max_data_rate - data_rate < best_rate) {
best_rate = max_data_rate - data_rate;
qgv_peak_bw = display->bw.max[bw_index].peakbw[i];
if (max_data_rate < best_rate) {
best_rate = max_data_rate;
qgv_peak_bw = display->bw.peakbw[i];
}
drm_dbg_kms(display->drm, "QGV point %d: max bw %d required %d qgv_peak_bw: %d\n",
@@ -1162,8 +1146,8 @@ static int icl_find_qgv_points(struct intel_display *display,
const struct intel_bw_state *old_bw_state,
struct intel_bw_state *new_bw_state)
{
unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points;
unsigned int num_qgv_points = display->bw.max[0].num_qgv_points;
unsigned int num_psf_gv_points = display->bw.num_psf_gv_points;
unsigned int num_qgv_points = display->bw.num_qgv_points;
u16 psf_points = 0;
u16 qgv_points = 0;
int i;

View File

@@ -1229,23 +1229,28 @@ static void skl_set_cdclk(struct intel_display *display,
static void skl_sanitize_cdclk(struct intel_display *display)
{
u32 cdctl, expected;
u32 cdctl, expected, swf18;
/*
* check if the pre-os initialized the display
* There is SWF18 scratchpad register defined which is set by the
* pre-os which can be used by the OS drivers to check the status
*/
if ((intel_de_read(display, SWF_ILK(0x18)) & 0x00FFFFFF) == 0)
swf18 = intel_de_read(display, SWF_ILK(0x18));
if ((swf18 & 0x00FFFFFF) == 0) {
drm_dbg_kms(display->drm, "Sanitizing CDCLK due to SWF18 0x%x\n", swf18);
goto sanitize;
}
intel_update_cdclk(display);
intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK");
/* Is PLL enabled and locked ? */
if (display->cdclk.hw.vco == 0 ||
display->cdclk.hw.cdclk == display->cdclk.hw.bypass)
display->cdclk.hw.cdclk == display->cdclk.hw.bypass) {
drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n");
goto sanitize;
}
/* DPLL okay; verify the cdclock
*
@@ -1261,8 +1266,11 @@ static void skl_sanitize_cdclk(struct intel_display *display)
cdctl &= ~CDCLK_FREQ_DECIMAL_MASK;
cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK;
if (cdctl != expected)
if (cdctl != expected) {
drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x)\n",
intel_de_read(display, CDCLK_CTL), expected);
goto sanitize;
}
drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n",
intel_de_read(display, CDCLK_CTL), expected);
@@ -1274,8 +1282,6 @@ static void skl_sanitize_cdclk(struct intel_display *display)
return;
sanitize:
drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n");
/* force cdclk programming */
display->cdclk.hw.cdclk = 0;
/* force full PLL disable + enable */
@@ -1938,14 +1944,17 @@ static void adlp_cdclk_pll_crawl(struct intel_display *display, int vco)
display->cdclk.hw.vco = vco;
}
static u32 bxt_cdclk_cd2x_pipe_mask(struct intel_display *display)
{
if (DISPLAY_VER(display) >= 11)
return ICL_CDCLK_CD2X_PIPE_MASK;
else
return BXT_CDCLK_CD2X_PIPE_MASK;
}
static u32 bxt_cdclk_cd2x_pipe(struct intel_display *display, enum pipe pipe)
{
if (DISPLAY_VER(display) >= 12) {
if (pipe == INVALID_PIPE)
return TGL_CDCLK_CD2X_PIPE_NONE;
else
return TGL_CDCLK_CD2X_PIPE(pipe);
} else if (DISPLAY_VER(display) >= 11) {
if (DISPLAY_VER(display) >= 11) {
if (pipe == INVALID_PIPE)
return ICL_CDCLK_CD2X_PIPE_NONE;
else
@@ -2340,18 +2349,24 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK");
if (display->cdclk.hw.vco == 0 ||
display->cdclk.hw.cdclk == display->cdclk.hw.bypass)
display->cdclk.hw.cdclk == display->cdclk.hw.bypass) {
drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n");
goto sanitize;
}
/* Make sure this is a legal cdclk value for the platform */
cdclk = bxt_calc_cdclk(display, display->cdclk.hw.cdclk);
if (cdclk != display->cdclk.hw.cdclk)
if (cdclk != display->cdclk.hw.cdclk) {
drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad CDCLK frequency\n");
goto sanitize;
}
/* Make sure the VCO is correct for the cdclk */
vco = bxt_calc_cdclk_pll_vco(display, cdclk);
if (vco != display->cdclk.hw.vco)
if (vco != display->cdclk.hw.vco) {
drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad VCO frequency\n");
goto sanitize;
}
/*
* Some BIOS versions leave an incorrect decimal frequency value and
@@ -2366,7 +2381,7 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
* dividers both syncing to an active pipe, or asynchronously
* (PIPE_NONE).
*/
cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display);
cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
if (cdctl != expected) {
@@ -2375,8 +2390,11 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK;
}
if (cdctl != expected)
if (cdctl != expected) {
drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x\n",
intel_de_read(display, CDCLK_CTL), expected);
goto sanitize;
}
drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n",
intel_de_read(display, CDCLK_CTL), expected);
@@ -2388,8 +2406,6 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
return;
sanitize:
drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n");
/* force cdclk programming */
display->cdclk.hw.cdclk = 0;

View File

@@ -4,7 +4,6 @@
*/
#include <linux/string_choices.h>
#include <linux/types.h>
#include <drm/drm_device.h>
#include <drm/drm_print.h>
@@ -13,9 +12,14 @@
#include "intel_cmtg_regs.h"
#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display.h"
#include "intel_display_device.h"
#include "intel_display_irq.h"
#include "intel_display_power.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_vrr.h"
#include "intel_vrr_regs.h"
/**
* DOC: Common Primary Timing Generator (CMTG)
@@ -81,6 +85,18 @@ static void intel_cmtg_dump_config(struct intel_display *display,
str_yes_no(cmtg_config->trans_b_secondary));
}
static inline enum transcoder to_cmtg_transcoder(enum transcoder cpu_transcoder)
{
switch (cpu_transcoder) {
case TRANSCODER_A:
return TRANSCODER_CMTG0;
case TRANSCODER_B:
return TRANSCODER_CMTG1;
default:
return INVALID_TRANSCODER;
}
}
static bool intel_cmtg_transcoder_is_secondary(struct intel_display *display,
enum transcoder trans)
{
@@ -103,11 +119,11 @@ static void intel_cmtg_get_config(struct intel_display *display,
{
u32 val;
val = intel_de_read(display, TRANS_CMTG_CTL_A);
val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_A));
cmtg_config->cmtg_a_enable = val & CMTG_ENABLE;
if (intel_cmtg_has_cmtg_b(display)) {
val = intel_de_read(display, TRANS_CMTG_CTL_B);
val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_B));
cmtg_config->cmtg_b_enable = val & CMTG_ENABLE;
}
@@ -124,8 +140,8 @@ static bool intel_cmtg_disable_requires_modeset(struct intel_display *display,
return cmtg_config->trans_a_secondary || cmtg_config->trans_b_secondary;
}
static void intel_cmtg_disable(struct intel_display *display,
struct intel_cmtg_config *cmtg_config)
static void intel_cmtg_disable_all(struct intel_display *display,
struct intel_cmtg_config *cmtg_config)
{
u32 clk_sel_clr = 0;
u32 clk_sel_set = 0;
@@ -140,14 +156,14 @@ static void intel_cmtg_disable(struct intel_display *display,
if (cmtg_config->cmtg_a_enable) {
drm_dbg_kms(display->drm, "Disabling CMTG A\n");
intel_de_rmw(display, TRANS_CMTG_CTL_A, CMTG_ENABLE, 0);
intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_A), CMTG_ENABLE, 0);
clk_sel_clr |= CMTG_CLK_SEL_A_MASK;
clk_sel_set |= CMTG_CLK_SEL_A_DISABLED;
}
if (cmtg_config->cmtg_b_enable) {
drm_dbg_kms(display->drm, "Disabling CMTG B\n");
intel_de_rmw(display, TRANS_CMTG_CTL_B, CMTG_ENABLE, 0);
intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_B), CMTG_ENABLE, 0);
clk_sel_clr |= CMTG_CLK_SEL_B_MASK;
clk_sel_set |= CMTG_CLK_SEL_B_DISABLED;
}
@@ -156,6 +172,57 @@ static void intel_cmtg_disable(struct intel_display *display,
intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set);
}
void intel_cmtg_disable(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
u32 clk_sel_clr = 0, interrupt_mask = 0;
if (!crtc->cmtg.enabled)
return;
if (drm_WARN_ON(display->drm, cmtg_transcoder == INVALID_TRANSCODER))
return;
crtc->cmtg.enabled = false;
intel_de_rmw(display, TRANS_VRR_CTL(display, cmtg_transcoder),
VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN, 0);
/*
* Use cpu_transcoder for:
* 1. Exclusive CMTG registers that do not use the standard transcoder offset
* (e.g., TRANS_CMTG_CTL, CMTG_CLK_SEL).
* 2. Registers shared between the eDP and CMTG transcoders.
* (e.g., TRANS_DDI_FUNC_CTL2).
*/
intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder),
CMTG_SECONDARY_MODE, 0);
intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), CMTG_HW_GB_ENABLE, 0);
intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_ENABLE, 0);
if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_STATE, 50)) {
drm_WARN(display->drm, 1, "CMTG: %s disable timeout\n",
transcoder_name(cpu_transcoder));
return;
}
clk_sel_clr = cpu_transcoder == TRANSCODER_A ? CMTG_CLK_SEL_A_MASK : CMTG_CLK_SEL_B_MASK;
intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, 0);
drm_dbg_kms(display->drm, "CMTG: %s disabled\n", transcoder_name(cpu_transcoder));
if (cpu_transcoder == TRANSCODER_A)
interrupt_mask = CMTG_VBLANK_A;
else if (cpu_transcoder == TRANSCODER_B)
interrupt_mask = CMTG_VBLANK_B;
intel_display_irq_port_interrupt_mask(display, interrupt_mask, true);
}
/*
* Read out CMTG configuration and, on platforms that allow disabling it without
* a modeset, do it.
@@ -183,5 +250,202 @@ void intel_cmtg_sanitize(struct intel_display *display)
if (intel_cmtg_disable_requires_modeset(display, &cmtg_config))
return;
intel_cmtg_disable(display, &cmtg_config);
intel_cmtg_disable_all(display, &cmtg_config);
}
bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
if ((cpu_transcoder == TRANSCODER_A || cpu_transcoder == TRANSCODER_B) &&
DISPLAY_VER(display) == 35 && intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
return true;
return false;
}
void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
u32 clk_sel_clr = 0;
u32 clk_sel_set = 0;
if (!intel_cmtg_is_allowed(crtc_state))
return;
if (cpu_transcoder == TRANSCODER_A) {
clk_sel_clr = CMTG_CLK_SEL_A_MASK;
clk_sel_set = CMTG_CLK_SELECT_PHYA_ENABLE;
} else if (cpu_transcoder == TRANSCODER_B) {
clk_sel_clr = CMTG_CLK_SEL_B_MASK;
clk_sel_set = CMTG_CLK_SELECT_PHYB_ENABLE;
}
if (clk_sel_set)
intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set);
}
void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type)
{
enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
if (cmtg_transcoder == INVALID_TRANSCODER)
return;
if (!intel_cmtg_is_allowed(crtc_state))
return;
if (type == LRR)
intel_set_transcoder_timings_lrr(crtc_state, cmtg_transcoder);
else
intel_set_transcoder_timings(crtc_state, cmtg_transcoder);
}
void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state)
{
enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
if (!intel_cmtg_is_allowed(crtc_state))
return;
intel_vrr_set_fixed_rr_timings(crtc_state, cmtg_transcoder);
}
void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
u32 vrr_ctl;
if (!intel_cmtg_is_allowed(crtc_state))
return;
vrr_ctl = VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN |
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)
vrr_ctl |= VRR_CTL_CMRR_ENABLE;
intel_de_write(display, TRANS_VRR_CTL(display, cmtg_transcoder), vrr_ctl);
}
void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder);
const struct intel_link_m_n *m_n = &crtc_state->dp_m_n;
if (!intel_cmtg_is_allowed(crtc_state))
return;
intel_de_write(display, PIPE_LINK_M1(display, cmtg_transcoder), m_n->link_m);
intel_de_write(display, PIPE_LINK_N1(display, cmtg_transcoder), m_n->link_n);
}
static void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
u32 cmtg_ctl;
cmtg_ctl = CMTG_SYNC_TO_PORT | CMTG_ENABLE;
intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), 0, cmtg_ctl);
if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder),
CMTG_SYNC_TO_PORT, 50)) {
drm_WARN(display->drm, 1, "CMTG: %s enable timeout\n",
transcoder_name(cpu_transcoder));
}
}
static void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
u32 interrupt_mask = 0;
intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE);
intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), 0, CMTG_HW_GB_ENABLE);
crtc->cmtg.enabled = true;
drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder));
/*
* TODO: Currently cmtg is enabled along with eDP transcoder so cmtg
* interrupt is not enabled through IER, need to do some fine
* tuning in future.
*/
if (cpu_transcoder == TRANSCODER_A)
interrupt_mask = CMTG_VBLANK_A;
else if (cpu_transcoder == TRANSCODER_B)
interrupt_mask = CMTG_VBLANK_B;
intel_display_irq_port_interrupt_mask(display, interrupt_mask, false);
}
/* Bspec: 75253 */
#define DC3CO_ENTRY_LATENCY_US 55
#define DC3CO_EXIT_LATENCY_US 40
static void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
u32 breakeven_gb;
u32 dc5_exit_latency;
u32 line_time_us = 75; /* Max default initialization value */
u32 val;
if (crtc_state->linetime)
line_time_us = DIV_ROUND_UP(crtc_state->linetime, 8);
/* Break Even Guardband - DC3co Entry Latency / linetime */
breakeven_gb = DIV_ROUND_UP(DC3CO_ENTRY_LATENCY_US, line_time_us);
/* DC5 Exit Latency - DC3co Exit Latency / linetime */
dc5_exit_latency = DIV_ROUND_UP(DC3CO_EXIT_LATENCY_US, line_time_us);
val = REG_FIELD_PREP(CMTG_HW_GB_BREAKEVEN_MASK, breakeven_gb) |
REG_FIELD_PREP(CMTG_HW_GB_DC5_EXIT_LATENCY_MASK, dc5_exit_latency) |
REG_FIELD_PREP(CMTG_HW_GB_UP_LW_BG_DIFF_MASK, 1);
intel_de_write(display, CMTG_HW_GB(cpu_transcoder), val);
}
static void intel_cmtg_restore(const struct intel_crtc_state *crtc_state)
{
intel_cmtg_set_clk_select(crtc_state);
intel_cmtg_set_timings(crtc_state, MODESET);
intel_cmtg_set_vrr_timings(crtc_state);
intel_cmtg_set_vrr_ctl(crtc_state);
intel_cmtg_set_m_n(crtc_state);
}
void intel_cmtg_program(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
struct intel_crtc *crtc;
struct intel_crtc_state *new_crtc_state;
bool dc3co_to_dc6 = intel_display_power_get_and_reset_dc3co_to_dc6(display);
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) {
bool modeset = intel_crtc_needs_modeset(new_crtc_state);
if (!intel_cmtg_is_allowed(new_crtc_state))
continue;
if ((modeset || dc3co_to_dc6) &&
new_crtc_state->hw.active && !crtc->cmtg.enabled) {
if (dc3co_to_dc6)
intel_cmtg_restore(new_crtc_state);
intel_cmtg_enable_sync(new_crtc_state);
intel_cmtg_set_hwgb(new_crtc_state);
intel_cmtg_enable_ddi(new_crtc_state);
}
}
}

View File

@@ -6,8 +6,25 @@
#ifndef __INTEL_CMTG_H__
#define __INTEL_CMTG_H__
struct intel_display;
#include <linux/types.h>
struct intel_atomic_state;
struct intel_display;
struct intel_crtc_state;
enum set_timing_type {
MODESET = 0,
LRR
};
void intel_cmtg_disable(const struct intel_crtc_state *crtc_state);
void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state);
void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state);
void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state);
void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type);
void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state);
void intel_cmtg_sanitize(struct intel_display *display);
bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state);
void intel_cmtg_program(struct intel_atomic_state *state);
#endif /* __INTEL_CMTG_H__ */

View File

@@ -10,12 +10,32 @@
#define CMTG_CLK_SEL _MMIO(0x46160)
#define CMTG_CLK_SEL_A_MASK REG_GENMASK(31, 29)
#define CMTG_CLK_SELECT_PHYA_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0x4)
#define CMTG_CLK_SEL_A_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0)
#define CMTG_CLK_SEL_B_MASK REG_GENMASK(15, 13)
#define CMTG_CLK_SELECT_PHYB_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0x6)
#define CMTG_CLK_SEL_B_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0)
#define TRANS_CMTG_CTL_A _MMIO(0x6fa88)
#define TRANS_CMTG_CTL_B _MMIO(0x6fb88)
#define _TRANS_CMTG_CTL_A 0x6fa88
#define _TRANS_CMTG_CTL_B 0x6fb88
#define TRANS_CMTG_CTL(trans) _MMIO_TRANS((trans), \
_TRANS_CMTG_CTL_A, _TRANS_CMTG_CTL_B)
#define CMTG_ENABLE REG_BIT(31)
#define CMTG_SYNC_TO_PORT REG_BIT(29)
#define CMTG_STATE REG_BIT(23)
#define _CMTG_HW_GB_A 0x6fa8c
#define _CMTG_HW_GB_B 0x6fb8c
#define CMTG_HW_GB(trans) _MMIO_TRANS((trans), \
_CMTG_HW_GB_A, _CMTG_HW_GB_B)
#define CMTG_HW_GB_BREAKEVEN_MASK REG_GENMASK(11, 0)
#define CMTG_HW_GB_DC5_EXIT_LATENCY_MASK REG_GENMASK(27, 16)
#define CMTG_HW_GB_UP_LW_BG_DIFF_MASK REG_GENMASK(31, 28)
#define _CMTG_SCANLINE_GB1_A 0x456A0
#define _CMTG_SCANLINE_GB1_B 0x456C0
#define CMTG_SCANLINE_GB1(trans) _MMIO_TRANS((trans), \
_CMTG_SCANLINE_GB1_A, _CMTG_SCANLINE_GB1_B)
#define CMTG_HW_GB_ENABLE REG_BIT(31)
#endif /* __INTEL_CMTG_REGS_H__ */

View File

@@ -3967,48 +3967,34 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb,
enum pipe pipe = to_intel_plane(state->plane)->pipe;
enum plane_id plane = to_intel_plane(state->plane)->id;
const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data;
u32 i, lut_size;
int i, lut_size = 128;
u32 lut_val;
if (icl_is_hdr_plane(display, plane)) {
lut_size = 128;
if (!icl_is_hdr_plane(display, plane))
return;
intel_de_write_dsb(display, dsb,
PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
PLANE_PAL_PREC_AUTO_INCREMENT);
intel_de_write_dsb(display, dsb,
PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
PLANE_PAL_PREC_AUTO_INCREMENT);
for (i = 0; i < lut_size + 3; i++) {
if (pre_csc_lut) {
for (i = 0; i < lut_size; i++) {
u32 lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24);
intel_de_write_dsb(display, dsb,
PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
lut_val);
}
/* Program the max register to clamp values > 1.0. */
/* TODO: Restrict to 0x7ffffff */
do {
intel_de_write_dsb(display, dsb,
PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
(1 << 24));
} while (i++ < 130);
if (i < lut_size)
lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24);
/* else duplicate last lut_val */
} else {
for (i = 0; i < lut_size; i++) {
u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);
intel_de_write_dsb(display, dsb,
PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
}
do {
intel_de_write_dsb(display, dsb,
PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
1 << 24);
} while (i++ < 130);
if (i < lut_size)
lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1);
else
lut_val = 1 << 24;
}
intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
intel_de_write_dsb(display, dsb,
PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
lut_val);
}
intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
}
static void
@@ -4020,51 +4006,38 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb,
enum pipe pipe = to_intel_plane(state->plane)->pipe;
enum plane_id plane = to_intel_plane(state->plane)->id;
const struct drm_color_lut32 *post_csc_lut = plane_state->hw.gamma_lut->data;
u32 i, lut_size, lut_val;
int i, lut_size = 32;
u32 lut_val;
if (icl_is_hdr_plane(display, plane)) {
intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
PLANE_PAL_PREC_AUTO_INCREMENT);
/* TODO: Add macro */
intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0),
PLANE_PAL_PREC_AUTO_INCREMENT);
if (!icl_is_hdr_plane(display, plane))
return;
intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
PLANE_PAL_PREC_AUTO_INCREMENT);
/* TODO: Add macro */
intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0),
PLANE_PAL_PREC_AUTO_INCREMENT);
for (i = 0; i < lut_size + 3; i++) {
if (post_csc_lut) {
lut_size = 32;
for (i = 0; i < lut_size; i++) {
if (i < lut_size)
lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24);
intel_de_write_dsb(display, dsb,
PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
lut_val);
}
/* Segment 2 */
do {
intel_de_write_dsb(display, dsb,
PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
(1 << 24));
} while (i++ < 34);
/* else clamp to the last LUT value to prevent step discontinuity */
} else {
/*TODO: Add for segment 0 */
lut_size = 32;
for (i = 0; i < lut_size; i++) {
u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);
intel_de_write_dsb(display, dsb,
PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
}
do {
intel_de_write_dsb(display, dsb,
PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
1 << 24);
} while (i++ < 34);
if (i < lut_size)
lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1);
else
lut_val = 1 << 24;
}
intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
intel_de_write_dsb(display, dsb,
PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0);
PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
lut_val);
}
intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
intel_de_write_dsb(display, dsb,
PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0);
}
static void

View File

@@ -397,7 +397,8 @@ intel_crt_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
static int intel_crt_compute_config(struct intel_encoder *encoder,
static int intel_crt_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -413,7 +414,8 @@ static int intel_crt_compute_config(struct intel_encoder *encoder,
return 0;
}
static int pch_crt_compute_config(struct intel_encoder *encoder,
static int pch_crt_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -432,7 +434,8 @@ static int pch_crt_compute_config(struct intel_encoder *encoder,
return 0;
}
static int hsw_crt_compute_config(struct intel_encoder *encoder,
static int hsw_crt_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{

View File

@@ -876,7 +876,7 @@ intel_legacy_cursor_update(struct drm_plane *_plane,
new_plane_state->uapi.crtc_w = crtc_w;
new_plane_state->uapi.crtc_h = crtc_h;
intel_plane_copy_uapi_to_hw_state(new_plane_state, new_plane_state, crtc);
intel_plane_copy_uapi_to_hw_state(NULL, new_plane_state, new_plane_state, crtc);
ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state,
old_plane_state, new_plane_state);

View File

@@ -9,6 +9,7 @@
#include <drm/drm_print.h>
#include "intel_alpm.h"
#include "intel_cmtg.h"
#include "intel_cx0_phy.h"
#include "intel_cx0_phy_regs.h"
#include "intel_display_regs.h"
@@ -3418,10 +3419,20 @@ void intel_mtl_pll_enable(struct intel_encoder *encoder,
void intel_mtl_pll_enable_clock(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(encoder);
struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
if (intel_tc_port_in_tbt_alt_mode(dig_port))
intel_mtl_tbt_pll_enable_clock(encoder, crtc_state->port_clock);
/*
* CMTG can be enabled only when the transcoder and port are compatible
* (transcoder A with port A, transcoder B with port B).
*/
if (HAS_LT_PHY(display) &&
((crtc_state->cpu_transcoder == TRANSCODER_A && encoder->port == PORT_A) ||
(crtc_state->cpu_transcoder == TRANSCODER_B && encoder->port == PORT_B)))
intel_cmtg_set_clk_select(crtc_state);
}
/*

View File

@@ -60,7 +60,6 @@ void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
u8 lane_mask, u8 state);
int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock);
void intel_cx0_setup_powerdown(struct intel_encoder *encoder);
bool intel_cx0_is_hdmi_frl(u32 clock);
u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr);

View File

@@ -3501,6 +3501,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state,
}
intel_ddi_buf_enable(encoder, buf_ctl);
intel_hdmi_poll_for_scrambling_enable(crtc_state, connector);
}
static void intel_ddi_enable(struct intel_atomic_state *state,
@@ -4483,7 +4485,8 @@ intel_ddi_compute_output_type(struct intel_encoder *encoder,
}
}
static int intel_ddi_compute_config(struct intel_encoder *encoder,
static int intel_ddi_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -4501,7 +4504,7 @@ static int intel_ddi_compute_config(struct intel_encoder *encoder,
ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state);
} else {
ret = intel_dp_compute_config(encoder, pipe_config, conn_state);
ret = intel_dp_compute_config(state, encoder, pipe_config, conn_state);
}
if (ret)
@@ -4606,7 +4609,8 @@ intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state,
return transcoders;
}
static int intel_ddi_compute_config_late(struct intel_encoder *encoder,
static int intel_ddi_compute_config_late(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -4659,6 +4663,7 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
drm_encoder_cleanup(encoder);
kfree(dig_port->hdcp.port_data.streams);
intel_dp_link_cleanup(&dig_port->dp);
kfree(dig_port);
}
@@ -4696,11 +4701,16 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
struct intel_display *display = to_intel_display(dig_port);
struct intel_connector *connector;
enum port port = dig_port->base.port;
int err;
connector = intel_connector_alloc();
if (!connector)
return -ENOMEM;
err = intel_dp_link_init(&dig_port->dp);
if (err)
goto err_dp_init;
dig_port->dp.output_reg = DDI_BUF_CTL(port);
if (DISPLAY_VER(display) >= 14)
dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain;
@@ -4713,8 +4723,9 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
dig_port->dp.preemph_max = intel_ddi_dp_preemph_max;
if (!intel_dp_init_connector(dig_port, connector)) {
kfree(connector);
return -EINVAL;
err = -EINVAL;
goto err_init_connector;
}
if (dig_port->base.type == INTEL_OUTPUT_EDP) {
@@ -4730,6 +4741,13 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
}
return 0;
err_init_connector:
intel_dp_link_cleanup(&dig_port->dp);
err_dp_init:
kfree(connector);
return err;
}
static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port)
@@ -4738,6 +4756,7 @@ static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port)
struct intel_connector *connector = intel_dp->attached_connector;
intel_dp_cleanup_connector(dig_port, connector);
intel_dp_link_cleanup(intel_dp);
kfree(connector);
}

View File

@@ -9,11 +9,11 @@
#include "intel_de.h"
static int __intel_de_wait_for_register(struct intel_display *display,
intel_reg_t reg, u32 mask, u32 value,
unsigned int timeout_us,
u32 (*read)(struct intel_display *display, intel_reg_t reg),
u32 *out_val, bool is_atomic)
static int intel_de_wait_for_register(struct intel_display *display,
intel_reg_t reg, u32 mask, u32 value,
unsigned int timeout_us,
u32 (*read)(struct intel_display *display, intel_reg_t reg),
u32 *out_val, bool is_atomic)
{
const ktime_t end = ktime_add_us(ktime_get_raw(), timeout_us);
int wait_max = 1000;
@@ -60,28 +60,6 @@ static int __intel_de_wait_for_register(struct intel_display *display,
return ret;
}
static int intel_de_wait_for_register(struct intel_display *display,
intel_reg_t reg, u32 mask, u32 value,
unsigned int fast_timeout_us,
unsigned int slow_timeout_us,
u32 (*read)(struct intel_display *display, intel_reg_t reg),
u32 *out_value, bool is_atomic)
{
int ret = -EINVAL;
if (fast_timeout_us)
ret = __intel_de_wait_for_register(display, reg, mask, value,
fast_timeout_us, read,
out_value, is_atomic);
if (ret && slow_timeout_us)
ret = __intel_de_wait_for_register(display, reg, mask, value,
slow_timeout_us, read,
out_value, is_atomic);
return ret;
}
int intel_de_wait_us(struct intel_display *display, intel_reg_t reg,
u32 mask, u32 value, unsigned int timeout_us,
u32 *out_value)
@@ -91,7 +69,7 @@ int intel_de_wait_us(struct intel_display *display, intel_reg_t reg,
intel_dmc_wl_get(display, reg);
ret = intel_de_wait_for_register(display, reg, mask, value,
timeout_us, 0,
timeout_us,
intel_de_read,
out_value, false);
@@ -109,7 +87,7 @@ int intel_de_wait_ms(struct intel_display *display, intel_reg_t reg,
intel_dmc_wl_get(display, reg);
ret = intel_de_wait_for_register(display, reg, mask, value,
2, timeout_ms * 1000,
timeout_ms * 1000,
intel_de_read,
out_value, false);
@@ -123,7 +101,7 @@ int intel_de_wait_fw_ms(struct intel_display *display, intel_reg_t reg,
u32 *out_value)
{
return intel_de_wait_for_register(display, reg, mask, value,
2, timeout_ms * 1000,
timeout_ms * 1000,
intel_de_read_fw,
out_value, false);
}
@@ -133,7 +111,7 @@ int intel_de_wait_fw_us_atomic(struct intel_display *display, intel_reg_t reg,
u32 *out_value)
{
return intel_de_wait_for_register(display, reg, mask, value,
timeout_us, 0,
timeout_us,
intel_de_read_fw,
out_value, true);
}

View File

@@ -60,6 +60,7 @@
#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"
#include "intel_crtc.h"
@@ -132,7 +133,6 @@
#include "vlv_dsi_pll.h"
#include "vlv_dsi_regs.h"
static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state);
static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state);
static void hsw_set_transconf(const struct intel_crtc_state *crtc_state);
static void bdw_set_pipe_misc(struct intel_dsb *dsb,
@@ -1504,7 +1504,7 @@ static void ilk_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta
&crtc_state->dp_m2_n2);
}
intel_set_transcoder_timings(crtc_state);
intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder);
ilk_set_pipeconf(crtc_state);
}
@@ -1635,7 +1635,9 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta
&crtc_state->dp_m2_n2);
}
intel_set_transcoder_timings(crtc_state);
intel_cmtg_set_m_n(crtc_state);
intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder);
intel_cmtg_set_timings(crtc_state, MODESET);
if (cpu_transcoder != TRANSCODER_EDP)
intel_de_write(display, TRANS_MULT(display, cpu_transcoder),
@@ -1788,6 +1790,10 @@ static void hsw_crtc_disable(struct intel_atomic_state *state,
intel_atomic_get_old_crtc_state(state, crtc);
struct intel_crtc *pipe_crtc;
if (crtc->cmtg.enabled && intel_cmtg_is_allowed(old_crtc_state)) {
intel_cmtg_set_clk_select(old_crtc_state);
intel_cmtg_disable(old_crtc_state);
}
/*
* FIXME collapse everything to one hook.
* Need care with mst->ddi interactions.
@@ -2048,7 +2054,7 @@ static void i9xx_configure_cpu_transcoder(const struct intel_crtc_state *crtc_st
&crtc_state->dp_m2_n2);
}
intel_set_transcoder_timings(crtc_state);
intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder);
i9xx_set_pipeconf(crtc_state);
}
@@ -2664,17 +2670,17 @@ transcoder_has_vrr(const struct intel_crtc_state *crtc_state)
return HAS_VRR(display) && !transcoder_is_dsi(cpu_transcoder);
}
static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state,
enum transcoder transcoder)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
enum pipe pipe = crtc->pipe;
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end;
int vsyncshift = 0;
drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder));
drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder));
/* We need to be careful not to changed the adjusted mode, for otherwise
* the hw state checker will get angry at the mismatch. */
@@ -2703,7 +2709,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
*/
if (DISPLAY_VER(display) >= 13) {
intel_de_write(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
TRANS_SET_CONTEXT_LATENCY(display, transcoder),
crtc_state->set_context_latency);
/*
@@ -2718,35 +2724,26 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
if (DISPLAY_VER(display) >= 4 && DISPLAY_VER(display) < 35)
intel_de_write(display,
TRANS_VSYNCSHIFT(display, cpu_transcoder),
TRANS_VSYNCSHIFT(display, transcoder),
vsyncshift);
intel_de_write(display, TRANS_HTOTAL(display, cpu_transcoder),
intel_de_write(display, TRANS_HTOTAL(display, transcoder),
HACTIVE(adjusted_mode->crtc_hdisplay - 1) |
HTOTAL(adjusted_mode->crtc_htotal - 1));
intel_de_write(display, TRANS_HBLANK(display, cpu_transcoder),
intel_de_write(display, TRANS_HBLANK(display, transcoder),
HBLANK_START(adjusted_mode->crtc_hblank_start - 1) |
HBLANK_END(adjusted_mode->crtc_hblank_end - 1));
intel_de_write(display, TRANS_HSYNC(display, cpu_transcoder),
intel_de_write(display, TRANS_HSYNC(display, transcoder),
HSYNC_START(adjusted_mode->crtc_hsync_start - 1) |
HSYNC_END(adjusted_mode->crtc_hsync_end - 1));
/*
* For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal
* bits are not required. Since the support for these bits is going to
* be deprecated in upcoming platforms, avoid writing these bits for the
* platforms that do not use legacy Timing Generator.
*/
if (intel_vrr_always_use_vrr_tg(display))
crtc_vtotal = 1;
intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder),
intel_de_write(display, TRANS_VTOTAL(display, transcoder),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder),
intel_de_write(display, TRANS_VBLANK(display, transcoder),
VBLANK_START(crtc_vblank_start - 1) |
VBLANK_END(crtc_vblank_end - 1));
intel_de_write(display, TRANS_VSYNC(display, cpu_transcoder),
intel_de_write(display, TRANS_VSYNC(display, transcoder),
VSYNC_START(adjusted_mode->crtc_vsync_start - 1) |
VSYNC_END(adjusted_mode->crtc_vsync_end - 1));
@@ -2754,13 +2751,15 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
* programmed with the VTOTAL_EDP value. Same for VTOTAL_C. This is
* documented on the DDI_FUNC_CTL register description, EDP Input Select
* bits. */
if (display->platform.haswell && cpu_transcoder == TRANSCODER_EDP &&
if (display->platform.haswell && transcoder == TRANSCODER_EDP &&
(pipe == PIPE_B || pipe == PIPE_C))
intel_de_write(display, TRANS_VTOTAL(display, pipe),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
if (DISPLAY_VER(display) >= 30) {
if (DISPLAY_VER(display) >= 30 &&
transcoder != TRANSCODER_CMTG0 &&
transcoder != TRANSCODER_CMTG1) {
/*
* Address issues for resolutions with high refresh rate that
* have small Hblank, specifically where Hblank is smaller than
@@ -2769,19 +2768,19 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
* followed by BE which DPRX devices are unable to handle.
* https://groups.vesa.org/wg/DP/document/20494
*/
intel_de_write(display, DP_MIN_HBLANK_CTL(cpu_transcoder),
intel_de_write(display, DP_MIN_HBLANK_CTL(transcoder),
crtc_state->min_hblank);
}
}
static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state)
void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state,
enum transcoder transcoder)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end;
drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder));
drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder));
crtc_vdisplay = adjusted_mode->crtc_vdisplay;
crtc_vtotal = adjusted_mode->crtc_vtotal;
@@ -2796,7 +2795,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc
if (DISPLAY_VER(display) >= 13) {
intel_de_write(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
TRANS_SET_CONTEXT_LATENCY(display, transcoder),
crtc_state->set_context_latency);
/*
@@ -2813,28 +2812,27 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc
* The hardware actually ignores TRANS_VBLANK.VBLANK_END in DP mode.
* But let's write it anyway to keep the state checker happy.
*/
intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder),
intel_de_write(display, TRANS_VBLANK(display, transcoder),
VBLANK_START(crtc_vblank_start - 1) |
VBLANK_END(crtc_vblank_end - 1));
/*
* For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal
* bits are not required. Since the support for these bits is going to
* be deprecated in upcoming platforms, avoid writing these bits for the
* platforms that do not use legacy Timing Generator.
* DP doesn't have vertical sync, so TRANS_VSYNC only affects
* the position of the vsync interrupt (and does so even when
* using the VRR timing generator!). Thus updating TRANS_VSYNC
* here seems fine even if it isn't double buffered.
*/
if (intel_vrr_always_use_vrr_tg(display))
crtc_vtotal = 1;
intel_de_write(display, TRANS_VSYNC(display, transcoder),
VSYNC_START(adjusted_mode->crtc_vsync_start - 1) |
VSYNC_END(adjusted_mode->crtc_vsync_end - 1));
/*
* The double buffer latch point for TRANS_VTOTAL
* is the transcoder's undelayed vblank.
*/
intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder),
intel_de_write(display, TRANS_VTOTAL(display, transcoder),
VACTIVE(crtc_vdisplay - 1) |
VTOTAL(crtc_vtotal - 1));
intel_vrr_set_fixed_rr_timings(crtc_state);
intel_vrr_transcoder_enable(crtc_state);
}
static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state)
@@ -4783,7 +4781,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
if (connector_state->crtc != &crtc->base)
continue;
ret = encoder->compute_config(encoder, crtc_state,
ret = encoder->compute_config(state, encoder, crtc_state,
connector_state);
if (ret == -EDEADLK)
return ret;
@@ -4843,7 +4841,7 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state,
!encoder->compute_config_late)
continue;
ret = encoder->compute_config_late(encoder, crtc_state,
ret = encoder->compute_config_late(state, encoder, crtc_state,
conn_state);
if (ret)
return ret;
@@ -5189,9 +5187,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_I(name.crtc_vdisplay); \
if (!fastset || !allow_vblank_delay_fastset(current_config)) \
PIPE_CONF_CHECK_I(name.crtc_vblank_start); \
PIPE_CONF_CHECK_I(name.crtc_vsync_start); \
PIPE_CONF_CHECK_I(name.crtc_vsync_end); \
if (!fastset || !pipe_config->update_lrr) { \
PIPE_CONF_CHECK_I(name.crtc_vsync_start); \
PIPE_CONF_CHECK_I(name.crtc_vsync_end); \
PIPE_CONF_CHECK_I(name.crtc_vtotal); \
PIPE_CONF_CHECK_I(name.crtc_vblank_end); \
} \
@@ -5658,35 +5656,43 @@ int intel_modeset_all_pipes_late(struct intel_atomic_state *state,
return 0;
}
int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state,
u8 pipe_mask,
struct drm_modeset_acquire_ctx *ctx)
{
struct intel_display *display = to_intel_display(state);
struct intel_crtc *crtc;
state->base.acquire_ctx = ctx;
state->internal = true;
for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) {
struct intel_crtc_state *crtc_state =
intel_atomic_get_crtc_state(&state->base, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
crtc_state->uapi.connectors_changed = true;
}
return drm_atomic_commit(&state->base);
}
int intel_modeset_commit_pipes(struct intel_display *display,
u8 pipe_mask,
struct drm_modeset_acquire_ctx *ctx)
{
struct drm_atomic_commit *state;
struct intel_crtc *crtc;
int ret;
state = drm_atomic_commit_alloc(display->drm);
if (!state)
return -ENOMEM;
state->acquire_ctx = ctx;
to_intel_atomic_state(state)->internal = true;
ret = intel_modeset_commit_pipes_for_atomic_state(to_intel_atomic_state(state),
pipe_mask, ctx);
for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) {
struct intel_crtc_state *crtc_state =
intel_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
ret = PTR_ERR(crtc_state);
goto out;
}
crtc_state->uapi.connectors_changed = true;
}
ret = drm_atomic_commit(state);
out:
drm_atomic_commit_put(state);
return ret;
@@ -5805,6 +5811,8 @@ static bool lrr_params_changed(const struct intel_crtc_state *old_crtc_state,
return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start ||
old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end ||
old_adjusted_mode->crtc_vsync_start != new_adjusted_mode->crtc_vsync_start ||
old_adjusted_mode->crtc_vsync_end != new_adjusted_mode->crtc_vsync_end ||
old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal ||
old_crtc_state->set_context_latency != new_crtc_state->set_context_latency;
}
@@ -6667,12 +6675,19 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state,
display->platform.broadwell || display->platform.haswell)
hsw_set_linetime_wm(new_crtc_state);
if (new_crtc_state->update_m_n)
if (new_crtc_state->update_m_n) {
intel_cpu_transcoder_set_m1_n1(crtc, new_crtc_state->cpu_transcoder,
&new_crtc_state->dp_m_n);
intel_cmtg_set_m_n(new_crtc_state);
}
if (new_crtc_state->update_lrr)
intel_set_transcoder_timings_lrr(new_crtc_state);
if (new_crtc_state->update_lrr) {
intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder);
intel_cmtg_set_timings(new_crtc_state, LRR);
intel_vrr_set_fixed_rr_timings(new_crtc_state, new_crtc_state->cpu_transcoder);
intel_cmtg_set_vrr_timings(new_crtc_state);
intel_vrr_transcoder_enable(new_crtc_state);
}
}
static void commit_pipe_pre_planes(struct intel_atomic_state *state,
@@ -6870,6 +6885,12 @@ static void intel_update_crtc(struct intel_atomic_state *state,
if (intel_crtc_needs_fastset(new_crtc_state) &&
old_crtc_state->inherited)
intel_crtc_arm_fifo_underrun(crtc, new_crtc_state);
if (crtc->cmtg.enabled && intel_crtc_vrr_enabling(state, crtc) &&
intel_cmtg_is_allowed(new_crtc_state)) {
intel_cmtg_set_clk_select(new_crtc_state);
intel_cmtg_disable(new_crtc_state);
}
}
static void intel_old_crtc_state_disables(struct intel_atomic_state *state,
@@ -7428,6 +7449,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
struct intel_crtc *crtc;
struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {};
struct ref_tracker *wakeref = NULL;
int power_async_delay;
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state)
intel_atomic_dsb_prepare(state, crtc);
@@ -7536,9 +7558,14 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
/* Now enable the clocks, plane, pipe, and connectors that we set up. */
display->modeset.funcs->commit_modeset_enables(state);
intel_display_power_dc3co_compute(state);
/* FIXME probably need to sequence this properly */
intel_program_dpkgc_latency(state);
if (intel_display_power_dc3co_allowed(display))
intel_cmtg_program(state);
intel_wait_for_vblank_workers(state);
/* FIXME: We should call drm_atomic_helper_commit_hw_done() here
@@ -7632,11 +7659,12 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
*/
intel_uncore_arm_unclaimed_mmio_detection(uncore);
}
/*
* Delay re-enabling DC states by 17 ms to avoid the off->on->off
* toggling overhead at and above 60 FPS.
*/
intel_display_power_put_async_delay(display, POWER_DOMAIN_DC_OFF, wakeref, 17);
power_async_delay = intel_display_power_select_target_dc_state(state);
intel_display_power_put_async_delay(display,
POWER_DOMAIN_DC_OFF, wakeref, power_async_delay);
intel_display_rpm_put(display, state->wakeref);
/*

View File

@@ -424,6 +424,10 @@ void intel_set_m_n(struct intel_display *display,
const struct intel_link_m_n *m_n,
intel_reg_t data_m_reg, intel_reg_t data_n_reg,
intel_reg_t link_m_reg, intel_reg_t link_n_reg);
void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state,
enum transcoder transcoder);
void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state,
enum transcoder transcoder);
void intel_get_m_n(struct intel_display *display,
struct intel_link_m_n *m_n,
intel_reg_t data_m_reg, intel_reg_t data_n_reg,
@@ -474,6 +478,9 @@ int intel_modeset_pipes_in_mask_early(struct intel_atomic_state *state,
const char *reason, u8 pipe_mask);
int intel_modeset_all_pipes_late(struct intel_atomic_state *state,
const char *reason);
int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state,
u8 pipe_mask,
struct drm_modeset_acquire_ctx *ctx);
int intel_modeset_commit_pipes(struct intel_display *display,
u8 pipe_mask,
struct drm_modeset_acquire_ctx *ctx);

View File

@@ -325,14 +325,14 @@ struct intel_display {
struct intel_bw_info {
/* for each QGV point */
unsigned int deratedbw[I915_NUM_QGV_POINTS];
/* for each PSF GV point */
unsigned int psf_bw[I915_NUM_PSF_GV_POINTS];
/* Peak BW for each QGV point */
unsigned int peakbw[I915_NUM_QGV_POINTS];
u8 num_qgv_points;
u8 num_psf_gv_points;
u8 num_planes;
} max[6];
/* for each PSF GV point */
unsigned int psf_bw[I915_NUM_PSF_GV_POINTS];
/* Peak BW for each QGV point */
unsigned int peakbw[I915_NUM_QGV_POINTS];
u8 num_qgv_points;
u8 num_psf_gv_points;
} bw;
struct {
@@ -538,6 +538,8 @@ struct intel_display {
struct {
struct i915_power_domains domains;
/* DC3CO state */
struct intel_dc3co_state dc3co;
/* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */
u32 chv_phy_control;

View File

@@ -32,6 +32,7 @@
#include "intel_display_types.h"
#include "intel_dmc.h"
#include "intel_dp.h"
#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
@@ -1342,6 +1343,7 @@ void intel_connector_debugfs_add(struct intel_connector *connector)
intel_psr_connector_debugfs_add(connector);
intel_alpm_lobf_debugfs_add(connector);
intel_dp_link_training_debugfs_add(connector);
intel_dp_link_caps_debugfs_add(connector);
intel_link_bw_connector_debugfs_add(connector);
if (DISPLAY_VER(display) >= 11 &&

View File

@@ -101,6 +101,8 @@ static const struct intel_display_device_info no_display = {};
#define TRANSCODER_EDP_OFFSET 0x6f000
#define TRANSCODER_DSI0_OFFSET 0x6b000
#define TRANSCODER_DSI1_OFFSET 0x6b800
#define TRANSCODER_CMTG0_OFFSET 0x6F000
#define TRANSCODER_CMTG1_OFFSET 0x6F100
#define CURSOR_A_OFFSET 0x70080
#define CURSOR_B_OFFSET 0x700c0
@@ -1352,6 +1354,18 @@ static const struct intel_display_device_info xe2_lpd_display = {
BIT(INTEL_FBC_A) | BIT(INTEL_FBC_B) |
BIT(INTEL_FBC_C) | BIT(INTEL_FBC_D),
.__runtime_defaults.has_dbuf_overlap_detection = true,
.trans_offsets = {
[TRANSCODER_A] = TRANSCODER_A_OFFSET,
[TRANSCODER_B] = TRANSCODER_B_OFFSET,
[TRANSCODER_C] = TRANSCODER_C_OFFSET,
[TRANSCODER_D] = TRANSCODER_D_OFFSET,
[TRANSCODER_CMTG0] = TRANSCODER_CMTG0_OFFSET,
[TRANSCODER_CMTG1] = TRANSCODER_CMTG1_OFFSET,
},
.__runtime_defaults.cpu_transcoder_mask =
BIT(TRANSCODER_A) | BIT(TRANSCODER_B) |
BIT(TRANSCODER_C) | BIT(TRANSCODER_D) |
BIT(TRANSCODER_CMTG0) | BIT(TRANSCODER_CMTG1),
};
static const struct intel_display_device_info wcl_display = {

View File

@@ -159,6 +159,7 @@ struct intel_display_platforms {
#define HAS_CUR_FBC(__display) (!HAS_GMCH(__display) && IS_DISPLAY_VER(__display, 7, 13))
#define HAS_D12_PLANE_MINIMIZATION(__display) ((__display)->platform.rocketlake || (__display)->platform.alderlake_s)
#define HAS_DBUF_OVERLAP_DETECTION(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dbuf_overlap_detection)
#define HAS_DC3CO(__display) (DISPLAY_VER(__display) >= 35)
#define HAS_DDI(__display) (DISPLAY_INFO(__display)->has_ddi)
#define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0)
#define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc)
@@ -292,7 +293,7 @@ struct intel_display_runtime_info {
u32 rawclk_freq;
u8 pipe_mask;
u8 cpu_transcoder_mask;
u16 cpu_transcoder_mask;
u16 port_mask;
u8 num_sprites[I915_MAX_PIPES];

View File

@@ -43,6 +43,8 @@
#include "intel_dp_tunnel.h"
#include "intel_dpll.h"
#include "intel_dpll_mgr.h"
#include "intel_dram.h"
#include "intel_encoder.h"
#include "intel_fb.h"
#include "intel_fbc.h"
#include "intel_fbdev.h"
@@ -65,6 +67,8 @@
#include "intel_wm.h"
#include "skl_watermark.h"
static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown);
bool intel_display_driver_probe_defer(struct pci_dev *pdev)
{
struct drm_privacy_screen *privacy_screen;
@@ -200,11 +204,23 @@ int intel_display_driver_probe_noirq(struct intel_display *display)
{
int ret;
intel_opregion_setup(display);
/*
* Fill the dram structure to get the system dram info. This will be
* used for memory latency calculation.
*/
ret = intel_dram_detect(display);
if (ret)
goto cleanup_opregion;
intel_bw_init_hw(display);
if (HAS_DISPLAY(display)) {
ret = drm_vblank_init(display->drm,
INTEL_NUM_PIPES(display));
if (ret)
return ret;
goto cleanup_opregion;
}
intel_bios_init(display);
@@ -303,6 +319,8 @@ int intel_display_driver_probe_noirq(struct intel_display *display)
intel_display_power_driver_remove(display);
cleanup_bios:
intel_bios_driver_remove(display);
cleanup_opregion:
intel_opregion_cleanup(display);
return ret;
}
@@ -604,6 +622,8 @@ void intel_display_driver_remove_noirq(struct intel_display *display)
if (!HAS_DISPLAY(display))
return;
intel_hpd_cancel_work(display);
intel_display_driver_suspend_access(display);
/*
@@ -644,6 +664,8 @@ void intel_display_driver_remove_nogem(struct intel_display *display)
intel_display_power_driver_remove(display);
intel_bios_driver_remove(display);
intel_opregion_cleanup(display);
}
void intel_display_driver_unregister(struct intel_display *display)
@@ -674,34 +696,102 @@ void intel_display_driver_unregister(struct intel_display *display)
intel_vga_unregister(display);
}
void intel_display_driver_shutdown(struct intel_display *display)
{
if (!HAS_DISPLAY(display))
return;
__intel_display_driver_pm_suspend(display, true);
intel_encoder_shutdown_all(display);
}
void intel_display_driver_shutdown_late(struct intel_display *display)
{
if (!HAS_DISPLAY(display))
return;
/*
* The only requirement is to reboot with display DC states disabled,
* for now leaving all display power wells in the INIT power domain
* enabled.
*/
intel_display_power_driver_remove(display);
}
/*
* turn all crtc's off, but do not adjust state
* This has to be paired with a call to intel_modeset_setup_hw_state.
*/
int intel_display_driver_suspend(struct intel_display *display)
static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown)
{
struct drm_atomic_commit *state;
int ret;
int ret = 0;
if (!HAS_DISPLAY(display))
return 0;
state = drm_atomic_helper_suspend(display->drm);
ret = PTR_ERR_OR_ZERO(state);
if (ret)
drm_err(display->drm, "Suspending crtc's failed with %i\n",
ret);
else
display->restore.modeset_state = state;
/*
* We do a lot of poking in a lot of registers, make sure they work
* properly.
*/
intel_display_power_disable(display);
drm_client_dev_suspend(display->drm);
drm_kms_helper_poll_disable(display->drm);
intel_display_driver_disable_user_access(display);
if (shutdown) {
drm_atomic_helper_shutdown(display->drm);
} else {
struct drm_atomic_commit *state;
state = drm_atomic_helper_suspend(display->drm);
ret = PTR_ERR_OR_ZERO(state);
if (ret)
drm_err(display->drm, "Suspending crtc's failed with %i\n",
ret);
else
display->restore.modeset_state = state;
}
/* ensure all DPT VMAs have been unpinned for intel_dpt_suspend() */
flush_workqueue(display->wq.cleanup);
intel_dp_mst_suspend(display);
intel_encoder_block_all_hpds(display);
intel_hpd_cancel_work(display);
intel_display_driver_suspend_access(display);
intel_encoder_suspend_all(display);
return ret;
}
int intel_display_driver_pm_suspend(struct intel_display *display)
{
return __intel_display_driver_pm_suspend(display, false);
}
void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle)
{
if (!HAS_DISPLAY(display))
return;
intel_display_power_suspend_late(display, s2idle);
}
void intel_display_driver_pm_resume_early(struct intel_display *display)
{
if (!HAS_DISPLAY(display))
return;
intel_display_power_resume_early(display);
}
int
__intel_display_driver_resume(struct intel_display *display,
struct drm_atomic_commit *state,
@@ -741,7 +831,7 @@ __intel_display_driver_resume(struct intel_display *display,
return ret;
}
void intel_display_driver_resume(struct intel_display *display)
void intel_display_driver_pm_resume(struct intel_display *display)
{
struct drm_atomic_commit *state = display->restore.modeset_state;
struct drm_modeset_acquire_ctx ctx;
@@ -750,6 +840,12 @@ void intel_display_driver_resume(struct intel_display *display)
if (!HAS_DISPLAY(display))
return;
intel_display_driver_resume_access(display);
intel_hpd_init(display);
intel_encoder_unblock_all_hpds(display);
/* MST sideband requires HPD interrupts enabled */
intel_dp_mst_resume(display);
@@ -779,4 +875,15 @@ void intel_display_driver_resume(struct intel_display *display)
"Restoring old state failed with %i\n", ret);
if (state)
drm_atomic_commit_put(state);
intel_display_driver_enable_user_access(display);
drm_kms_helper_poll_enable(display->drm);
intel_hpd_poll_disable(display);
intel_opregion_resume(display);
drm_client_dev_resume(display->drm);
intel_display_power_enable(display);
}

View File

@@ -24,8 +24,13 @@ void intel_display_driver_remove(struct intel_display *display);
void intel_display_driver_remove_noirq(struct intel_display *display);
void intel_display_driver_remove_nogem(struct intel_display *display);
void intel_display_driver_unregister(struct intel_display *display);
int intel_display_driver_suspend(struct intel_display *display);
void intel_display_driver_resume(struct intel_display *display);
void intel_display_driver_shutdown(struct intel_display *display);
void intel_display_driver_shutdown_late(struct intel_display *display);
int intel_display_driver_pm_suspend(struct intel_display *display);
void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle);
void intel_display_driver_pm_resume_early(struct intel_display *display);
void intel_display_driver_pm_resume(struct intel_display *display);
/* interface for intel_display_reset.c */
int __intel_display_driver_resume(struct intel_display *display,

View File

@@ -1469,6 +1469,18 @@ static void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl)
found = true;
}
if (DISPLAY_VER(display) == 35) {
if (iir & CMTG_VBLANK_A) {
intel_handle_vblank(display, PIPE_A);
found = true;
}
if (iir & CMTG_VBLANK_B) {
intel_handle_vblank(display, PIPE_B);
found = true;
}
}
if (DISPLAY_VER(display) >= 11) {
u32 te_trigger = iir & (DSI0_TE | DSI1_TE);
@@ -2666,3 +2678,10 @@ void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *s
drm_printf(p, "DERRMR: 0x%08x\n", snapshot->derrmr);
drm_printf(p, "ERR_INT: 0x%08x\n", snapshot->err_int);
}
void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask)
{
spin_lock_irq(&display->irq.lock);
bdw_update_port_irq(display, bits, mask ? 0 : bits);
spin_unlock_irq(&display->irq.lock);
}

View File

@@ -82,4 +82,6 @@ void i915gm_irq_cstate_wa(struct intel_display *display, bool enable);
struct intel_display_irq_snapshot *intel_display_irq_snapshot_capture(struct intel_display *display);
void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *snapshot, struct drm_printer *p);
void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask);
#endif /* __INTEL_DISPLAY_IRQ_H__ */

View File

@@ -45,6 +45,8 @@ enum transcoder {
TRANSCODER_DSI_1,
TRANSCODER_DSI_A = TRANSCODER_DSI_0, /* legacy DSI */
TRANSCODER_DSI_C = TRANSCODER_DSI_1, /* legacy DSI */
TRANSCODER_CMTG0,
TRANSCODER_CMTG1,
I915_MAX_TRANSCODERS
};

View File

@@ -14,7 +14,9 @@
#include "intel_cdclk.h"
#include "intel_clock_gating.h"
#include "intel_combo_phy.h"
#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display.h"
#include "intel_display_power.h"
#include "intel_display_power_map.h"
#include "intel_display_power_well.h"
@@ -30,6 +32,8 @@
#include "intel_pch_refclk.h"
#include "intel_pmdemand.h"
#include "intel_pps_regs.h"
#include "intel_psr.h"
#include "intel_psr_regs.h"
#include "intel_snps_phy.h"
#include "skl_watermark.h"
#include "skl_watermark_regs.h"
@@ -267,7 +271,7 @@ sanitize_target_dc_state(struct intel_display *display,
static const u32 states[] = {
DC_STATE_EN_UPTO_DC6,
DC_STATE_EN_UPTO_DC5,
DC_STATE_EN_DC3CO,
DC_STATE_EN_UPTO_DC3CO,
DC_STATE_DISABLE,
};
int i;
@@ -285,6 +289,19 @@ sanitize_target_dc_state(struct intel_display *display,
return target_dc_state;
}
bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display)
{
struct i915_power_domains *power_domains = &display->power.domains;
bool ret;
mutex_lock(&power_domains->lock);
ret = power_domains->dc3co_to_dc6;
power_domains->dc3co_to_dc6 = false;
mutex_unlock(&power_domains->lock);
return ret;
}
/**
* intel_display_power_set_target_dc_state - Set target dc state.
* @display: display device
@@ -300,6 +317,7 @@ void intel_display_power_set_target_dc_state(struct intel_display *display,
struct i915_power_well *power_well;
bool dc_off_enabled;
struct i915_power_domains *power_domains = &display->power.domains;
u32 old_target_dc_state;
mutex_lock(&power_domains->lock);
power_well = lookup_power_well(display, SKL_DISP_DC_OFF);
@@ -320,8 +338,17 @@ void intel_display_power_set_target_dc_state(struct intel_display *display,
if (!dc_off_enabled)
intel_power_well_enable(display, power_well);
old_target_dc_state = power_domains->target_dc_state;
power_domains->target_dc_state = state;
/*
* CMTG must be restored explicitly after DC6 exit. The dc3co_to_dc6
* flag helps CMTG determine whether restoration is required.
*/
if (old_target_dc_state == DC_STATE_EN_UPTO_DC3CO &&
power_domains->target_dc_state == DC_STATE_EN_UPTO_DC6)
power_domains->dc3co_to_dc6 = true;
if (!dc_off_enabled)
intel_power_well_disable(display, power_well);
@@ -358,6 +385,144 @@ u32 intel_display_power_get_current_dc_state(struct intel_display *display)
return current_dc_state;
}
bool intel_display_power_dc3co_supported(struct intel_display *display)
{
struct i915_power_domains *power_domains = &display->power.domains;
if (!HAS_DC3CO(display))
return false;
return (power_domains->allowed_dc_mask & DC_STATE_EN_UPTO_DC3CO) == DC_STATE_EN_UPTO_DC3CO;
}
bool intel_display_power_dc3co_allowed(struct intel_display *display)
{
struct intel_dc3co_state *dc3co = &display->power.dc3co;
bool allowed;
if (!intel_display_power_dc3co_supported(display))
return false;
mutex_lock(&dc3co->lock);
allowed = dc3co->allowed;
mutex_unlock(&dc3co->lock);
return allowed;
}
void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger)
{
struct intel_dc3co_state *dc3co = &display->power.dc3co;
if (!intel_display_power_dc3co_supported(display))
return;
mutex_lock(&dc3co->lock);
dc3co->trigger = trigger;
dc3co->allowed = !!trigger;
mutex_unlock(&dc3co->lock);
}
static bool intel_dc3co_port_pipe_compatible(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
{
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
enum port port = dig_port->base.port;
int num_pipes = intel_crtc_num_joined_pipes(crtc_state);
/* Need to follow 1:1 mapping because of CMTG restriction */
if (DISPLAY_VER(to_intel_display(crtc_state)) == 35)
return num_pipes == 1 &&
((pipe == PIPE_A && port == PORT_A) ||
(pipe == PIPE_B && port == PORT_B));
else
return num_pipes == 1 && pipe <= PIPE_B && port <= PORT_B;
}
void intel_display_power_dc3co_compute(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
struct intel_crtc *crtc;
struct intel_crtc_state *crtc_state;
struct intel_encoder *encoder;
struct intel_dp *intel_dp;
u8 active_pipes = 0;
enum pipe pipe;
u32 trigger = DC3CO_TRIGGER_NONE;
if (!intel_display_power_dc3co_supported(display))
return;
for_each_intel_crtc(display, crtc)
active_pipes |= crtc->active ? BIT(crtc->pipe) : 0;
active_pipes = intel_calc_active_pipes(state, active_pipes);
if (hweight8(active_pipes) != 1)
goto done;
pipe = ffs(active_pipes) - 1;
crtc = intel_crtc_for_pipe(display, pipe);
crtc_state = to_intel_crtc_state(crtc->base.state);
for_each_intel_encoder_mask(display->drm, encoder,
crtc_state->uapi.encoder_mask) {
if (encoder->type != INTEL_OUTPUT_EDP)
goto done;
intel_dp = enc_to_intel_dp(encoder);
if (!intel_dc3co_port_pipe_compatible(intel_dp, crtc_state))
goto done;
if (intel_psr2_in_deep_sleep(intel_dp))
goto done;
}
if (crtc_state->has_lobf)
trigger |= DC3CO_TRIGGER_LOBF;
if (crtc_state->has_panel_replay && intel_dp->as_sdp_supported)
trigger |= DC3CO_TRIGGER_PANEL_REPLAY;
if (crtc_state->has_sel_update)
trigger |= DC3CO_TRIGGER_PSR2;
done:
intel_display_power_dc3co_update(display, trigger);
}
/*
* Select the target DC state for this commit and return the async-put delay
* to use when releasing the DC_OFF reference.
*
* Picks DC_STATE_EN_UPTO_DC3CO when DC3CO can be enabled
* otherwise falls back to default DC state of DC_STATE_EN_UPTO_DC6.
* The chosen target is programmed via intel_display_power_set_target_dc_state().
*
* Returns the async-put delay (in ms) to use when releasing the DC_OFF
* reference: DC3CO_PUT_ASYNC_DELAY_MS when DC3CO was selected, otherwise
* DC6_PUT_ASYNC_DELAY_MS.
*/
int intel_display_power_select_target_dc_state(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
u32 target_dc_state;
if (!intel_display_power_dc3co_supported(display))
return DC6_PUT_ASYNC_DELAY_MS;
if (intel_display_power_dc3co_allowed(display))
target_dc_state = DC_STATE_EN_UPTO_DC3CO;
else
target_dc_state = DC_STATE_EN_UPTO_DC6;
intel_display_power_set_target_dc_state(display, target_dc_state);
return target_dc_state == DC_STATE_EN_UPTO_DC3CO ?
DC3CO_PUT_ASYNC_DELAY_MS : DC6_PUT_ASYNC_DELAY_MS;
}
static void __async_put_domains_mask(struct i915_power_domains *power_domains,
struct intel_power_domain_mask *mask)
{
@@ -956,7 +1121,9 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc)
if (!HAS_DISPLAY(display))
return 0;
if (DISPLAY_VER(display) >= 20)
if (DISPLAY_VER(display) >= 35)
max_dc = 4;
else if (DISPLAY_VER(display) >= 20)
max_dc = 2;
else if (display->platform.dg2)
max_dc = 1;
@@ -999,10 +1166,10 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc)
switch (requested_dc) {
case 4:
mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6;
mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6;
break;
case 3:
mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC5;
mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC5;
break;
case 2:
mask |= DC_STATE_EN_UPTO_DC6;
@@ -1037,6 +1204,7 @@ int intel_display_power_init(struct intel_display *display)
sanitize_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
mutex_init(&power_domains->lock);
mutex_init(&display->power.dc3co.lock);
INIT_DELAYED_WORK(&power_domains->async_put_work,
intel_display_power_put_async_work);
@@ -1634,9 +1802,12 @@ static void tgl_bw_buddy_init(struct intel_display *display)
if (table[config].page_mask == 0) {
drm_dbg_kms(display->drm,
"Unknown memory configuration; disabling address buddy logic.\n");
for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask))
intel_de_write(display, BW_BUDDY_CTL(i),
BW_BUDDY_DISABLE);
if (DISPLAY_VER(display) < 20) {
for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask))
intel_de_write(display, BW_BUDDY_CTL(i),
BW_BUDDY_DISABLE);
}
} else {
for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) {
intel_de_write(display, BW_BUDDY_PAGE_MASK(i),

View File

@@ -9,9 +9,12 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include "intel_display_limits.h"
enum aux_ch;
enum port;
struct i915_power_well;
struct intel_atomic_state;
struct intel_display;
struct intel_encoder;
struct ref_tracker;
@@ -131,6 +134,36 @@ struct intel_power_domain_mask {
DECLARE_BITMAP(bits, POWER_DOMAIN_NUM);
};
/*
* DC3CO enabling triggers (bitmask).
* DC3CO may be enabled when at least one of these triggers is active.
* Additional constraints may still apply.
*/
#define DC3CO_TRIGGER_NONE (0)
#define DC3CO_TRIGGER_PSR2 BIT(0)
#define DC3CO_TRIGGER_LOBF BIT(1)
#define DC3CO_TRIGGER_PANEL_REPLAY BIT(2)
#define DC3CO_TRIGGER_ALL (DC3CO_TRIGGER_PSR2 | \
DC3CO_TRIGGER_LOBF | \
DC3CO_TRIGGER_PANEL_REPLAY)
/*
* Delay to re-enable DC5/DC6 states by 17 ms to avoid the off->on->off
* toggling overhead at and above 60 FPS.
*/
#define DC6_PUT_ASYNC_DELAY_MS 17
/*
* Use minimal re-enable delay to allow DC3CO entry on
* the next idle frame.
*/
#define DC3CO_PUT_ASYNC_DELAY_MS 1
struct intel_dc3co_state {
struct mutex lock; /* protects allowed and trigger fields */
bool allowed; /* DC3CO compute result */
u32 trigger; /* Bitmask of active DC3CO triggers */
};
struct i915_power_domains {
/*
* Power wells needed for initialization at driver init and suspend
@@ -138,6 +171,7 @@ struct i915_power_domains {
*/
bool initializing;
bool display_core_suspended;
bool dc3co_to_dc6;
int power_well_count;
u32 dc_state;
@@ -179,9 +213,15 @@ void intel_display_power_sanitize_state(struct intel_display *display);
void intel_display_power_suspend_late(struct intel_display *display, bool s2idle);
void intel_display_power_resume_early(struct intel_display *display);
bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display);
void intel_display_power_set_target_dc_state(struct intel_display *display,
u32 state);
u32 intel_display_power_get_current_dc_state(struct intel_display *display);
bool intel_display_power_dc3co_supported(struct intel_display *display);
void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger);
bool intel_display_power_dc3co_allowed(struct intel_display *display);
void intel_display_power_dc3co_compute(struct intel_atomic_state *state);
int intel_display_power_select_target_dc_state(struct intel_atomic_state *state);
void intel_display_power_runtime_suspend(struct intel_display *display);
void intel_display_power_runtime_resume(struct intel_display *display);

View File

@@ -726,12 +726,28 @@ static void assert_can_disable_dc9(struct intel_display *display)
*/
}
static u32 dc_state_ro_mask(struct intel_display *display)
{
if (DISPLAY_VER(display) >= 20)
return DC_STATE_EN_CSR_MASK_CMTG_1 | DC_STATE_EN_CSR_MASK_CMTG_0;
else if (DISPLAY_VER(display) >= 13 && !display->platform.dg2)
return DC_STATE_EN_CSR_MASK_CMTG_0;
return 0;
}
static void gen9_write_dc_state(struct intel_display *display,
u32 state)
{
int rewrites = 0;
int rereads = 0;
u32 v;
/*
* Mask out RO status bits from read-back comparison.
* HW may set these bits independently, so exclude them
* to prevent the verify loop from retrying due to RO bits mismatch.
*/
u32 ro_mask = dc_state_ro_mask(display);
intel_de_write(display, DC_STATE_EN, state);
@@ -743,7 +759,7 @@ static void gen9_write_dc_state(struct intel_display *display,
do {
v = intel_de_read(display, DC_STATE_EN);
if (v != state) {
if ((v & ~ro_mask) != (state & ~ro_mask)) {
intel_de_write(display, DC_STATE_EN, state);
rewrites++;
rereads = 0;
@@ -753,10 +769,10 @@ static void gen9_write_dc_state(struct intel_display *display,
} while (rewrites < 100);
if (v != state)
if ((v & ~ro_mask) != (state & ~ro_mask))
drm_err(display->drm,
"Writing dc state to 0x%x failed, now 0x%x\n",
state, v);
"Writing dc state to 0x%x failed, now 0x%x (ro_mask=0x%x)\n",
state, v, ro_mask);
/* Most of the times we need one retry, avoid spam */
if (rewrites > 1)
@@ -772,7 +788,7 @@ static u32 gen9_dc_mask(struct intel_display *display)
mask = DC_STATE_EN_UPTO_DC5;
if (DISPLAY_VER(display) >= 12)
mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6
mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6
| DC_STATE_EN_DC9;
else if (DISPLAY_VER(display) == 11)
mask |= DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9;
@@ -866,21 +882,30 @@ void gen9_set_dc_state(struct intel_display *display, u32 state)
power_domains->dc_state = val & mask;
}
static void tgl_enable_dc3co(struct intel_display *display)
void xe3lpd_enable_dc_count(struct intel_display *display)
{
drm_dbg_kms(display->drm, "Enabling DC3CO\n");
gen9_set_dc_state(display, DC_STATE_EN_DC3CO);
if (DISPLAY_VER(display) < 35)
return;
intel_de_write(display, DC_COUNT_EN, DC_COUNT_EN_COUNTER_ENABLE);
}
static void tgl_disable_dc3co(struct intel_display *display)
static void assert_can_enable_dc3co(struct intel_display *display)
{
drm_dbg_kms(display->drm, "Disabling DC3CO\n");
intel_de_rmw(display, DC_STATE_EN, DC_STATE_DC3CO_STATUS, 0);
gen9_set_dc_state(display, DC_STATE_DISABLE);
/*
* Delay of 200us DC3CO Exit time B.Spec 49196
*/
usleep_range(200, 210);
drm_WARN_ONCE(display->drm,
(intel_de_read(display, DC_STATE_EN) &
DC_STATE_EN_UPTO_DC3CO),
"DC3CO already programmed to be enabled.\n");
assert_main_dmc_loaded(display);
}
static void xe3lpd_enable_dc3co(struct intel_display *display)
{
assert_can_enable_dc3co(display);
drm_dbg_kms(display->drm, "Enabling DC3CO\n");
intel_dmc_wl_enable(display, DC_STATE_EN_UPTO_DC3CO);
gen9_set_dc_state(display, DC_STATE_EN_UPTO_DC3CO);
}
static void assert_can_enable_dc5(struct intel_display *display)
@@ -1039,8 +1064,8 @@ static void bxt_verify_dpio_phy_power_wells(struct intel_display *display)
static bool gen9_dc_off_power_well_enabled(struct intel_display *display,
struct i915_power_well *power_well)
{
return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_DC3CO) == 0 &&
(intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0);
return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO) == 0 &&
(intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK) == 0);
}
static void gen9_assert_dbuf_enabled(struct intel_display *display)
@@ -1061,11 +1086,6 @@ void gen9_disable_dc_states(struct intel_display *display)
struct intel_cdclk_config cdclk_config = {};
u32 old_state = power_domains->dc_state;
if (power_domains->target_dc_state == DC_STATE_EN_DC3CO) {
tgl_disable_dc3co(display);
return;
}
if (HAS_DISPLAY(display)) {
intel_dmc_wl_get_noreg(display);
gen9_set_dc_state(display, DC_STATE_DISABLE);
@@ -1076,9 +1096,13 @@ void gen9_disable_dc_states(struct intel_display *display)
}
if (old_state == DC_STATE_EN_UPTO_DC5 ||
old_state == DC_STATE_EN_UPTO_DC6)
old_state == DC_STATE_EN_UPTO_DC6 ||
old_state == DC_STATE_EN_UPTO_DC3CO)
intel_dmc_wl_disable(display);
if (old_state == DC_STATE_EN_UPTO_DC3CO)
return;
intel_cdclk_get_cdclk(display, &cdclk_config);
/* Can't read out voltage_level so can't use intel_cdclk_changed() */
drm_WARN_ON(display->drm,
@@ -1114,8 +1138,8 @@ static void gen9_dc_off_power_well_disable(struct intel_display *display,
return;
switch (power_domains->target_dc_state) {
case DC_STATE_EN_DC3CO:
tgl_enable_dc3co(display);
case DC_STATE_EN_UPTO_DC3CO:
xe3lpd_enable_dc3co(display);
break;
case DC_STATE_EN_UPTO_DC6:
skl_enable_dc6(display);

View File

@@ -159,6 +159,7 @@ void gen9_set_dc_state(struct intel_display *display, u32 state);
void gen9_disable_dc_states(struct intel_display *display);
void bxt_enable_dc9(struct intel_display *display);
void bxt_disable_dc9(struct intel_display *display);
void xe3lpd_enable_dc_count(struct intel_display *display);
extern const struct i915_power_well_ops i9xx_always_on_power_well_ops;
extern const struct i915_power_well_ops chv_pipe_power_well_ops;

View File

@@ -1458,6 +1458,8 @@
#define GEN9_AUX_CHANNEL_B (1 << 25)
#define DSI1_TE (1 << 24)
#define DSI0_TE (1 << 23)
#define CMTG_VBLANK_B (1 << 17)
#define CMTG_VBLANK_A (1 << 14)
#define GEN8_DE_PORT_HOTPLUG(hpd_pin) REG_BIT(3 + _HPD_PIN_DDI(hpd_pin))
#define BXT_DE_PORT_HOTPLUG_MASK (GEN8_DE_PORT_HOTPLUG(HPD_PORT_A) | \
GEN8_DE_PORT_HOTPLUG(HPD_PORT_B) | \
@@ -2767,28 +2769,28 @@ enum skl_power_gate {
*/
/* CDCLK_CTL */
#define CDCLK_CTL _MMIO(0x46000)
#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26)
#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) /* skl */
#define CDCLK_FREQ_450_432 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 0)
#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1)
#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1)
#define CDCLK_FREQ_337_308 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 2)
#define CDCLK_FREQ_675_617 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 3)
#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25)
#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) /* lnl+ */
#define MDCLK_SOURCE_SEL_CD2XCLK REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 0)
#define MDCLK_SOURCE_SEL_CDCLK_PLL REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 1)
#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22)
#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) /* bxt+ */
#define BXT_CDCLK_CD2X_DIV_SEL_1 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 0)
#define BXT_CDCLK_CD2X_DIV_SEL_1_5 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 1)
#define BXT_CDCLK_CD2X_DIV_SEL_2 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 2)
#define BXT_CDCLK_CD2X_DIV_SEL_4 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 3)
#define BXT_CDCLK_CD2X_PIPE(pipe) ((pipe) << 20)
#define CDCLK_DIVMUX_CD_OVERRIDE (1 << 19)
#define BXT_CDCLK_CD2X_PIPE_NONE BXT_CDCLK_CD2X_PIPE(3)
#define ICL_CDCLK_CD2X_PIPE(pipe) (_PICK(pipe, 0, 2, 6) << 19)
#define ICL_CDCLK_CD2X_PIPE_NONE (7 << 19)
#define TGL_CDCLK_CD2X_PIPE(pipe) BXT_CDCLK_CD2X_PIPE(pipe)
#define TGL_CDCLK_CD2X_PIPE_NONE ICL_CDCLK_CD2X_PIPE_NONE
#define BXT_CDCLK_SSA_PRECHARGE_ENABLE (1 << 16)
#define CDCLK_FREQ_DECIMAL_MASK (0x7ff)
#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(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 */
#define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) /* bxt/glk */
#define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) /* pre-lnl */
/* CDCLK_SQUASH_CTL */
#define CDCLK_SQUASH_CTL _MMIO(0x46008)
@@ -3070,19 +3072,29 @@ enum skl_power_gate {
/* GEN9 DC */
#define DC_STATE_EN _MMIO(0x45504)
#define DC_STATE_DISABLE 0
#define DC_STATE_EN_DC3CO REG_BIT(30)
#define DC_STATE_DC3CO_STATUS REG_BIT(29)
#define HOLD_PHY_CLKREQ_PG1_LATCH REG_BIT(21)
#define HOLD_PHY_PG1_LATCH REG_BIT(20)
#define DC_STATE_EN_UPTO_DC5 (1 << 0)
#define DC_STATE_EN_DC9 (1 << 3)
#define DC_STATE_EN_UPTO_DC6 (2 << 0)
#define DC_STATE_EN_UPTO_DC5_DC6_MASK 0x3
#define DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK REG_GENMASK(1, 0)
#define DC_STATE_EN_DISABLE REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 0)
#define DC_STATE_EN_UPTO_DC5 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 1)
#define DC_STATE_EN_UPTO_DC6 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 2)
#define DC_STATE_EN_UPTO_DC3CO REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 3)
/* display version 20+ */
#define DC_STATE_EN_CSR_MASK_CMTG_1 REG_BIT(11)
/* display version 13+, except dg2 */
#define DC_STATE_EN_CSR_MASK_CMTG_0 REG_BIT(10)
#define DC_STATE_DEBUG _MMIO(0x45520)
#define DC_STATE_DEBUG_MASK_CORES (1 << 0)
#define DC_STATE_DEBUG_MASK_MEMORY_UP (1 << 1)
#define DC_COUNT_EN _MMIO(0x457B4)
#define DC_COUNT_EN_COUNTER_ENABLE REG_BIT(31)
#define DC_STATE_DC3CO_RESIDENCY _MMIO(0x457B8)
#define D_COMP_BDW _MMIO(0x138144)
/* Pipe WM_LINETIME - watermark line time */

View File

@@ -58,6 +58,8 @@ struct cec_notifier;
struct drm_printer;
struct intel_connector;
struct intel_ddi_buf_trans;
struct intel_dp_link_caps;
struct intel_dp_link_training;
struct intel_fbc;
struct intel_global_objs_state;
struct intel_hdcp_shim;
@@ -150,7 +152,6 @@ struct intel_framebuffer {
unsigned int min_alignment;
unsigned int vtd_guard;
unsigned int (*panic_tiling)(unsigned int x, unsigned int y, unsigned int width);
struct intel_panic *panic;
};
@@ -177,10 +178,12 @@ struct intel_encoder {
enum intel_output_type (*compute_output_type)(struct intel_encoder *,
struct intel_crtc_state *,
struct drm_connector_state *);
int (*compute_config)(struct intel_encoder *,
int (*compute_config)(struct intel_atomic_state *,
struct intel_encoder *,
struct intel_crtc_state *,
struct drm_connector_state *);
int (*compute_config_late)(struct intel_encoder *,
int (*compute_config_late)(struct intel_atomic_state *,
struct intel_encoder *,
struct intel_crtc_state *,
struct drm_connector_state *);
void (*pre_pll_enable)(struct intel_atomic_state *,
@@ -1185,7 +1188,6 @@ struct intel_crtc_state {
bool pkg_c_latency_used;
/* Only used for state verification. */
enum intel_panel_replay_dsc_support panel_replay_dsc_support;
u32 dc3co_exitline;
u16 su_y_granularity;
u8 active_non_psr_pipes;
u8 entry_setup_frames;
@@ -1573,6 +1575,10 @@ struct intel_crtc {
#endif
bool vblank_psr_notify;
struct {
bool enabled;
} cmtg;
};
struct intel_plane_error {
@@ -1772,14 +1778,16 @@ struct intel_psr {
ktime_t last_exit;
bool sink_not_reliable;
bool irq_aux_error;
/* DC3CO allowed used to control PSR configuration */
bool dc3co_allowed;
/* DC3CO disable work */
struct delayed_work dc3co_work;
u16 su_w_granularity;
u16 su_y_granularity;
bool source_panel_replay_support;
bool sink_panel_replay_support;
bool panel_replay_enabled;
u32 dc3co_exitline;
u32 dc3co_exit_delay;
struct delayed_work dc3co_work;
u8 entry_setup_frames;
u8 io_wake_lines;
@@ -1795,6 +1803,14 @@ struct intel_psr {
struct ref_tracker *vblank_wakeref;
};
struct intel_dp_link_config {
int rate;
int lane_count;
};
#define INTEL_DP_LINK_CONFIG_NULL \
((struct intel_dp_link_config){})
struct intel_dp {
intel_reg_t output_reg;
u32 DP;
@@ -1820,29 +1836,9 @@ struct intel_dp {
bool use_rate_select;
/* Max sink lane count as reported by DP_MAX_LANE_COUNT */
int max_sink_lane_count;
/* intersection of source and sink rates */
int num_common_rates;
int common_rates[DP_MAX_SUPPORTED_RATES];
int max_common_lane_count;
struct {
/* TODO: move the rest of link specific fields to here */
bool active;
/* common rate,lane_count configs in bw order */
int num_configs;
#define INTEL_DP_MAX_LANE_COUNT 4
#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1)
#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS)
#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \
INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
struct intel_dp_link_config {
u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS;
u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS;
} configs[INTEL_DP_MAX_LINK_CONFIGS];
/* Max lane count for the current link */
int max_lane_count;
/* Max rate for the current link */
int max_rate;
/*
* Link parameters for which the MST topology was probed.
* Tracking these ensures that the MST path resources are
@@ -1851,13 +1847,8 @@ struct intel_dp {
*/
int mst_probed_lane_count;
int mst_probed_rate;
int force_lane_count;
int force_rate;
bool retrain_disabled;
/* Sequential link training failures after a passing LT */
int seq_train_failures;
int force_train_failure;
bool force_retrain;
struct intel_dp_link_training *training;
struct intel_dp_link_caps *caps;
} link;
bool reset_link_params;
int mso_link_count;

View File

@@ -941,6 +941,8 @@ void intel_dmc_load_program(struct intel_display *display)
gen9_set_dc_state_debugmask(display);
xe3lpd_enable_dc_count(display);
pipedmc_clock_gating_wa(display, false);
}
@@ -1647,19 +1649,20 @@ static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused)
DMC_VERSION_MINOR(dmc->version));
if (DISPLAY_VER(display) >= 12) {
intel_reg_t dc3co_reg;
if (DISPLAY_VER(display) >= 35) {
dc5_reg = DG1_DMC_DEBUG_DC5_COUNT;
seq_printf(m, "DC3CO count: %d\n",
intel_de_read(display, XE3P_DMC_DC3CO_COUNT));
if (display->platform.dgfx || DISPLAY_VER(display) >= 14) {
dc3co_reg = DG1_DMC_DEBUG3;
seq_printf(m, "DC3CO residency: %d\n",
intel_de_read(display, DC_STATE_DC3CO_RESIDENCY));
} else if (display->platform.dgfx || DISPLAY_VER(display) >= 14) {
dc5_reg = DG1_DMC_DEBUG_DC5_COUNT;
} else {
dc3co_reg = TGL_DMC_DEBUG3;
dc5_reg = TGL_DMC_DEBUG_DC5_COUNT;
dc6_reg = TGL_DMC_DEBUG_DC6_COUNT;
}
seq_printf(m, "DC3CO count: %d\n",
intel_de_read(display, dc3co_reg));
} else {
dc5_reg = display->platform.broxton ? BXT_DMC_DC3_DC5_COUNT :
SKL_DMC_DC3_DC5_COUNT;

View File

@@ -531,6 +531,8 @@ enum pipedmc_event_id {
#define TGL_DMC_DEBUG3 _MMIO(0x101090)
#define DG1_DMC_DEBUG3 _MMIO(0x13415c)
#define XE3P_DMC_DC3CO_COUNT _MMIO(0x8f05c)
#define DMC_WAKELOCK_CFG _MMIO(0x8F1B0)
#define DMC_WAKELOCK_CFG_ENABLE REG_BIT(31)
#define DMC_WAKELOCK1_CTL _MMIO(0x8F140)

View File

@@ -267,7 +267,7 @@ static bool intel_dmc_wl_check_range(struct intel_display *display,
* the DMC and requires a DC exit for proper access.
*/
switch (dc_state) {
case DC_STATE_EN_DC3CO:
case DC_STATE_EN_UPTO_DC3CO:
ranges = xe3lpd_dc3co_dmc_ranges;
break;
case DC_STATE_EN_UPTO_DC5:

View File

@@ -33,7 +33,6 @@
#include <linux/notifier.h>
#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/string_helpers.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
@@ -71,6 +70,7 @@
#include "intel_dp.h"
#include "intel_dp_aux.h"
#include "intel_dp_hdcp.h"
#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
@@ -313,7 +313,7 @@ static void intel_dp_set_max_sink_lane_count(struct intel_dp *intel_dp)
}
/* Get length of rates array potentially limited by max_rate. */
static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate)
int intel_dp_rate_limit_len(const int *rates, int len, int max_rate)
{
int i;
@@ -326,31 +326,6 @@ static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate)
return 0;
}
/* Get length of common rates array potentially limited by max_rate. */
static int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp,
int max_rate)
{
return intel_dp_rate_limit_len(intel_dp->common_rates,
intel_dp->num_common_rates, max_rate);
}
int intel_dp_common_rate(struct intel_dp *intel_dp, int index)
{
struct intel_display *display = to_intel_display(intel_dp);
if (drm_WARN_ON(display->drm,
index < 0 || index >= intel_dp->num_common_rates))
return 162000;
return intel_dp->common_rates[index];
}
/* Theoretical max between source and sink */
int intel_dp_max_common_rate(struct intel_dp *intel_dp)
{
return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1);
}
int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port)
{
int vbt_max_lanes = intel_bios_dp_max_lane_count(dig_port->base.devdata);
@@ -364,43 +339,35 @@ int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port)
/*
* Theoretical max between source and sink.
* Return %true if the max common lane count changed.
*/
static bool intel_dp_set_max_common_lane_count(struct intel_dp *intel_dp)
static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp)
{
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
int source_max = intel_dp_max_source_lane_count(dig_port);
int sink_max = intel_dp->max_sink_lane_count;
int lane_max = intel_tc_port_max_lane_count(dig_port);
int lttpr_max = drm_dp_lttpr_max_lane_count(intel_dp->lttpr_common_caps);
int old_max_common_lane_count = intel_dp->max_common_lane_count;
if (lttpr_max)
sink_max = min(sink_max, lttpr_max);
intel_dp->max_common_lane_count = min3(source_max, sink_max, lane_max);
return intel_dp->max_common_lane_count != old_max_common_lane_count;
}
int intel_dp_max_common_lane_count(struct intel_dp *intel_dp)
{
return intel_dp->max_common_lane_count;
}
static int forced_lane_count(struct intel_dp *intel_dp)
{
return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp));
return min3(source_max, sink_max, lane_max);
}
int intel_dp_max_lane_count(struct intel_dp *intel_dp)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config max_link_limits;
struct intel_dp_link_config forced_params;
int lane_count;
if (intel_dp->link.force_lane_count)
lane_count = forced_lane_count(intel_dp);
intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits);
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
if (forced_params.lane_count)
lane_count = forced_params.lane_count;
else
lane_count = intel_dp->link.max_lane_count;
lane_count = max_link_limits.lane_count;
switch (lane_count) {
case 1:
@@ -415,8 +382,12 @@ int intel_dp_max_lane_count(struct intel_dp *intel_dp)
static int intel_dp_min_lane_count(struct intel_dp *intel_dp)
{
if (intel_dp->link.force_lane_count)
return forced_lane_count(intel_dp);
struct intel_dp_link_config forced_params;
intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params);
if (forced_params.lane_count)
return forced_params.lane_count;
return 1;
}
@@ -698,174 +669,44 @@ int intel_dp_rate_index(const int *rates, int len, int rate)
return -1;
}
static int intel_dp_link_config_rate(struct intel_dp *intel_dp,
const struct intel_dp_link_config *lc)
{
return intel_dp_common_rate(intel_dp, lc->link_rate_idx);
}
static int intel_dp_link_config_lane_count(const struct intel_dp_link_config *lc)
{
return 1 << lc->lane_count_exp;
}
static int intel_dp_link_config_bw(struct intel_dp *intel_dp,
const struct intel_dp_link_config *lc)
{
return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc),
intel_dp_link_config_lane_count(lc));
}
static int link_config_cmp_by_bw(const void *a, const void *b, const void *p)
{
struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */
const struct intel_dp_link_config *lc_a = a;
const struct intel_dp_link_config *lc_b = b;
int bw_a = intel_dp_link_config_bw(intel_dp, lc_a);
int bw_b = intel_dp_link_config_bw(intel_dp, lc_b);
if (bw_a != bw_b)
return bw_a - bw_b;
return intel_dp_link_config_rate(intel_dp, lc_a) -
intel_dp_link_config_rate(intel_dp, lc_b);
}
static void intel_dp_link_config_init(struct intel_dp *intel_dp)
static void intel_dp_get_common_rates(struct intel_dp *intel_dp,
int common_rates[DP_MAX_SUPPORTED_RATES],
int *num_common_rates)
{
struct intel_display *display = to_intel_display(intel_dp);
struct intel_dp_link_config *lc;
int num_common_lane_configs;
int i;
int j;
if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp))))
return;
num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1;
if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs >
ARRAY_SIZE(intel_dp->link.configs)))
return;
intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs;
lc = &intel_dp->link.configs[0];
for (i = 0; i < intel_dp->num_common_rates; i++) {
for (j = 0; j < num_common_lane_configs; j++) {
lc->lane_count_exp = j;
lc->link_rate_idx = i;
lc++;
}
}
sort_r(intel_dp->link.configs, intel_dp->link.num_configs,
sizeof(intel_dp->link.configs[0]),
link_config_cmp_by_bw, NULL,
intel_dp);
}
void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count)
{
struct intel_display *display = to_intel_display(intel_dp);
const struct intel_dp_link_config *lc;
if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs))
idx = 0;
lc = &intel_dp->link.configs[idx];
*link_rate = intel_dp_link_config_rate(intel_dp, lc);
*lane_count = intel_dp_link_config_lane_count(lc);
}
int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count)
{
int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates,
link_rate);
int lane_count_exp = ilog2(lane_count);
int i;
for (i = 0; i < intel_dp->link.num_configs; i++) {
const struct intel_dp_link_config *lc = &intel_dp->link.configs[i];
if (lc->lane_count_exp == lane_count_exp &&
lc->link_rate_idx == link_rate_idx)
return i;
}
return -1;
}
/* Return %true if the common rates changed. */
static bool intel_dp_set_common_rates(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
int num_old_common_rates = intel_dp->num_common_rates;
int old_common_rates[DP_MAX_SUPPORTED_RATES];
drm_WARN_ON(display->drm,
!intel_dp->num_source_rates || !intel_dp->num_sink_rates);
/* TODO: Add a struct containing both rates and number of rates. */
static_assert(__same_type(old_common_rates[0], intel_dp->common_rates[0]) &&
sizeof(old_common_rates) == sizeof(intel_dp->common_rates));
memcpy(old_common_rates, intel_dp->common_rates,
num_old_common_rates * sizeof(old_common_rates[0]));
intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates,
intel_dp->num_source_rates,
intel_dp->sink_rates,
intel_dp->num_sink_rates,
intel_dp->common_rates);
*num_common_rates = intersect_rates(intel_dp->source_rates,
intel_dp->num_source_rates,
intel_dp->sink_rates,
intel_dp->num_sink_rates,
common_rates);
/* Paranoia, there should always be something in common. */
if (drm_WARN_ON(display->drm, intel_dp->num_common_rates == 0)) {
intel_dp->common_rates[0] = 162000;
intel_dp->num_common_rates = 1;
if (drm_WARN_ON(display->drm, *num_common_rates == 0)) {
common_rates[0] = 162000;
*num_common_rates = 1;
}
return num_old_common_rates != intel_dp->num_common_rates ||
memcmp(old_common_rates, intel_dp->common_rates,
num_old_common_rates * sizeof(old_common_rates[0]));
}
/* Return %true if any common link param changed. */
static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp)
{
int num_common_rates;
int common_rates[DP_MAX_SUPPORTED_RATES];
bool params_changed = false;
if (intel_dp_set_common_rates(intel_dp))
intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates);
if (intel_dp_link_caps_update(intel_dp->link.caps,
common_rates, num_common_rates,
intel_dp_get_max_common_lane_count(intel_dp)))
params_changed = true;
if (intel_dp_set_max_common_lane_count(intel_dp))
params_changed = true;
intel_dp_link_config_init(intel_dp);
return params_changed;
}
bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
u8 lane_count)
{
/*
* FIXME: we need to synchronize the current link parameters with
* hardware readout. Currently fast link training doesn't work on
* boot-up.
*/
if (link_rate == 0 ||
link_rate > intel_dp->link.max_rate)
return false;
if (lane_count == 0 ||
lane_count > intel_dp_max_lane_count(intel_dp))
return false;
return true;
}
u32 intel_dp_mode_to_fec_clock(u32 mode_clock)
{
return div_u64(mul_u32_u32(mode_clock, DP_DSC_FEC_OVERHEAD_FACTOR),
@@ -1693,41 +1534,40 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp)
seq_buf_print_array(&s, intel_dp->sink_rates, intel_dp->num_sink_rates);
drm_dbg_kms(display->drm, "sink rates: %s\n", seq_buf_str(&s));
seq_buf_clear(&s);
seq_buf_print_array(&s, intel_dp->common_rates, intel_dp->num_common_rates);
drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s));
}
static int forced_link_rate(struct intel_dp *intel_dp)
{
int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate);
if (len == 0)
return intel_dp_common_rate(intel_dp, 0);
return intel_dp_common_rate(intel_dp, len - 1);
intel_dp_link_caps_print_common_rates(intel_dp->link.caps);
}
int
intel_dp_max_link_rate(struct intel_dp *intel_dp)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config max_link_limits;
struct intel_dp_link_config forced_params;
int len;
if (intel_dp->link.force_rate)
return forced_link_rate(intel_dp);
intel_dp_link_caps_get_forced_params(link_caps, &forced_params);
len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate);
if (forced_params.rate)
return forced_params.rate;
return intel_dp_common_rate(intel_dp, len - 1);
intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits);
len = intel_dp_common_len_rate_limit(link_caps, max_link_limits.rate);
return intel_dp_common_rate(link_caps, len - 1);
}
static int
intel_dp_min_link_rate(struct intel_dp *intel_dp)
{
if (intel_dp->link.force_rate)
return forced_link_rate(intel_dp);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_dp_link_config forced_params;
return intel_dp_common_rate(intel_dp, 0);
intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params);
if (forced_params.rate)
return forced_params.rate;
return intel_dp_common_rate(link_caps, 0);
}
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
@@ -1911,6 +1751,7 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp,
const struct drm_connector_state *conn_state,
const struct link_config_limits *limits)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state);
int link_rate, link_avail;
@@ -1920,8 +1761,8 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp,
int link_bpp_x16 =
intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp);
for (i = 0; i < intel_dp->num_common_rates; i++) {
link_rate = intel_dp_common_rate(intel_dp, i);
for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) {
link_rate = intel_dp_common_rate(link_caps, i);
if (link_rate < limits->min_rate ||
link_rate > limits->max_rate)
continue;
@@ -2145,12 +1986,13 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp,
const struct link_config_limits *limits,
int dsc_bpp_x16)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
int link_rate, lane_count;
int i;
for (i = 0; i < intel_dp->num_common_rates; i++) {
link_rate = intel_dp_common_rate(intel_dp, i);
for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) {
link_rate = intel_dp_common_rate(link_caps, i);
if (link_rate < limits->min_rate || link_rate > limits->max_rate)
continue;
@@ -3685,19 +3527,19 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state,
}
int
intel_dp_compute_config(struct intel_encoder *encoder,
intel_dp_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state);
struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct intel_connector *connector = intel_dp->attached_connector;
int ret = 0, link_bpp_x16;
if (intel_dp_is_edp(intel_dp)) {
ret = intel_panel_compute_config(connector, adjusted_mode);
ret = intel_panel_compute_config(state, pipe_config, connector);
if (ret)
return ret;
}
@@ -3810,12 +3652,10 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp,
void intel_dp_reset_link_params(struct intel_dp *intel_dp)
{
intel_dp->link.max_lane_count = intel_dp_max_common_lane_count(intel_dp);
intel_dp->link.max_rate = intel_dp_max_common_rate(intel_dp);
intel_dp_link_caps_reset(intel_dp->link.caps);
intel_dp->link.mst_probed_lane_count = 0;
intel_dp->link.mst_probed_rate = 0;
intel_dp->link.retrain_disabled = false;
intel_dp->link.seq_train_failures = 0;
intel_dp_link_training_reset(intel_dp->link.training);
}
/* Enable backlight PWM and backlight PP control. */
@@ -5715,32 +5555,6 @@ void intel_read_dp_sdp(struct intel_encoder *encoder,
}
}
static bool intel_dp_link_ok(struct intel_dp *intel_dp,
u8 link_status[DP_LINK_STATUS_SIZE])
{
struct intel_display *display = to_intel_display(intel_dp);
struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
bool uhbr = intel_dp->link_rate >= 1000000;
bool ok;
if (uhbr)
ok = drm_dp_128b132b_lane_channel_eq_done(link_status,
intel_dp->lane_count);
else
ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count);
if (ok)
return true;
intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status);
drm_dbg_kms(display->drm,
"[ENCODER:%d:%s] %s link not ok, retraining\n",
encoder->base.base.id, encoder->base.name,
uhbr ? "128b/132b" : "8b/10b");
return false;
}
static void
intel_dp_mst_hpd_irq(struct intel_dp *intel_dp, u8 *esi, u8 *ack)
{
@@ -5774,7 +5588,7 @@ static bool
intel_dp_check_mst_status(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
bool force_retrain = intel_dp->link.force_retrain;
bool force_retrain = intel_dp_link_training_get_force_retrain(intel_dp->link.training);
bool reprobe_needed = false;
for (;;) {
@@ -5847,78 +5661,6 @@ intel_dp_handle_hdmi_link_status_change(struct intel_dp *intel_dp)
}
}
static int
intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE])
{
int err;
memset(link_status, 0, DP_LINK_STATUS_SIZE);
if (intel_dp_mst_active_streams(intel_dp) > 0)
err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI,
link_status, DP_LINK_STATUS_SIZE - 2);
else
err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX,
link_status);
if (err)
return err;
if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] &
DP_DOWNSTREAM_PORT_STATUS_CHANGED)
WRITE_ONCE(intel_dp->downstream_port_changed, true);
return 0;
}
static bool
intel_dp_needs_link_retrain(struct intel_dp *intel_dp)
{
u8 link_status[DP_LINK_STATUS_SIZE];
if (!intel_dp->link.active)
return false;
/*
* While PSR source HW is enabled, it will control main-link sending
* frames, enabling and disabling it so trying to do a retrain will fail
* as the link would or not be on or it could mix training patterns
* and frame data at the same time causing retrain to fail.
* Also when exiting PSR, HW will retrain the link anyways fixing
* any link status error.
*/
if (intel_psr_enabled(intel_dp))
return false;
if (intel_dp->link.force_retrain)
return true;
if (intel_dp_read_link_status(intel_dp, link_status) < 0)
return false;
/*
* Validate the cached values of intel_dp->link_rate and
* intel_dp->lane_count before attempting to retrain.
*
* FIXME would be nice to user the crtc state here, but since
* we need to call this from the short HPD handler that seems
* a bit hard.
*/
if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate,
intel_dp->lane_count))
return false;
if (intel_dp->link.retrain_disabled)
return false;
if (intel_dp->link.seq_train_failures)
return true;
/* Retrain if link not ok */
return !intel_dp_link_ok(intel_dp, link_status) &&
!intel_psr_link_ok(intel_dp);
}
bool intel_dp_has_connector(struct intel_dp *intel_dp,
const struct drm_connector_state *conn_state)
{
@@ -6010,86 +5752,6 @@ void intel_dp_flush_connector_commits(struct intel_connector *connector)
wait_for_connector_hw_done(connector->base.state);
}
static bool intel_dp_is_connected(struct intel_dp *intel_dp)
{
struct intel_connector *connector = intel_dp->attached_connector;
return connector->base.status == connector_status_connected ||
intel_dp->is_mst;
}
static int intel_dp_retrain_link(struct intel_encoder *encoder,
struct drm_modeset_acquire_ctx *ctx)
{
struct intel_display *display = to_intel_display(encoder);
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
u8 pipe_mask;
int ret;
if (!intel_dp_is_connected(intel_dp))
return 0;
ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex,
ctx);
if (ret)
return ret;
if (!intel_dp_needs_link_retrain(intel_dp))
return 0;
ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask);
if (ret)
return ret;
if (pipe_mask == 0)
return 0;
if (!intel_dp_needs_link_retrain(intel_dp))
return 0;
drm_dbg_kms(display->drm,
"[ENCODER:%d:%s] retraining link (forced %s)\n",
encoder->base.base.id, encoder->base.name,
str_yes_no(intel_dp->link.force_retrain));
ret = intel_modeset_commit_pipes(display, pipe_mask, ctx);
if (ret == -EDEADLK)
return ret;
intel_dp->link.force_retrain = false;
if (ret)
drm_dbg_kms(display->drm,
"[ENCODER:%d:%s] link retraining failed: %pe\n",
encoder->base.base.id, encoder->base.name,
ERR_PTR(ret));
return ret;
}
void intel_dp_link_check(struct intel_encoder *encoder)
{
struct drm_modeset_acquire_ctx ctx;
int ret;
intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret)
ret = intel_dp_retrain_link(encoder, &ctx);
}
void intel_dp_check_link_state(struct intel_dp *intel_dp)
{
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
struct intel_encoder *encoder = &dig_port->base;
if (!intel_dp_is_connected(intel_dp))
return;
if (!intel_dp_needs_link_retrain(intel_dp))
return;
intel_encoder_link_check_queue_work(encoder, 0);
}
static void intel_dp_handle_device_service_irq(struct intel_dp *intel_dp, u8 irq_mask)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -6185,7 +5847,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
/*
* Force checking the link status for DPCD_REV < 1.2
* TODO: let the link status check depend on LINK_STATUS_CHANGED
* or intel_dp->link.force_retrain for DPCD_REV >= 1.2
* or intel_dp->link.training.force_retrain for DPCD_REV >= 1.2
*/
esi[3] |= LINK_STATUS_CHANGED;
if (intel_dp_handle_link_service_irq(intel_dp, esi[3]))
@@ -7688,3 +7350,25 @@ u8 intel_dp_as_sdp_transmission_time(void)
return DP_PR_AS_SDP_SETUP_TIME_T1;
}
int intel_dp_link_init(struct intel_dp *intel_dp)
{
intel_dp->link.training = intel_dp_link_training_init(intel_dp);
if (!intel_dp->link.training)
return -ENOMEM;
intel_dp->link.caps = intel_dp_link_caps_init(intel_dp);
if (!intel_dp->link.caps) {
intel_dp_link_training_cleanup(intel_dp->link.training);
return -ENOMEM;
}
return 0;
}
void intel_dp_link_cleanup(struct intel_dp *intel_dp)
{
intel_dp_link_caps_cleanup(intel_dp->link.caps);
intel_dp_link_training_cleanup(intel_dp->link.training);
}

View File

@@ -59,8 +59,6 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp,
struct drm_modeset_acquire_ctx *ctx,
u8 *pipe_mask);
void intel_dp_flush_connector_commits(struct intel_connector *connector);
void intel_dp_link_check(struct intel_encoder *encoder);
void intel_dp_check_link_state(struct intel_dp *intel_dp);
void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode);
void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
@@ -73,7 +71,8 @@ void intel_dp_sink_disable_decompression(struct intel_atomic_state *state,
void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder);
void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder);
void intel_dp_encoder_flush_work(struct drm_encoder *encoder);
int intel_dp_compute_config(struct intel_encoder *encoder,
int intel_dp_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state);
bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state,
@@ -102,17 +101,13 @@ void intel_edp_backlight_off(const struct drm_connector_state *conn_state);
void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp);
void intel_dp_mst_suspend(struct intel_display *display);
void intel_dp_mst_resume(struct intel_display *display);
int intel_dp_rate_limit_len(const int *rates, int len, int max_rate);
int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port);
int intel_dp_max_link_rate(struct intel_dp *intel_dp);
int intel_dp_max_lane_count(struct intel_dp *intel_dp);
int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state);
int intel_dp_rate_select(struct intel_dp *intel_dp, int rate);
int intel_dp_max_common_rate(struct intel_dp *intel_dp);
int intel_dp_max_common_lane_count(struct intel_dp *intel_dp);
int intel_dp_common_rate(struct intel_dp *intel_dp, int index);
int intel_dp_rate_index(const int *rates, int len, int rate);
int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count);
void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count);
void intel_dp_update_sink_caps(struct intel_dp *intel_dp);
void intel_dp_reset_link_params(struct intel_dp *intel_dp);
@@ -209,8 +204,6 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev,
struct intel_connector *connector);
bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder);
bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
u8 lane_count);
bool intel_dp_has_connector(struct intel_dp *intel_dp,
const struct drm_connector_state *conn_state);
int intel_dp_dsc_max_src_input_bpc(struct intel_display *display);
@@ -243,4 +236,7 @@ bool intel_dp_joiner_candidate_valid(struct intel_connector *connector,
u8 intel_dp_as_sdp_transmission_time(void);
int intel_dp_link_init(struct intel_dp *intel_dp);
void intel_dp_link_cleanup(struct intel_dp *intel_dp);
#endif /* __INTEL_DP_H__ */

View File

@@ -0,0 +1,675 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2026 Intel Corporation
*/
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/log2.h>
#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/string.h>
#include <linux/types.h>
#include <drm/drm_print.h>
#include "intel_display_core.h"
#include "intel_display_types.h"
#include "intel_dp.h"
#include "intel_dp_link_caps.h"
struct intel_dp_link_caps {
struct intel_dp *dp;
/* Rate, lane count caps common to source and sink. */
int num_rates;
int rates[DP_MAX_SUPPORTED_RATES];
int max_lane_count;
/* common rate,lane_count configs in bw order */
int num_configs;
#define INTEL_DP_MAX_LANE_COUNT 4
#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1)
#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS)
#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \
INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
struct intel_dp_link_config_entry {
/* index into rates[] */
u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS;
u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS;
} configs[INTEL_DP_MAX_LINK_CONFIGS];
/*
* Forced parameters requested via debugfs. Remains set across sink
* disconnects.
*/
struct intel_dp_link_config forced_params;
/*
* User set maximum limits. These limits constrain the currently
* allowed set of configurations and are not adjusted when sink
* capabilities change.
*
* max_limits.rate/lane_count may come from different allowed
* configurations, i.e. the (max_limits.rate, max_limits.lane_count)
* tuple itself may not be an allowed configuration.
*/
struct intel_dp_link_config max_limits;
};
/* Get length of common rates array potentially limited by max_rate. */
int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps,
int max_rate)
{
return intel_dp_rate_limit_len(link_caps->rates,
link_caps->num_rates, max_rate);
}
int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index)
{
struct intel_display *display = to_intel_display(link_caps->dp);
if (drm_WARN_ON(display->drm,
index < 0 || index >= link_caps->num_rates))
return 162000;
return link_caps->rates[index];
}
int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate)
{
return intel_dp_rate_index(link_caps->rates,
link_caps->num_rates,
rate);
}
/* Theoretical max between source and sink */
int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps)
{
return intel_dp_common_rate(link_caps, link_caps->num_rates - 1);
}
int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps)
{
return link_caps->num_rates;
}
void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps)
{
struct intel_display *display = to_intel_display(link_caps->dp);
DECLARE_SEQ_BUF(s, 128);
int i;
for (i = 0; i < link_caps->num_rates; i++)
seq_buf_printf(&s, "%s%d", i ? ", " : "", link_caps->rates[i]);
drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s));
}
int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps)
{
return link_caps->max_lane_count;
}
static int forced_lane_count(struct intel_dp_link_caps *link_caps)
{
if (!link_caps->forced_params.lane_count)
return 0;
return clamp(link_caps->forced_params.lane_count,
1, intel_dp_link_caps_max_common_lane_count(link_caps));
}
static int forced_link_rate(struct intel_dp_link_caps *link_caps)
{
int len;
if (!link_caps->forced_params.rate)
return 0;
len = intel_dp_common_len_rate_limit(link_caps, link_caps->forced_params.rate);
if (len == 0)
return intel_dp_common_rate(link_caps, 0);
return intel_dp_common_rate(link_caps, len - 1);
}
void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *forced_params)
{
forced_params->rate = forced_link_rate(link_caps);
forced_params->lane_count = forced_lane_count(link_caps);
}
static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config_entry *lce)
{
return intel_dp_common_rate(link_caps, lce->link_rate_idx);
}
static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lce)
{
return 1 << lce->lane_count_exp;
}
static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits)
{
link_caps->max_limits = *max_link_limits;
}
static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps)
{
struct intel_dp_link_config max_link_limits = {
.rate = intel_dp_max_common_rate(link_caps),
.lane_count = intel_dp_link_caps_max_common_lane_count(link_caps),
};
set_max_link_limits_no_update(link_caps, &max_link_limits);
}
/**
* intel_dp_link_caps_get_max_limits - get the current maximum link limits
* @link_caps: link capabilities state
* @max_link_limits: returned maximum link limits
*
* Return the current maximum rate and lane count limits in
* @max_link_limits.
*
* These limits constrain the set of allowed configurations.
*
* The limits are set to the maximum common supported values after
* intel_dp_link_caps_reset() is called, and can later be modified by
* intel_dp_link_caps_set_max_limits(). The max rate and lane count
* parameters are independent limits, so the pair does not necessarily
* define a valid configuration.
*
* This function may be called without serializing against updates to
* @link_caps. However, without such serialization the returned value may be
* an out-of-sync (link rate, lane count) tuple, i.e. the parameters may
* belong to different update snapshots in time.
*/
void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *max_link_limits)
{
*max_link_limits = link_caps->max_limits;
}
/**
* intel_dp_link_caps_set_max_limits - set the current maximum link limits
* @link_caps: link capabilities state
* @max_link_limits: new maximum link limits
*
* Set the current maximum rate and lane count limits to @max_link_limits,
* constraining the set of allowed configurations.
*
* Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize
* this call against concurrent queries and updates to @link_caps, in line
* with the rest of the API.
*
* Return:
* - %true if the @link_caps cached max limits value got updated with
* @max_link_limits.
* - %false if @max_link_limits is invalid.
*/
bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits)
{
set_max_link_limits_no_update(link_caps, max_link_limits);
/* TODO: validate max_link_limits */
return true;
}
/**
* intel_dp_link_caps_reset_max_limits - reset the current maximum link limits
* @link_caps: link capabilities state
*
* Reset the current maximum link limits to the maximum supported common link
* rate and lane count.
*/
void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps)
{
reset_max_link_limits_no_update(link_caps);
}
static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config_entry *lce)
{
return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lce),
intel_dp_link_config_lane_count(lce));
}
static int link_config_cmp_by_bw(const void *a, const void *b, const void *p)
{
struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
const struct intel_dp_link_config_entry *lce_a = a;
const struct intel_dp_link_config_entry *lce_b = b;
int bw_a = intel_dp_link_config_bw(link_caps, lce_a);
int bw_b = intel_dp_link_config_bw(link_caps, lce_b);
if (bw_a != bw_b)
return bw_a - bw_b;
return intel_dp_link_config_rate(link_caps, lce_a) -
intel_dp_link_config_rate(link_caps, lce_b);
}
/* Return %true if the supported link parameters have changed. */
bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps,
const int *rates, int num_rates, int max_lane_count)
{
struct intel_dp *intel_dp = link_caps->dp;
struct intel_display *display = to_intel_display(intel_dp);
struct intel_dp_link_config_entry *lce;
bool link_params_changed = false;
int num_common_lane_configs;
int i;
int j;
if (drm_WARN_ON(display->drm, !is_power_of_2(max_lane_count)))
return false;
if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(link_caps->rates)))
return false;
num_common_lane_configs = ilog2(max_lane_count) + 1;
if (drm_WARN_ON(display->drm, num_rates * num_common_lane_configs >
ARRAY_SIZE(link_caps->configs)))
return false;
/* TODO: Add a struct containing both rates and number of rates. */
static_assert(__same_type(rates[0], link_caps->rates[0]));
if (num_rates != link_caps->num_rates ||
memcmp(rates, link_caps->rates, num_rates * sizeof(rates[0])))
link_params_changed = true;
if (max_lane_count != link_caps->max_lane_count)
link_params_changed = true;
memcpy(link_caps->rates, rates, num_rates * sizeof(rates[0]));
link_caps->num_rates = num_rates;
link_caps->max_lane_count = max_lane_count;
link_caps->num_configs = num_rates * num_common_lane_configs;
lce = &link_caps->configs[0];
for (i = 0; i < link_caps->num_rates; i++) {
for (j = 0; j < num_common_lane_configs; j++) {
lce->lane_count_exp = j;
lce->link_rate_idx = i;
lce++;
}
}
sort_r(link_caps->configs, link_caps->num_configs,
sizeof(link_caps->configs[0]),
link_config_cmp_by_bw, NULL,
intel_dp);
return link_params_changed;
}
void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps,
int idx, int *link_rate, int *lane_count)
{
struct intel_display *display = to_intel_display(link_caps->dp);
const struct intel_dp_link_config_entry *lce;
if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs))
idx = 0;
lce = &link_caps->configs[idx];
*link_rate = intel_dp_link_config_rate(link_caps, lce);
*lane_count = intel_dp_link_config_lane_count(lce);
}
int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps,
int link_rate, int lane_count)
{
int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates,
link_rate);
int lane_count_exp = ilog2(lane_count);
int i;
for (i = 0; i < link_caps->num_configs; i++) {
const struct intel_dp_link_config_entry *lce = &link_caps->configs[i];
if (lce->lane_count_exp == lane_count_exp &&
lce->link_rate_idx == link_rate_idx)
return i;
}
return -1;
}
/**
* intel_dp_link_caps_reset - reset link capability restrictions
* @link_caps: link capabilities state
*
* Reset all current restrictions except for the user requested forced
* parameters, thus updating the set of allowed configurations and the
* derived maximum link information accordingly.
*
* This function is regularly called after a sink is connected, either
* for the first time to the connector or after a previous sink was
* disconnected from it, and intel_dp_link_caps_update() was called.
*/
void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps)
{
/* TODO: Update the maximum link information. */
reset_max_link_limits_no_update(link_caps);
}
static int i915_dp_force_link_rate_show(struct seq_file *m, void *data)
{
struct intel_connector *connector = to_intel_connector(m->private);
struct intel_display *display = to_intel_display(connector);
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int current_rate = -1;
int force_rate;
int err;
int i;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
intel_dp_flush_connector_commits(connector);
if (intel_dp->link.active)
current_rate = intel_dp->link_rate;
force_rate = link_caps->forced_params.rate;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
seq_printf(m, "%sauto%s",
force_rate == 0 ? "[" : "",
force_rate == 0 ? "]" : "");
for (i = 0; i < intel_dp->num_source_rates; i++)
seq_printf(m, " %s%d%s%s",
intel_dp->source_rates[i] == force_rate ? "[" : "",
intel_dp->source_rates[i],
intel_dp->source_rates[i] == current_rate ? "*" : "",
intel_dp->source_rates[i] == force_rate ? "]" : "");
seq_putc(m, '\n');
return 0;
}
static int parse_link_rate(struct intel_dp_link_caps *link_caps, const char __user *ubuf, size_t len)
{
struct intel_dp *intel_dp = link_caps->dp;
char *kbuf;
const char *p;
int rate;
int ret = 0;
kbuf = memdup_user_nul(ubuf, len);
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);
p = strim(kbuf);
if (!strcmp(p, "auto")) {
rate = 0;
} else {
ret = kstrtoint(p, 0, &rate);
if (ret < 0)
goto out_free;
if (intel_dp_rate_index(intel_dp->source_rates,
intel_dp->num_source_rates,
rate) < 0)
ret = -EINVAL;
}
out_free:
kfree(kbuf);
return ret < 0 ? ret : rate;
}
static ssize_t i915_dp_force_link_rate_write(struct file *file,
const char __user *ubuf,
size_t len, loff_t *offp)
{
struct seq_file *m = file->private_data;
struct intel_connector *connector = to_intel_connector(m->private);
struct intel_display *display = to_intel_display(connector);
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int rate;
int err;
rate = parse_link_rate(link_caps, ubuf, len);
if (rate < 0)
return rate;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
intel_dp_flush_connector_commits(connector);
intel_dp_reset_link_params(intel_dp);
link_caps->forced_params.rate = rate;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
*offp += len;
return len;
}
DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate);
static int i915_dp_force_lane_count_show(struct seq_file *m, void *data)
{
struct intel_connector *connector = to_intel_connector(m->private);
struct intel_display *display = to_intel_display(connector);
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int current_lane_count = -1;
int force_lane_count;
int err;
int i;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
intel_dp_flush_connector_commits(connector);
if (intel_dp->link.active)
current_lane_count = intel_dp->lane_count;
force_lane_count = link_caps->forced_params.lane_count;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
seq_printf(m, "%sauto%s",
force_lane_count == 0 ? "[" : "",
force_lane_count == 0 ? "]" : "");
for (i = 1; i <= 4; i <<= 1)
seq_printf(m, " %s%d%s%s",
i == force_lane_count ? "[" : "",
i,
i == current_lane_count ? "*" : "",
i == force_lane_count ? "]" : "");
seq_putc(m, '\n');
return 0;
}
static int parse_lane_count(const char __user *ubuf, size_t len)
{
char *kbuf;
const char *p;
int lane_count;
int ret = 0;
kbuf = memdup_user_nul(ubuf, len);
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);
p = strim(kbuf);
if (!strcmp(p, "auto")) {
lane_count = 0;
} else {
ret = kstrtoint(p, 0, &lane_count);
if (ret < 0)
goto out_free;
switch (lane_count) {
case 1:
case 2:
case 4:
break;
default:
ret = -EINVAL;
}
}
out_free:
kfree(kbuf);
return ret < 0 ? ret : lane_count;
}
static ssize_t i915_dp_force_lane_count_write(struct file *file,
const char __user *ubuf,
size_t len, loff_t *offp)
{
struct seq_file *m = file->private_data;
struct intel_connector *connector = to_intel_connector(m->private);
struct intel_display *display = to_intel_display(connector);
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int lane_count;
int err;
lane_count = parse_lane_count(ubuf, len);
if (lane_count < 0)
return lane_count;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
intel_dp_flush_connector_commits(connector);
intel_dp_reset_link_params(intel_dp);
link_caps->forced_params.lane_count = lane_count;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
*offp += len;
return len;
}
DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count);
static int i915_dp_max_link_rate_show(void *data, u64 *val)
{
struct intel_connector *connector = to_intel_connector(data);
struct intel_display *display = to_intel_display(connector);
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_config max_link_limits;
int err;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
intel_dp_flush_connector_commits(connector);
intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits);
*val = max_link_limits.rate;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n");
static int i915_dp_max_lane_count_show(void *data, u64 *val)
{
struct intel_connector *connector = to_intel_connector(data);
struct intel_display *display = to_intel_display(connector);
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_dp_link_config max_link_limits;
int err;
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
if (err)
return err;
intel_dp_flush_connector_commits(connector);
intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits);
*val = max_link_limits.lane_count;
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n");
/**
* intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector
* @connector: connector to add the debugfs files for
*
* Add the link-capability debugfs files for a DP @connector.
*/
void intel_dp_link_caps_debugfs_add(struct intel_connector *connector)
{
struct dentry *root = connector->base.debugfs_entry;
if (connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort &&
connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
return;
debugfs_create_file("i915_dp_force_link_rate", 0644, root,
connector, &i915_dp_force_link_rate_fops);
debugfs_create_file("i915_dp_force_lane_count", 0644, root,
connector, &i915_dp_force_lane_count_fops);
debugfs_create_file("i915_dp_max_link_rate", 0444, root,
connector, &i915_dp_max_link_rate_fops);
debugfs_create_file("i915_dp_max_lane_count", 0444, root,
connector, &i915_dp_max_lane_count_fops);
}
struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp)
{
struct intel_dp_link_caps *link_caps;
link_caps = kzalloc_obj(*link_caps);
if (!link_caps)
return NULL;
link_caps->dp = intel_dp;
return link_caps;
}
void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps)
{
kfree(link_caps);
}

View File

@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef __INTEL_DP_LINK_CAPS_H__
#define __INTEL_DP_LINK_CAPS_H__
#include <linux/types.h>
struct intel_connector;
struct intel_dp;
struct intel_dp_link_caps;
struct intel_dp_link_config;
int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps,
int max_rate);
int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index);
int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate);
int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps);
int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps);
int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps);
void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps);
void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *forced_params);
int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps,
int link_rate, int lane_count);
void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps,
int idx, int *link_rate, int *lane_count);
void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *max_link_limits);
bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits);
void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps);
bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps,
const int *rates, int num_rates, int max_lane_count);
void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps);
void intel_dp_link_caps_debugfs_add(struct intel_connector *connector);
struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp);
void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps);
#endif /* __INTEL_DP_LINK_CAPS_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,8 @@ struct intel_atomic_state;
struct intel_connector;
struct intel_crtc_state;
struct intel_dp;
struct intel_dp_link_training;
struct intel_encoder;
int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_SIZE]);
int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp);
@@ -54,6 +56,19 @@ static inline u8 intel_dp_training_pattern_symbol(u8 pattern)
void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
u8 lane_count);
bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training);
void intel_dp_link_check(struct intel_encoder *encoder);
void intel_dp_check_link_state(struct intel_dp *intel_dp);
void intel_dp_link_training_debugfs_add(struct intel_connector *connector);
void intel_dp_link_training_reset(struct intel_dp_link_training *link_training);
struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp);
void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training);
#endif /* __INTEL_DP_LINK_TRAINING_H__ */

View File

@@ -697,12 +697,12 @@ static int mst_stream_compute_link_for_joined_pipes(struct intel_encoder *encode
return 0;
}
static int mst_stream_compute_config(struct intel_encoder *encoder,
static int mst_stream_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state);
struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
struct intel_dp *intel_dp = to_primary_dp(encoder);
struct intel_connector *connector =
@@ -925,11 +925,11 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
return 0;
}
static int mst_stream_compute_config_late(struct intel_encoder *encoder,
static int mst_stream_compute_config_late(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state);
struct intel_dp *intel_dp = to_primary_dp(encoder);
/* lowest numbered transcoder will be designated master */

View File

@@ -14,6 +14,7 @@
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_dp.h"
#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_test.h"
@@ -32,6 +33,7 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *pipe_config,
struct link_config_limits *limits)
{
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
struct intel_display *display = to_intel_display(intel_dp);
/* For DP Compliance we override the computed bpp for the pipe */
@@ -54,9 +56,8 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp,
*/
if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate,
intel_dp->compliance.test_lane_count)) {
index = intel_dp_rate_index(intel_dp->common_rates,
intel_dp->num_common_rates,
intel_dp->compliance.test_link_rate);
index = intel_dp_link_caps_common_rate_idx(link_caps,
intel_dp->compliance.test_link_rate);
if (index >= 0) {
limits->min_rate = intel_dp->compliance.test_link_rate;
limits->max_rate = intel_dp->compliance.test_link_rate;

View File

@@ -11,6 +11,7 @@
#include "intel_display_limits.h"
#include "intel_display_types.h"
#include "intel_dp.h"
#include "intel_dp_link_caps.h"
#include "intel_dp_link_training.h"
#include "intel_dp_mst.h"
#include "intel_dp_tunnel.h"
@@ -56,8 +57,9 @@ static int kbytes_to_mbits(int kbytes)
static int get_current_link_bw(struct intel_dp *intel_dp)
{
int rate = intel_dp_max_common_rate(intel_dp);
int lane_count = intel_dp_max_common_lane_count(intel_dp);
struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
int rate = intel_dp_max_common_rate(link_caps);
int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps);
return intel_dp_max_link_data_rate(intel_dp, rate, lane_count);
}

View File

@@ -210,6 +210,18 @@ static int dsb_scanline_to_hw(struct intel_atomic_state *state,
return (scanline + vtotal - intel_crtc_scanline_offset(crtc_state)) % vtotal;
}
static
bool pre_commit_use_safe_window(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
struct intel_display *display = to_intel_display(crtc->base.dev);
if (intel_vrr_always_use_vrr_tg(display))
return true;
return pre_commit_is_vrr_active(state, crtc);
}
/*
* Bspec suggests that we should always set DSB_SKIP_WAITS_EN. We have approach
* different from what is explained in Bspec on how flip is considered being
@@ -229,7 +241,7 @@ static u32 dsb_chicken(struct intel_atomic_state *state,
u32 chicken = intel_psr_use_trans_push(new_crtc_state) ?
DSB_SKIP_WAITS_EN : 0;
if (pre_commit_is_vrr_active(state, crtc))
if (pre_commit_use_safe_window(state, crtc))
chicken |= DSB_CTRL_WAIT_SAFE_WINDOW |
DSB_CTRL_NO_WAIT_VBLANK |
DSB_INST_WAIT_SAFE_WINDOW |
@@ -798,6 +810,12 @@ void intel_dsb_vblank_evade(struct intel_atomic_state *state,
end = intel_vrr_vmax_vblank_start(crtc_state);
start = end - vblank_delay - latency;
intel_dsb_wait_scanline_out(state, dsb, start, end);
} else if (pre_commit_use_safe_window(state, crtc)) {
int vblank_delay = crtc_state->set_context_latency;
end = intel_mode_vblank_start(&crtc_state->hw.adjusted_mode);
start = end - vblank_delay - latency;
intel_dsb_wait_scanline_out(state, dsb, start, end);
} else {
int vblank_delay = intel_mode_vblank_delay(&crtc_state->hw.adjusted_mode);
@@ -891,7 +909,7 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
&crtc_state->hw.adjusted_mode;
int wait_scanlines;
if (pre_commit_is_vrr_active(state, crtc)) {
if (pre_commit_use_safe_window(state, crtc)) {
/*
* If the push happened before the vmin decision boundary
* we don't know how far we are from the undelayed vblank.
@@ -902,9 +920,17 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
* the hardware itself guarantees that we're SCL lines
* away from the delayed vblank, and we won't be inside
* the vmin safe window so this extra wait does nothing.
*
* Experimentally, DSB may observe a slightly stale
* PIPEDSL value. When the actual scanline has just reached
* safe_window_start, WAIT_DSL_OUT may complete immediately
* due to the stale value.
*
* Shift the start back by one scanline to ensure the wait
* window is entered reliably.
*/
intel_dsb_wait_scanline_out(state, dsb,
intel_vrr_safe_window_start(crtc_state),
intel_vrr_safe_window_start(crtc_state) - 1,
intel_vrr_vmin_safe_window_end(crtc_state));
/*
* When the push is sent during vblank it will trigger

View File

@@ -242,7 +242,8 @@ intel_dvo_mode_valid(struct drm_connector *_connector,
return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode);
}
static int intel_dvo_compute_config(struct intel_encoder *encoder,
static int intel_dvo_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -256,7 +257,7 @@ static int intel_dvo_compute_config(struct intel_encoder *encoder,
* with the panel scaling set up to source from the H/VDisplay
* of the original mode.
*/
ret = intel_panel_compute_config(connector, adjusted_mode);
ret = intel_panel_compute_config(state, pipe_config, connector);
if (ret)
return ret;

View File

@@ -54,21 +54,21 @@ struct intel_gmbus {
};
enum gmbus_gpio {
GPIOA,
GPIOB,
GPIOC,
GPIOD,
GPIOE,
GPIOF,
GPIOG,
GPIOH,
__GPIOI_UNUSED,
GPIOJ,
GPIOK,
GPIOL,
GPIOM,
GPION,
GPIOO,
GPIO_0,
GPIO_1,
GPIO_2,
GPIO_3,
GPIO_4,
GPIO_5,
GPIO_6,
GPIO_7,
GPIO_8,
GPIO_9,
GPIO_10,
GPIO_11,
GPIO_12,
GPIO_13,
GPIO_14,
};
struct gmbus_pin {
@@ -78,77 +78,82 @@ struct gmbus_pin {
/* Map gmbus pin pairs to names and registers. */
static const struct gmbus_pin gmbus_pins[] = {
[GMBUS_PIN_SSC] = { "ssc", GPIOB },
[GMBUS_PIN_VGADDC] = { "vga", GPIOA },
[GMBUS_PIN_PANEL] = { "panel", GPIOC },
[GMBUS_PIN_DPC] = { "dpc", GPIOD },
[GMBUS_PIN_DPB] = { "dpb", GPIOE },
[GMBUS_PIN_DPD] = { "dpd", GPIOF },
[GMBUS_PIN_SSC] = { "ssc", GPIO_1 },
[GMBUS_PIN_VGADDC] = { "vga", GPIO_0 },
[GMBUS_PIN_PANEL] = { "panel", GPIO_2 },
[GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
[GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
[GMBUS_PIN_DPD] = { "dpd", GPIO_5 },
};
static const struct gmbus_pin gmbus_pins_bdw[] = {
[GMBUS_PIN_VGADDC] = { "vga", GPIOA },
[GMBUS_PIN_DPC] = { "dpc", GPIOD },
[GMBUS_PIN_DPB] = { "dpb", GPIOE },
[GMBUS_PIN_DPD] = { "dpd", GPIOF },
static const struct gmbus_pin gmbus_pins_lpt_h[] = {
[GMBUS_PIN_VGADDC] = { "vga", GPIO_0 },
[GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
[GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
[GMBUS_PIN_DPD] = { "dpd", GPIO_5 },
};
static const struct gmbus_pin gmbus_pins_skl[] = {
[GMBUS_PIN_DPC] = { "dpc", GPIOD },
[GMBUS_PIN_DPB] = { "dpb", GPIOE },
[GMBUS_PIN_DPD] = { "dpd", GPIOF },
static const struct gmbus_pin gmbus_pins_lpt_lp[] = {
[GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
[GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
};
static const struct gmbus_pin gmbus_pins_spt[] = {
[GMBUS_PIN_DPC] = { "dpc", GPIO_3 },
[GMBUS_PIN_DPB] = { "dpb", GPIO_4 },
[GMBUS_PIN_DPD] = { "dpd", GPIO_5 },
};
static const struct gmbus_pin gmbus_pins_bxt[] = {
[GMBUS_PIN_1_BXT] = { "dpb", GPIOB },
[GMBUS_PIN_2_BXT] = { "dpc", GPIOC },
[GMBUS_PIN_3_BXT] = { "misc", GPIOD },
[GMBUS_PIN_1] = { "dpb", GPIO_1 },
[GMBUS_PIN_2] = { "dpc", GPIO_2 },
[GMBUS_PIN_3] = { "misc", GPIO_3 },
};
static const struct gmbus_pin gmbus_pins_cnp[] = {
[GMBUS_PIN_1_BXT] = { "dpb", GPIOB },
[GMBUS_PIN_2_BXT] = { "dpc", GPIOC },
[GMBUS_PIN_3_BXT] = { "misc", GPIOD },
[GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
[GMBUS_PIN_1] = { "dpb", GPIO_1 },
[GMBUS_PIN_2] = { "dpc", GPIO_2 },
[GMBUS_PIN_3] = { "misc", GPIO_3 },
[GMBUS_PIN_4] = { "dpd", GPIO_4 },
};
static const struct gmbus_pin gmbus_pins_icp[] = {
[GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
[GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
[GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
[GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ },
[GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK },
[GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL },
[GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM },
[GMBUS_PIN_13_TC5_TGP] = { "tc5", GPION },
[GMBUS_PIN_14_TC6_TGP] = { "tc6", GPIOO },
[GMBUS_PIN_1] = { "dpa", GPIO_1 },
[GMBUS_PIN_2] = { "dpb", GPIO_2 },
[GMBUS_PIN_3] = { "dpc", GPIO_3 },
[GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 },
[GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 },
[GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 },
[GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 },
[GMBUS_PIN_13_TC5] = { "tc5", GPIO_13 },
[GMBUS_PIN_14_TC6] = { "tc6", GPIO_14 },
};
static const struct gmbus_pin gmbus_pins_dg1[] = {
[GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
[GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
[GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
[GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
[GMBUS_PIN_1] = { "dpa", GPIO_1 },
[GMBUS_PIN_2] = { "dpb", GPIO_2 },
[GMBUS_PIN_3] = { "dpc", GPIO_3 },
[GMBUS_PIN_4] = { "dpd", GPIO_4 },
};
static const struct gmbus_pin gmbus_pins_dg2[] = {
[GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
[GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
[GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
[GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
[GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ },
[GMBUS_PIN_1] = { "dpa", GPIO_1 },
[GMBUS_PIN_2] = { "dpb", GPIO_2 },
[GMBUS_PIN_3] = { "dpc", GPIO_3 },
[GMBUS_PIN_4] = { "dpd", GPIO_4 },
[GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 },
};
static const struct gmbus_pin gmbus_pins_mtp[] = {
[GMBUS_PIN_1_BXT] = { "dpa", GPIOB },
[GMBUS_PIN_2_BXT] = { "dpb", GPIOC },
[GMBUS_PIN_3_BXT] = { "dpc", GPIOD },
[GMBUS_PIN_4_CNP] = { "dpd", GPIOE },
[GMBUS_PIN_5_MTP] = { "dpe", GPIOF },
[GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ },
[GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK },
[GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL },
[GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM },
[GMBUS_PIN_1] = { "dpa", GPIO_1 },
[GMBUS_PIN_2] = { "dpb", GPIO_2 },
[GMBUS_PIN_3] = { "dpc", GPIO_3 },
[GMBUS_PIN_4] = { "dpd", GPIO_4 },
[GMBUS_PIN_5] = { "dpe", GPIO_5 },
[GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 },
[GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 },
[GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 },
[GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 },
};
static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display,
@@ -175,12 +180,15 @@ static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display,
} else if (display->platform.geminilake || display->platform.broxton) {
pins = gmbus_pins_bxt;
size = ARRAY_SIZE(gmbus_pins_bxt);
} else if (DISPLAY_VER(display) == 9) {
pins = gmbus_pins_skl;
size = ARRAY_SIZE(gmbus_pins_skl);
} else if (display->platform.broadwell) {
pins = gmbus_pins_bdw;
size = ARRAY_SIZE(gmbus_pins_bdw);
} else if (HAS_PCH_SPT(display)) {
pins = gmbus_pins_spt;
size = ARRAY_SIZE(gmbus_pins_spt);
} else if (HAS_PCH_LPT_LP(display)) {
pins = gmbus_pins_lpt_lp;
size = ARRAY_SIZE(gmbus_pins_lpt_lp);
} else if (HAS_PCH_LPT(display)) {
pins = gmbus_pins_lpt_h;
size = ARRAY_SIZE(gmbus_pins_lpt_h);
} else {
pins = gmbus_pins;
size = ARRAY_SIZE(gmbus_pins);

View File

@@ -20,17 +20,17 @@ struct intel_display;
#define GMBUS_PIN_DPB 5 /* SDVO, HDMIB */
#define GMBUS_PIN_DPD 6 /* HDMID */
#define GMBUS_PIN_RESERVED 7 /* 7 reserved */
#define GMBUS_PIN_1_BXT 1 /* BXT+ (atom) and CNP+ (big core) */
#define GMBUS_PIN_2_BXT 2
#define GMBUS_PIN_3_BXT 3
#define GMBUS_PIN_4_CNP 4
#define GMBUS_PIN_5_MTP 5
#define GMBUS_PIN_9_TC1_ICP 9
#define GMBUS_PIN_10_TC2_ICP 10
#define GMBUS_PIN_11_TC3_ICP 11
#define GMBUS_PIN_12_TC4_ICP 12
#define GMBUS_PIN_13_TC5_TGP 13
#define GMBUS_PIN_14_TC6_TGP 14
#define GMBUS_PIN_1 1 /* BXT+ (atom) and CNP+ (big core) */
#define GMBUS_PIN_2 2
#define GMBUS_PIN_3 3
#define GMBUS_PIN_4 4
#define GMBUS_PIN_5 5
#define GMBUS_PIN_9_TC1 9 /* ICP+ */
#define GMBUS_PIN_10_TC2 10
#define GMBUS_PIN_11_TC3 11
#define GMBUS_PIN_12_TC4 12
#define GMBUS_PIN_13_TC5 13
#define GMBUS_PIN_14_TC6 14
#define GMBUS_NUM_PINS 15 /* including 0 */

View File

@@ -71,7 +71,7 @@
bool intel_hdmi_is_frl(u32 clock)
{
u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 };
static const u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 };
int i;
for (i = 0; i < ARRAY_SIZE(rates); i++)
@@ -2728,6 +2728,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_
drm_connector_attach_max_bpc_property(&connector->base, 8, 12);
}
/*
* HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after
* enabling Scrambling_Enable and starting scrambled video
* transmission, poll Scrambling_Status for up to 200 ms.
*/
void
intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
struct drm_connector *_connector)
{
struct intel_connector *connector = to_intel_connector(_connector);
struct intel_display *display = to_intel_display(crtc_state);
bool scrambling_enabled = false;
int ret;
if (!crtc_state->hdmi_scrambling)
return;
/* Poll for a max of 200 msec as per HDMI spec */
ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base),
scrambling_enabled, 1000, 200 * 1000, false);
if (ret)
drm_dbg_kms(display->drm,
"[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n",
connector->base.base.id, connector->base.name);
}
/*
* intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup
* @encoder: intel_encoder
@@ -2799,14 +2825,14 @@ static u8 bxt_encoder_to_ddc_pin(struct intel_encoder *encoder)
switch (port) {
case PORT_B:
ddc_pin = GMBUS_PIN_1_BXT;
ddc_pin = GMBUS_PIN_1;
break;
case PORT_C:
ddc_pin = GMBUS_PIN_2_BXT;
ddc_pin = GMBUS_PIN_2;
break;
default:
MISSING_CASE(port);
ddc_pin = GMBUS_PIN_1_BXT;
ddc_pin = GMBUS_PIN_1;
break;
}
return ddc_pin;
@@ -2819,20 +2845,17 @@ static u8 cnp_encoder_to_ddc_pin(struct intel_encoder *encoder)
switch (port) {
case PORT_B:
ddc_pin = GMBUS_PIN_1_BXT;
ddc_pin = GMBUS_PIN_1;
break;
case PORT_C:
ddc_pin = GMBUS_PIN_2_BXT;
ddc_pin = GMBUS_PIN_2;
break;
case PORT_D:
ddc_pin = GMBUS_PIN_4_CNP;
break;
case PORT_F:
ddc_pin = GMBUS_PIN_3_BXT;
ddc_pin = GMBUS_PIN_4;
break;
default:
MISSING_CASE(port);
ddc_pin = GMBUS_PIN_1_BXT;
ddc_pin = GMBUS_PIN_1;
break;
}
return ddc_pin;
@@ -2844,12 +2867,12 @@ static u8 icl_encoder_to_ddc_pin(struct intel_encoder *encoder)
enum port port = encoder->port;
if (intel_encoder_is_combo(encoder))
return GMBUS_PIN_1_BXT + port;
return GMBUS_PIN_1 + port;
else if (intel_encoder_is_tc(encoder))
return GMBUS_PIN_9_TC1_ICP + intel_encoder_to_tc(encoder);
return GMBUS_PIN_9_TC1 + intel_encoder_to_tc(encoder);
drm_WARN(display->drm, 1, "Unknown port:%c\n", port_name(port));
return GMBUS_PIN_2_BXT;
return GMBUS_PIN_2;
}
static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder)
@@ -2859,17 +2882,17 @@ static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder)
switch (phy) {
case PHY_A:
ddc_pin = GMBUS_PIN_1_BXT;
ddc_pin = GMBUS_PIN_1;
break;
case PHY_B:
ddc_pin = GMBUS_PIN_2_BXT;
ddc_pin = GMBUS_PIN_2;
break;
case PHY_C:
ddc_pin = GMBUS_PIN_9_TC1_ICP;
ddc_pin = GMBUS_PIN_9_TC1;
break;
default:
MISSING_CASE(phy);
ddc_pin = GMBUS_PIN_1_BXT;
ddc_pin = GMBUS_PIN_1;
break;
}
return ddc_pin;
@@ -2889,9 +2912,9 @@ static u8 rkl_encoder_to_ddc_pin(struct intel_encoder *encoder)
* all outputs.
*/
if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C)
return GMBUS_PIN_9_TC1_ICP + phy - PHY_C;
return GMBUS_PIN_9_TC1 + phy - PHY_C;
return GMBUS_PIN_1_BXT + phy;
return GMBUS_PIN_1 + phy;
}
static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder)
@@ -2908,9 +2931,9 @@ static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder)
* all outputs.
*/
if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C)
return GMBUS_PIN_9_TC1_ICP + phy - PHY_C;
return GMBUS_PIN_9_TC1 + phy - PHY_C;
return GMBUS_PIN_1_BXT + phy;
return GMBUS_PIN_1 + phy;
}
static u8 dg1_encoder_to_ddc_pin(struct intel_encoder *encoder)
@@ -2929,9 +2952,9 @@ static u8 adls_encoder_to_ddc_pin(struct intel_encoder *encoder)
* except first combo output.
*/
if (phy == PHY_A)
return GMBUS_PIN_1_BXT;
return GMBUS_PIN_1;
return GMBUS_PIN_9_TC1_ICP + phy - PHY_B;
return GMBUS_PIN_9_TC1 + phy - PHY_B;
}
static u8 g4x_encoder_to_ddc_pin(struct intel_encoder *encoder)

View File

@@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
unsigned int type,
void *frame, ssize_t len);
void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
struct drm_connector *_connector);
#endif /* __INTEL_HDMI_H__ */

View File

@@ -170,7 +170,7 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
drm_framebuffer_get(fb);
plane_state->uapi.crtc = &crtc->base;
intel_plane_copy_uapi_to_hw_state(plane_state, plane_state, crtc);
intel_plane_copy_uapi_to_hw_state(NULL, plane_state, plane_state, crtc);
atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits);

View File

@@ -262,10 +262,10 @@ void intel_lpe_audio_irq_handler(struct intel_display *display)
if (!HAS_LPE_AUDIO(display))
return;
ret = generic_handle_irq(display->audio.lpe.irq);
ret = generic_handle_irq_safe(display->audio.lpe.irq);
if (ret)
drm_err_ratelimited(display->drm,
"error handling LPE audio irq: %d\n", ret);
"LPE audio: irq handling failed (%pe)\n", ERR_PTR(ret));
}
/**

View File

@@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder,
else
val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK);
/* DP2.0 10G and 20G rates enable MPLLA*/
if (port_clock == 1000000 || port_clock == 2000000)
val |= XELPDP_SSC_ENABLE_PLLA;
else
val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0;
val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0;
intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE |
@@ -2178,8 +2174,9 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p,
{
int i, j;
drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %d, tbt mode: %d\n",
hw_state->lane_count, hw_state->ssc_enabled, hw_state->tbt_mode);
drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %s, tbt mode: %s\n",
hw_state->lane_count, str_yes_no(hw_state->ssc_enabled),
str_yes_no(hw_state->tbt_mode));
for (i = 0; i < 3; i++) {
drm_printf(p, "config[%d] = 0x%.4x,\n",
@@ -2221,6 +2218,14 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder)
XELPDP_LANE_PCLK_PLL_ACK(0);
}
static bool readout_ssc_state(struct intel_encoder *encoder)
{
struct intel_display *display = to_intel_display(encoder);
return intel_de_read(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port)) &
XELPDP_SSC_ENABLE_PLLA;
}
bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display,
struct intel_dpll *pll,
struct intel_dpll_hw_state *hw_state)
@@ -2250,6 +2255,7 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder,
owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1;
wakeref = intel_lt_phy_transaction_begin(encoder);
pll_state->ssc_enabled = readout_ssc_state(encoder);
pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_LT_PHY_LANE0,
INTEL_LT_PHY_LANE1);

View File

@@ -413,7 +413,8 @@ intel_lvds_mode_valid(struct drm_connector *_connector,
return MODE_OK;
}
static int intel_lvds_compute_config(struct intel_encoder *encoder,
static int intel_lvds_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -459,7 +460,7 @@ static int intel_lvds_compute_config(struct intel_encoder *encoder,
* with the panel scaling set up to source from the H/VDisplay
* of the original mode.
*/
ret = intel_panel_compute_config(connector, adjusted_mode);
ret = intel_panel_compute_config(state, crtc_state, connector);
if (ret)
return ret;

View File

@@ -67,21 +67,62 @@ static bool is_best_fixed_mode(struct intel_connector *connector,
if (!best_mode)
return true;
/*
* With VRR always pick a mode with equal/higher than requested
* vrefresh, which we can then reduce to match the requested
* vrefresh by extending the vblank length.
*/
if (intel_vrr_is_in_range(connector, vrefresh) &&
intel_vrr_is_in_range(connector, fixed_mode_vrefresh) &&
fixed_mode_vrefresh < vrefresh)
return false;
/* pick the fixed_mode that is closest in terms of vrefresh */
return abs(fixed_mode_vrefresh - vrefresh) <
abs(drm_mode_vrefresh(best_mode) - vrefresh);
}
static bool is_vrr_compatible(const struct drm_display_mode *mode1,
const struct drm_display_mode *mode2)
{
return drm_mode_match(mode1, mode2,
DRM_MODE_MATCH_CLOCK |
DRM_MODE_MATCH_TIMINGS_VRR |
DRM_MODE_MATCH_FLAGS |
DRM_MODE_MATCH_3D_FLAGS);
}
static const struct drm_display_mode *
intel_panel_fixed_mode_vrr(struct intel_connector *connector,
const struct drm_display_mode *mode,
const struct drm_display_mode *vrr_ref_mode)
{
const struct drm_display_mode *fixed_mode, *best_mode = NULL;
int vrefresh = drm_mode_vrefresh(mode);
if (!intel_vrr_is_in_range(connector, vrefresh))
return NULL;
if (vrr_ref_mode &&
!intel_vrr_is_in_range(connector, drm_mode_vrefresh(vrr_ref_mode)))
return NULL;
list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
if (!intel_vrr_is_in_range(connector, fixed_mode_vrefresh))
continue;
/*
* With VRR always pick a mode with equal/higher than requested
* vrefresh, which we can then reduce to match the requested
* vrefresh by extending the vblank length.
*/
if (fixed_mode_vrefresh < vrefresh)
continue;
if (vrr_ref_mode &&
!is_vrr_compatible(fixed_mode, vrr_ref_mode))
continue;
if (is_best_fixed_mode(connector, vrefresh,
fixed_mode_vrefresh, best_mode))
best_mode = fixed_mode;
}
return best_mode;
}
const struct drm_display_mode *
intel_panel_fixed_mode(struct intel_connector *connector,
const struct drm_display_mode *mode)
@@ -197,14 +238,66 @@ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector)
return connector->panel.vbt.drrs_type;
}
int intel_panel_compute_config(struct intel_connector *connector,
struct drm_display_mode *adjusted_mode)
static int intel_panel_compute_config_vrr(struct intel_atomic_state *state,
struct intel_crtc_state *crtc_state,
struct intel_connector *connector)
{
const struct drm_display_mode *fixed_mode =
intel_panel_fixed_mode(connector, adjusted_mode);
struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
const struct drm_display_mode *fixed_mode = NULL;
int vrefresh, fixed_mode_vrefresh;
bool is_vrr;
/*
* Attempt a VRR based refresh rate change if possible
* when userspace has forbidden a full modeset.
*/
if (!state->base.allow_modeset) {
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
const struct intel_crtc_state *old_crtc_state =
intel_atomic_get_old_crtc_state(state, crtc);
if (old_crtc_state->hw.enable &&
old_crtc_state->uapi.encoder_mask == crtc_state->uapi.encoder_mask)
fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode,
&old_crtc_state->hw.adjusted_mode);
}
if (!fixed_mode)
fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, NULL);
if (!fixed_mode)
return -EINVAL;
vrefresh = drm_mode_vrefresh(adjusted_mode);
fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
drm_mode_copy(adjusted_mode, fixed_mode);
if (fixed_mode_vrefresh != vrefresh) {
int vsync_start_offset = adjusted_mode->vtotal - adjusted_mode->vsync_start;
int vsync_end_offset = adjusted_mode->vtotal - adjusted_mode->vsync_end;
adjusted_mode->vtotal =
DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000,
adjusted_mode->htotal * vrefresh);
adjusted_mode->vsync_start = adjusted_mode->vtotal - vsync_start_offset;
adjusted_mode->vsync_end = adjusted_mode->vtotal - vsync_end_offset;
}
drm_mode_set_crtcinfo(adjusted_mode, 0);
return 0;
}
static int intel_panel_compute_config_fixed_rr(struct intel_atomic_state *state,
struct intel_crtc_state *crtc_state,
struct intel_connector *connector)
{
struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
const struct drm_display_mode *fixed_mode;
int vrefresh, fixed_mode_vrefresh;
fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode);
if (!fixed_mode)
return 0;
@@ -212,41 +305,40 @@ int intel_panel_compute_config(struct intel_connector *connector,
fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
/*
* Assume that we shouldn't muck about with the
* timings if they don't land in the VRR range.
* We don't want to lie too much to the user about the refresh
* rate they're going to get. But we have to allow a bit of latitude
* for Xorg since it likes to automagically cook up modes with slightly
* off refresh rates.
*/
is_vrr = intel_vrr_is_in_range(connector, vrefresh) &&
intel_vrr_is_in_range(connector, fixed_mode_vrefresh);
if (abs(vrefresh - fixed_mode_vrefresh) > 1) {
drm_dbg_kms(connector->base.dev,
"[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n",
connector->base.base.id, connector->base.name,
vrefresh, fixed_mode_vrefresh);
if (!is_vrr) {
/*
* We don't want to lie too much to the user about the refresh
* rate they're going to get. But we have to allow a bit of latitude
* for Xorg since it likes to automagically cook up modes with slightly
* off refresh rates.
*/
if (abs(vrefresh - fixed_mode_vrefresh) > 1) {
drm_dbg_kms(connector->base.dev,
"[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n",
connector->base.base.id, connector->base.name,
vrefresh, fixed_mode_vrefresh);
return -EINVAL;
}
return -EINVAL;
}
drm_mode_copy(adjusted_mode, fixed_mode);
if (is_vrr && fixed_mode_vrefresh != vrefresh)
adjusted_mode->vtotal =
DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000,
adjusted_mode->htotal * vrefresh);
drm_mode_set_crtcinfo(adjusted_mode, 0);
return 0;
}
int intel_panel_compute_config(struct intel_atomic_state *state,
struct intel_crtc_state *crtc_state,
struct intel_connector *connector)
{
int ret;
ret = intel_panel_compute_config_vrr(state, crtc_state, connector);
if (ret)
ret = intel_panel_compute_config_fixed_rr(state, crtc_state, connector);
return ret;
}
static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector)
{
struct intel_display *display = to_intel_display(connector);

View File

@@ -14,6 +14,7 @@ struct drm_connector;
struct drm_connector_state;
struct drm_display_mode;
struct drm_edid;
struct intel_atomic_state;
struct intel_connector;
struct intel_crtc_state;
struct intel_display;
@@ -45,8 +46,9 @@ enum drm_mode_status
intel_panel_mode_valid(struct intel_connector *connector,
const struct drm_display_mode *mode,
int *target_clock);
int intel_panel_compute_config(struct intel_connector *connector,
struct drm_display_mode *adjusted_mode);
int intel_panel_compute_config(struct intel_atomic_state *state,
struct intel_crtc_state *crtc_state,
struct intel_connector *connector);
void intel_panel_add_edid_fixed_modes(struct intel_connector *connector,
bool use_alt_fixed_modes);
void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector);

View File

@@ -251,9 +251,11 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display)
return display->parent->panic->alloc();
}
int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb)
int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic,
struct drm_scanout_buffer *sb, struct drm_gem_object *obj,
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width))
{
return display->parent->panic->setup(panic, sb);
return display->parent->panic->setup(panic, sb, obj, tiling);
}
void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic)

View File

@@ -105,7 +105,9 @@ void intel_parent_overlay_cleanup(struct intel_display *display);
/* panic */
struct intel_panic *intel_parent_panic_alloc(struct intel_display *display);
int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb);
int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic,
struct drm_scanout_buffer *sb, struct drm_gem_object *obj,
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width));
void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic);
/* pc8 */

View File

@@ -408,25 +408,27 @@ intel_plane_colorop_replace_blob(struct intel_plane_state *plane_state,
}
static void
intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
intel_plane_color_copy_uapi_to_hw_state(struct intel_atomic_state *state,
struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state,
struct intel_crtc *crtc)
{
struct drm_colorop *iter_colorop, *colorop;
struct drm_colorop_state *new_colorop_state;
struct drm_atomic_commit *state = plane_state->uapi.state;
struct intel_colorop *intel_colorop;
struct drm_property_blob *blob;
struct intel_atomic_state *intel_atomic_state = to_intel_atomic_state(state);
struct intel_crtc_state *new_crtc_state = intel_atomic_state ?
intel_atomic_get_new_crtc_state(intel_atomic_state, crtc) : NULL;
struct intel_crtc_state *new_crtc_state = state ?
intel_atomic_get_new_crtc_state(state, crtc) : NULL;
bool changed = false;
int i = 0;
if (!state)
return;
iter_colorop = from_plane_state->uapi.color_pipeline;
while (iter_colorop) {
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
for_each_new_colorop_in_state(&state->base, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == iter_colorop) {
blob = new_colorop_state->bypass ? NULL : new_colorop_state->data;
intel_colorop = to_intel_colorop(colorop);
@@ -442,7 +444,8 @@ intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
new_crtc_state->plane_color_changed = true;
}
void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state,
struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state,
struct intel_crtc *crtc)
{
@@ -471,19 +474,25 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi);
plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi);
intel_plane_color_copy_uapi_to_hw_state(plane_state, from_plane_state, crtc);
intel_plane_color_copy_uapi_to_hw_state(state, plane_state, from_plane_state, crtc);
}
void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state)
static void intel_plane_y_copy_hw_state(struct intel_plane_state *y_plane_state,
const struct intel_plane_state *uv_plane_state)
{
intel_plane_clear_hw_state(plane_state);
intel_plane_clear_hw_state(y_plane_state);
memcpy(&plane_state->hw, &from_plane_state->hw,
sizeof(plane_state->hw));
y_plane_state->hw.crtc = uv_plane_state->hw.crtc;
y_plane_state->hw.fb = uv_plane_state->hw.fb;
if (y_plane_state->hw.fb)
drm_framebuffer_get(y_plane_state->hw.fb);
if (plane_state->hw.fb)
drm_framebuffer_get(plane_state->hw.fb);
y_plane_state->hw.alpha = uv_plane_state->hw.alpha;
y_plane_state->hw.pixel_blend_mode = uv_plane_state->hw.pixel_blend_mode;
y_plane_state->hw.rotation = uv_plane_state->hw.rotation;
y_plane_state->hw.color_encoding = uv_plane_state->hw.color_encoding;
y_plane_state->hw.color_range = uv_plane_state->hw.color_range;
y_plane_state->hw.scaling_filter = uv_plane_state->hw.scaling_filter;
}
static void unlink_nv12_plane(struct intel_crtc_state *crtc_state,
@@ -868,7 +877,8 @@ static int plane_atomic_check(struct intel_atomic_state *state,
old_primary_crtc_plane_state,
new_primary_crtc_plane_state);
intel_plane_copy_uapi_to_hw_state(new_plane_state,
intel_plane_copy_uapi_to_hw_state(state,
new_plane_state,
new_primary_crtc_plane_state,
crtc);
@@ -1607,17 +1617,17 @@ static int intel_get_scanout_buffer(struct drm_plane *plane,
if (fb == intel_fbdev_framebuffer(display->fbdev.fbdev)) {
intel_fbdev_get_map(display, &sb->map[0]);
} else {
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width) = NULL;
int ret;
/* Can't disable tiling if DPT is in use */
if (intel_fb_uses_dpt(&fb->base)) {
if (fb->base.format->cpp[0] != 4)
return -EOPNOTSUPP;
fb->panic_tiling = intel_get_tiling_func(fb->base.modifier);
if (!fb->panic_tiling)
tiling = intel_get_tiling_func(fb->base.modifier);
if (!tiling)
return -EOPNOTSUPP;
}
sb->private = fb;
ret = intel_parent_panic_setup(display, fb->panic, sb);
ret = intel_parent_panic_setup(display, fb->panic, sb, obj, tiling);
if (ret)
return ret;
}
@@ -1688,7 +1698,7 @@ static void link_nv12_planes(struct intel_crtc_state *crtc_state,
crtc_state->rel_data_rate[y_plane->id] = crtc_state->rel_data_rate_y[uv_plane->id];
/* Copy parameters to Y plane */
intel_plane_copy_hw_state(y_plane_state, uv_plane_state);
intel_plane_y_copy_hw_state(y_plane_state, uv_plane_state);
y_plane_state->uapi.src = uv_plane_state->uapi.src;
y_plane_state->uapi.dst = uv_plane_state->uapi.dst;

View File

@@ -35,11 +35,10 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state,
unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state,
int color_plane);
void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state,
struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state,
struct intel_crtc *crtc);
void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state);
void intel_plane_async_flip(struct intel_dsb *dsb,
struct intel_plane *plane,
const struct intel_crtc_state *crtc_state,

View File

@@ -88,22 +88,6 @@
* issues the self-refresh re-enable code is done from a work queue, which
* must be correctly synchronized/cancelled when shutting down the pipe."
*
* DC3CO (DC3 clock off)
*
* On top of PSR2, GEN12 adds a intermediate power savings state that turns
* clock off automatically during PSR2 idle state.
* The smaller overhead of DC3co entry/exit vs. the overhead of PSR2 deep sleep
* entry/exit allows the HW to enter a low-power state even when page flipping
* periodically (for instance a 30fps video playback scenario).
*
* Every time a flips occurs PSR2 will get out of deep sleep state(if it was),
* so DC3CO is enabled and tgl_dc3co_disable_work is schedule to run after 6
* frames, if no other flip occurs and the function above is executed, DC3CO is
* disabled and PSR2 is configured to enter deep sleep, resetting again in case
* of another flip.
* Front buffer modifications do not trigger DC3CO activation on purpose as it
* would bring a lot of complexity and most of the moderns systems will only
* use page flips.
*/
/*
@@ -1098,10 +1082,11 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp)
u32 psr_val = 0;
u8 idle_frames;
/* Wa_16025596647 */
if ((DISPLAY_VER(display) == 20 ||
IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) &&
is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used)
/* DC3CO / Wa_16025596647 */
if (intel_dp->psr.dc3co_allowed ||
((DISPLAY_VER(display) == 20 ||
IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) &&
is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used))
idle_frames = 0;
else
idle_frames = psr_compute_idle_frames(intel_dp);
@@ -1220,108 +1205,6 @@ static void psr2_program_idle_frames(struct intel_dp *intel_dp,
EDP_PSR2_IDLE_FRAMES(idle_frames));
}
static void tgl_psr2_enable_dc3co(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
psr2_program_idle_frames(intel_dp, 0);
intel_display_power_set_target_dc_state(display, DC_STATE_EN_DC3CO);
}
static void tgl_psr2_disable_dc3co(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp));
}
static void tgl_dc3co_disable_work(struct work_struct *work)
{
struct intel_dp *intel_dp =
container_of(work, typeof(*intel_dp), psr.dc3co_work.work);
mutex_lock(&intel_dp->psr.lock);
/* If delayed work is pending, it is not idle */
if (delayed_work_pending(&intel_dp->psr.dc3co_work))
goto unlock;
tgl_psr2_disable_dc3co(intel_dp);
unlock:
mutex_unlock(&intel_dp->psr.lock);
}
static void tgl_disallow_dc3co_on_psr2_exit(struct intel_dp *intel_dp)
{
if (!intel_dp->psr.dc3co_exitline)
return;
cancel_delayed_work(&intel_dp->psr.dc3co_work);
/* Before PSR2 exit disallow dc3co*/
tgl_psr2_disable_dc3co(intel_dp);
}
static bool
dc3co_is_pipe_port_compatible(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(intel_dp);
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe;
enum port port = dig_port->base.port;
if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14)
return pipe <= PIPE_B && port <= PORT_B;
else
return pipe == PIPE_A && port == PORT_A;
}
static void
tgl_dc3co_exitline_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(intel_dp);
const u32 crtc_vdisplay = crtc_state->uapi.adjusted_mode.crtc_vdisplay;
struct i915_power_domains *power_domains = &display->power.domains;
u32 exit_scanlines;
/*
* FIXME: Due to the changed sequence of activating/deactivating DC3CO,
* disable DC3CO until the changed dc3co activating/deactivating sequence
* is applied. B.Specs:49196
*/
return;
/*
* DMC's DC3CO exit mechanism has an issue with Selective Fecth
* TODO: when the issue is addressed, this restriction should be removed.
*/
if (crtc_state->enable_psr2_sel_fetch)
return;
if (!(power_domains->allowed_dc_mask & DC_STATE_EN_DC3CO))
return;
if (!dc3co_is_pipe_port_compatible(intel_dp, crtc_state))
return;
/* Wa_16011303918:adl-p */
if (intel_display_wa(display, INTEL_DISPLAY_WA_16011303918))
return;
/*
* DC3CO Exit time 200us B.Spec 49196
* PSR2 transcoder Early Exit scanlines = ROUNDUP(200 / line time) + 1
*/
exit_scanlines =
intel_usecs_to_scanlines(&crtc_state->uapi.adjusted_mode, 200) + 1;
if (drm_WARN_ON(display->drm, exit_scanlines > crtc_vdisplay))
return;
crtc_state->dc3co_exitline = crtc_vdisplay - exit_scanlines;
}
static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state)
{
@@ -1522,9 +1405,6 @@ int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state
needs_panel_replay)
return 0;
if (intel_vrr_always_use_vrr_tg(display))
return 0;
return 1;
}
@@ -1697,8 +1577,6 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
return false;
}
tgl_dc3co_exitline_compute_config(intel_dp, crtc_state);
return true;
}
@@ -1893,6 +1771,50 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp,
!crtc_state->has_sel_update);
}
static void psr2_dc3co_disable_locked(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
if (intel_dp->psr.dc3co_allowed) {
intel_dp->psr.dc3co_allowed = false;
intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6);
psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp));
}
}
static void psr2_dc3co_disable_work(struct work_struct *work)
{
struct intel_dp *intel_dp =
container_of(work, typeof(*intel_dp), psr.dc3co_work.work);
mutex_lock(&intel_dp->psr.lock);
psr2_dc3co_disable_locked(intel_dp);
mutex_unlock(&intel_dp->psr.lock);
}
static void
psr2_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits)
{
struct intel_display *display = to_intel_display(intel_dp);
if (!intel_dp->psr.dc3co_allowed)
return;
if (!intel_dp->psr.sel_update_enabled ||
!intel_dp->psr.active)
return;
/*
* At every frontbuffer flush flip event modified delay of delayed work,
* when delayed work schedules that means display has been idle.
*/
if (!(frontbuffer_bits &
INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe)))
return;
mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
intel_dp->psr.dc3co_exit_delay);
}
static
void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state)
@@ -2013,12 +1935,6 @@ void intel_psr_get_config(struct intel_encoder *encoder,
}
pipe_config->enable_psr2_su_region_et = intel_dp->psr.su_region_et_enabled;
if (DISPLAY_VER(display) >= 12) {
val = intel_de_read(display,
TRANS_EXITLINE(display, cpu_transcoder));
pipe_config->dc3co_exitline = REG_FIELD_GET(EXITLINE_MASK, val);
}
unlock:
mutex_unlock(&intel_dp->psr.lock);
}
@@ -2146,16 +2062,6 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp,
psr_irq_control(intel_dp);
/*
* TODO: if future platforms supports DC3CO in more than one
* transcoder, EXITLINE will need to be unset when disabling PSR
*/
if (intel_dp->psr.dc3co_exitline)
intel_de_rmw(display,
TRANS_EXITLINE(display, cpu_transcoder),
EXITLINE_MASK,
intel_dp->psr.dc3co_exitline << EXITLINE_SHIFT | EXITLINE_ENABLE);
if (HAS_PSR_HW_TRACKING(display) && HAS_PSR2_SEL_FETCH(display))
intel_de_rmw(display, CHICKEN_PAR1_1, IGNORE_PSR2_HW_TRACKING,
intel_dp->psr.psr2_sel_fetch_enabled ?
@@ -2255,7 +2161,6 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp,
/* DC5/DC6 requires at least 6 idle frames */
val = usecs_to_jiffies(intel_get_frame_time_us(crtc_state) * 6);
intel_dp->psr.dc3co_exit_delay = val;
intel_dp->psr.dc3co_exitline = crtc_state->dc3co_exitline;
intel_dp->psr.psr2_sel_fetch_enabled = crtc_state->enable_psr2_sel_fetch;
intel_dp->psr.su_region_et_enabled = crtc_state->enable_psr2_su_region_et;
intel_dp->psr.psr2_sel_fetch_cff_enabled = false;
@@ -2334,8 +2239,6 @@ static void intel_psr_exit(struct intel_dp *intel_dp)
intel_de_rmw(display, TRANS_DP2_CTL(intel_dp->psr.transcoder),
TRANS_DP2_PANEL_REPLAY_ENABLE, 0);
} else if (intel_dp->psr.sel_update_enabled) {
tgl_disallow_dc3co_on_psr2_exit(intel_dp);
val = intel_de_rmw(display,
EDP_PSR2_CTL(display, cpu_transcoder),
EDP_PSR2_ENABLE, 0);
@@ -2358,6 +2261,27 @@ static void intel_psr_exit(struct intel_dp *intel_dp)
intel_dp->psr.active = false;
}
bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
enum transcoder cpu_transcoder;
bool in_deep_sleep = false;
u32 val;
mutex_lock(&intel_dp->psr.lock);
if (!intel_dp->psr.enabled || !intel_dp->psr.sel_update_enabled)
goto out;
cpu_transcoder = intel_dp->psr.transcoder;
val = intel_de_read(display, EDP_PSR2_STATUS(display, cpu_transcoder));
in_deep_sleep = (val & EDP_PSR2_STATUS_STATE_MASK) ==
EDP_PSR2_STATUS_STATE_DEEP_SLEEP;
out:
mutex_unlock(&intel_dp->psr.lock);
return in_deep_sleep;
}
static void intel_psr_wait_exit_locked(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -2448,6 +2372,8 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp)
intel_dp->psr.psr2_sel_fetch_cff_enabled = false;
intel_dp->psr.active_non_psr_pipes = 0;
intel_dp->psr.pkg_c_latency_used = 0;
cancel_delayed_work(&intel_dp->psr.dc3co_work);
intel_dp->psr.dc3co_allowed = false;
}
/**
@@ -2535,8 +2461,13 @@ void intel_psr_resume(struct intel_dp *intel_dp)
goto out;
}
if (--intel_dp->psr.pause_counter == 0)
if (--intel_dp->psr.pause_counter == 0) {
intel_psr_activate(intel_dp);
/* re-arm cancelled dc3co work from pause */
if (intel_dp->psr.dc3co_allowed)
mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
intel_dp->psr.dc3co_exit_delay);
}
out:
mutex_unlock(&psr->lock);
@@ -3240,10 +3171,13 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
const struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
struct intel_encoder *encoder;
bool dc3co_allowed;
if (!crtc_state->has_psr)
return;
dc3co_allowed = intel_display_power_dc3co_allowed(display);
verify_panel_replay_dsc_state(crtc_state);
for_each_intel_encoder_mask_with_psr(state->base.dev, encoder,
@@ -3271,6 +3205,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
keep_disabled = true;
}
intel_dp->psr.dc3co_allowed = dc3co_allowed;
if (!psr->enabled && !keep_disabled)
intel_psr_enable_locked(intel_dp, crtc_state);
else if (psr->enabled && !crtc_state->wm_level_disabled)
@@ -3287,6 +3223,11 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
*/
intel_dp->psr.busy_frontbuffer_bits = 0;
if (intel_dp->psr.dc3co_allowed) {
mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
intel_dp->psr.dc3co_exit_delay);
}
mutex_unlock(&psr->lock);
}
}
@@ -3659,34 +3600,6 @@ void intel_psr_invalidate(struct intel_display *display,
mutex_unlock(&intel_dp->psr.lock);
}
}
/*
* When we will be completely rely on PSR2 S/W tracking in future,
* intel_psr_flush() will invalidate and flush the PSR for ORIGIN_FLIP
* event also therefore tgl_dc3co_flush_locked() require to be changed
* accordingly in future.
*/
static void
tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits,
enum fb_op_origin origin)
{
struct intel_display *display = to_intel_display(intel_dp);
if (!intel_dp->psr.dc3co_exitline || !intel_dp->psr.sel_update_enabled ||
!intel_dp->psr.active)
return;
/*
* At every frontbuffer flush flip event modified delay of delayed work,
* when delayed work schedules that means display has been idle.
*/
if (!(frontbuffer_bits &
INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe)))
return;
tgl_psr2_enable_dc3co(intel_dp);
mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work,
intel_dp->psr.dc3co_exit_delay);
}
static void _psr_flush_handle(struct intel_dp *intel_dp)
{
@@ -3773,7 +3686,7 @@ void intel_psr_flush(struct intel_display *display,
if (origin == ORIGIN_FLIP ||
(origin == ORIGIN_CURSOR_UPDATE &&
!intel_dp->psr.psr2_sel_fetch_enabled)) {
tgl_dc3co_flush_locked(intel_dp, frontbuffer_bits, origin);
psr2_dc3co_flush_locked(intel_dp, frontbuffer_bits);
goto unlock;
}
@@ -3832,7 +3745,7 @@ void intel_psr_init(struct intel_dp *intel_dp)
intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link;
INIT_WORK(&intel_dp->psr.work, intel_psr_work);
INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work);
INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, psr2_dc3co_disable_work);
mutex_init(&intel_dp->psr.lock);
}

View File

@@ -87,5 +87,6 @@ void intel_psr_compute_config_late(struct intel_dp *intel_dp,
int intel_psr_min_guardband(struct intel_crtc_state *crtc_state);
bool intel_psr_use_trans_push(const struct intel_crtc_state *crtc_state);
bool intel_psr_pr_async_video_timing_supported(struct intel_dp *intel_dp);
bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp);
#endif /* __INTEL_PSR_H__ */

View File

@@ -268,6 +268,7 @@
#define _PR_ALPM_CTL_A 0x60948
#define PR_ALPM_CTL(dev_priv, tran) _MMIO_TRANS2(dev_priv, tran, _PR_ALPM_CTL_A)
#define PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL BIT(7)
#define PR_ALPM_CTL_ALLOW_LINK_OFF_BETWEEN_AS_SDP_AND_SU BIT(6)
#define PR_ALPM_CTL_RFB_UPDATE_CONTROL BIT(5)
#define PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE BIT(4)

View File

@@ -1354,7 +1354,8 @@ static bool intel_sdvo_has_audio(struct intel_encoder *encoder,
return intel_conn_state->force_audio == HDMI_AUDIO_ON;
}
static int intel_sdvo_compute_config(struct intel_encoder *encoder,
static int intel_sdvo_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -1398,8 +1399,8 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder,
const struct drm_display_mode *fixed_mode;
int ret;
ret = intel_panel_compute_config(&intel_sdvo_connector->base,
adjusted_mode);
ret = intel_panel_compute_config(state, pipe_config,
&intel_sdvo_connector->base);
if (ret)
return ret;

View File

@@ -1187,13 +1187,12 @@ static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode,
}
static int
intel_tv_compute_config(struct intel_encoder *encoder,
intel_tv_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(encoder);
struct intel_atomic_state *state =
to_intel_atomic_state(pipe_config->uapi.state);
struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
struct intel_tv_connector_state *tv_conn_state =
to_intel_tv_connector_state(conn_state);

View File

@@ -7,8 +7,10 @@
#include <drm/drm_print.h>
#include "intel_alpm.h"
#include "intel_cmtg.h"
#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display_limits.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_dmc.h"
@@ -322,19 +324,19 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state)
return intel_vrr_fixed_rr_hw_vtotal(crtc_state);
}
void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state)
void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state,
enum transcoder transcoder)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
if (!intel_vrr_possible(crtc_state))
return;
intel_de_write(display, TRANS_VRR_VMIN(display, cpu_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, cpu_transcoder),
intel_de_write(display, TRANS_VRR_VMAX(display, transcoder),
intel_vrr_fixed_rr_hw_vmax(crtc_state) - 1);
intel_de_write(display, TRANS_VRR_FLIPLINE(display, cpu_transcoder),
intel_de_write(display, TRANS_VRR_FLIPLINE(display, transcoder),
intel_vrr_fixed_rr_hw_flipline(crtc_state) - 1);
}
@@ -649,7 +651,8 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
lower_32_bits(crtc_state->cmrr.cmrr_n));
}
intel_vrr_set_fixed_rr_timings(crtc_state);
intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder);
intel_cmtg_set_vrr_timings(crtc_state);
if (!intel_vrr_always_use_vrr_tg(display))
intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
@@ -934,6 +937,8 @@ static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state,
vrr_ctl |= VRR_CTL_CMRR_ENABLE;
intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl);
intel_cmtg_set_vrr_ctl(crtc_state);
}
static void intel_vrr_tg_disable(const struct intel_crtc_state *old_crtc_state)
@@ -978,7 +983,7 @@ void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state)
intel_vrr_tg_disable(old_crtc_state);
intel_vrr_disable_dc_balancing(old_crtc_state);
intel_vrr_set_fixed_rr_timings(old_crtc_state);
intel_vrr_set_fixed_rr_timings(old_crtc_state, old_crtc_state->cpu_transcoder);
}
void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state)
@@ -1101,16 +1106,6 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state)
crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display);
}
/*
* For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal
* bits are not filled. Since for these platforms TRAN_VMIN is always
* filled with crtc_vtotal, use TRAN_VRR_VMIN to get the vtotal for
* adjusted_mode.
*/
if (intel_vrr_always_use_vrr_tg(display))
crtc_state->hw.adjusted_mode.crtc_vtotal =
intel_vrr_vmin_vtotal(crtc_state);
if (HAS_AS_SDP(display)) {
trans_vrr_vsync =
intel_de_read(display,

View File

@@ -15,6 +15,7 @@ struct intel_crtc;
struct intel_crtc_state;
struct intel_dsb;
struct intel_display;
enum transcoder;
bool intel_vrr_is_capable(struct intel_connector *connector);
bool intel_vrr_is_in_range(struct intel_connector *connector, int vrefresh);
@@ -42,7 +43,8 @@ int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state);
bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state);
void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state);
void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state);
void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state);
void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state,
enum transcoder transcoder);
void intel_vrr_dcb_reset(const struct intel_crtc_state *old_crtc_state,
struct intel_crtc *crtc);
bool intel_vrr_always_use_vrr_tg(struct intel_display *display);

View File

@@ -266,7 +266,8 @@ static void band_gap_reset(struct intel_display *display)
vlv_flisdsi_put(display);
}
static int intel_dsi_compute_config(struct intel_encoder *encoder,
static int intel_dsi_compute_config(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
{
@@ -280,7 +281,7 @@ static int intel_dsi_compute_config(struct intel_encoder *encoder,
pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
ret = intel_panel_compute_config(intel_connector, adjusted_mode);
ret = intel_panel_compute_config(state, pipe_config, intel_connector);
if (ret)
return ret;

View File

@@ -17,8 +17,6 @@
#include "i915_vma_types.h"
enum intel_region_id;
struct drm_scanout_buffer;
struct intel_panic;
#define obj_to_i915(obj__) to_i915((obj__)->base.dev)
@@ -693,11 +691,6 @@ i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
int i915_gem_object_truncate(struct drm_i915_gem_object *obj);
struct intel_panic *i915_gem_object_alloc_panic(void);
int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
struct drm_gem_object *_obj, bool panic_tiling);
void i915_gem_object_panic_finish(struct intel_panic *panic);
/**
* i915_gem_object_pin_map - return a contiguous mapping of the entire object
* @obj: the object to map into kernel address space

View File

@@ -6,11 +6,8 @@
#include <linux/vmalloc.h>
#include <drm/drm_cache.h>
#include <drm/drm_panic.h>
#include <drm/drm_print.h>
#include "display/intel_fb.h"
#include "display/intel_display_types.h"
#include "gt/intel_gt.h"
#include "gt/intel_tlb.h"
@@ -359,131 +356,6 @@ static void *i915_gem_object_map_pfn(struct drm_i915_gem_object *obj,
return vaddr ?: ERR_PTR(-ENOMEM);
}
struct intel_panic {
struct page **pages;
int page;
void *vaddr;
};
static void i915_panic_kunmap(struct intel_panic *panic)
{
if (panic->vaddr) {
drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
kunmap_local(panic->vaddr);
panic->vaddr = NULL;
}
}
static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj)
{
unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i;
struct page *page;
struct page **pages;
struct sgt_iter iter;
/* For a 3840x2160 32 bits Framebuffer, this should require ~64K */
pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC);
if (!pages)
return NULL;
i = 0;
for_each_sgt_page(page, iter, obj->mm.pages)
pages[i++] = page;
return pages;
}
static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
unsigned int y, u32 color)
{
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
unsigned int offset = fb->panic_tiling(sb->width, x, y);
iosys_map_wr(&sb->map[0], offset, u32, color);
}
/*
* The scanout buffer pages are not mapped, so for each pixel,
* use kmap_local_page_try_from_panic() to map the page, and write the pixel.
* Try to keep the map from the previous pixel, to avoid too much map/unmap.
*/
static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
unsigned int y, u32 color)
{
unsigned int new_page;
unsigned int offset;
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
struct intel_panic *panic = fb->panic;
if (fb->panic_tiling)
offset = fb->panic_tiling(sb->width, x, y);
else
offset = y * sb->pitch[0] + x * sb->format->cpp[0];
new_page = offset >> PAGE_SHIFT;
offset = offset % PAGE_SIZE;
if (new_page != panic->page) {
i915_panic_kunmap(panic);
panic->page = new_page;
panic->vaddr =
kmap_local_page_try_from_panic(panic->pages[panic->page]);
}
if (panic->vaddr) {
u32 *pix = panic->vaddr + offset;
*pix = color;
}
}
struct intel_panic *i915_gem_object_alloc_panic(void)
{
struct intel_panic *panic;
panic = kzalloc_obj(*panic);
return panic;
}
/*
* Setup the gem framebuffer for drm_panic access.
* Use current vaddr if it exists, or setup a list of pages.
* pfn is not supported yet.
*/
int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
struct drm_gem_object *_obj, bool panic_tiling)
{
enum i915_map_type has_type;
struct drm_i915_gem_object *obj = to_intel_bo(_obj);
void *ptr;
ptr = page_unpack_bits(obj->mm.mapping, &has_type);
if (ptr) {
if (i915_gem_object_has_iomem(obj))
iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr);
else
iosys_map_set_vaddr(&sb->map[0], ptr);
if (panic_tiling)
sb->set_pixel = i915_gem_object_panic_map_set_pixel;
return 0;
}
if (i915_gem_object_has_struct_page(obj)) {
panic->pages = i915_gem_object_panic_pages(obj);
if (!panic->pages)
return -ENOMEM;
panic->page = -1;
sb->set_pixel = i915_gem_object_panic_page_set_pixel;
return 0;
}
return -EOPNOTSUPP;
}
void i915_gem_object_panic_finish(struct intel_panic *panic)
{
i915_panic_kunmap(panic);
panic->page = -1;
kfree(panic->pages);
panic->pages = NULL;
}
/* get, pin, and map the pages of the object into kernel space */
void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
enum i915_map_type type)

View File

@@ -0,0 +1,147 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2026 Intel Corporation */
#include <drm/drm_cache.h>
#include <drm/drm_panic.h>
#include <drm/intel/display_parent_interface.h>
#include "i915_gem_object.h"
#include "i915_gem_panic.h"
struct intel_panic {
struct page **pages;
int page;
void *vaddr;
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width);
};
static void i915_panic_kunmap(struct intel_panic *panic)
{
if (panic->vaddr) {
drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
kunmap_local(panic->vaddr);
panic->vaddr = NULL;
}
}
static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj)
{
unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i;
struct page *page;
struct page **pages;
struct sgt_iter iter;
/* For a 3840x2160 32 bits Framebuffer, this should require ~64K */
pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC);
if (!pages)
return NULL;
i = 0;
for_each_sgt_page(page, iter, obj->mm.pages)
pages[i++] = page;
return pages;
}
static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
unsigned int y, u32 color)
{
struct intel_panic *panic = sb->private;
unsigned int offset = panic->tiling(sb->width, x, y);
iosys_map_wr(&sb->map[0], offset, u32, color);
}
/*
* The scanout buffer pages are not mapped, so for each pixel,
* use kmap_local_page_try_from_panic() to map the page, and write the pixel.
* Try to keep the map from the previous pixel, to avoid too much map/unmap.
*/
static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
unsigned int y, u32 color)
{
struct intel_panic *panic = sb->private;
unsigned int new_page;
unsigned int offset;
if (panic->tiling)
offset = panic->tiling(sb->width, x, y);
else
offset = y * sb->pitch[0] + x * sb->format->cpp[0];
new_page = offset >> PAGE_SHIFT;
offset = offset % PAGE_SIZE;
if (new_page != panic->page) {
i915_panic_kunmap(panic);
panic->page = new_page;
panic->vaddr =
kmap_local_page_try_from_panic(panic->pages[panic->page]);
}
if (panic->vaddr) {
u32 *pix = panic->vaddr + offset;
*pix = color;
}
}
static struct intel_panic *i915_gem_object_alloc_panic(void)
{
struct intel_panic *panic;
panic = kzalloc_obj(*panic);
return panic;
}
/*
* Setup the gem framebuffer for drm_panic access.
* Use current vaddr if it exists, or setup a list of pages.
* pfn is not supported yet.
*/
static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
struct drm_gem_object *_obj,
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width))
{
enum i915_map_type has_type;
struct drm_i915_gem_object *obj = to_intel_bo(_obj);
void *ptr;
sb->private = panic;
ptr = page_unpack_bits(obj->mm.mapping, &has_type);
if (ptr) {
if (i915_gem_object_has_iomem(obj))
iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr);
else
iosys_map_set_vaddr(&sb->map[0], ptr);
if (tiling) {
panic->tiling = tiling;
sb->set_pixel = i915_gem_object_panic_map_set_pixel;
}
return 0;
}
if (i915_gem_object_has_struct_page(obj)) {
panic->pages = i915_gem_object_panic_pages(obj);
if (!panic->pages)
return -ENOMEM;
panic->page = -1;
panic->tiling = tiling;
sb->set_pixel = i915_gem_object_panic_page_set_pixel;
return 0;
}
return -EOPNOTSUPP;
}
static void i915_gem_object_panic_finish(struct intel_panic *panic)
{
i915_panic_kunmap(panic);
panic->page = -1;
kfree(panic->pages);
panic->pages = NULL;
}
const struct intel_display_panic_interface i915_display_panic_interface = {
.alloc = i915_gem_object_alloc_panic,
.setup = i915_gem_object_panic_setup,
.finish = i915_gem_object_panic_finish,
};

View File

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2026 Intel Corporation */
#ifndef __I915_GEM_PANIC_H__
#define __I915_GEM_PANIC_H__
#include <linux/types.h>
extern const struct intel_display_panic_interface i915_display_panic_interface;
#endif /* __I915_GEM_PANIC_H__ */

View File

@@ -286,7 +286,7 @@ static void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id)
ret = generic_handle_irq_safe(gt->gsc.intf[intf_id].irq);
if (ret)
gt_err_ratelimited(gt, "error handling GSC irq: %d\n", ret);
gt_err_ratelimited(gt, "GSC: irq handling failed (%pe)\n", ERR_PTR(ret));
}
void intel_gsc_irq_handler(struct intel_gt *gt, u32 iir)

View File

@@ -90,13 +90,13 @@ static inline int cnp_get_port_from_gmbus0(u32 gmbus0)
int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK;
int port = -EINVAL;
if (port_select == GMBUS_PIN_1_BXT)
if (port_select == GMBUS_PIN_1)
port = PORT_B;
else if (port_select == GMBUS_PIN_2_BXT)
else if (port_select == GMBUS_PIN_2)
port = PORT_C;
else if (port_select == GMBUS_PIN_3_BXT)
else if (port_select == GMBUS_PIN_3)
port = PORT_D;
else if (port_select == GMBUS_PIN_4_CNP)
else if (port_select == GMBUS_PIN_4)
port = PORT_E;
return port;
}
@@ -106,11 +106,11 @@ static inline int bxt_get_port_from_gmbus0(u32 gmbus0)
int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK;
int port = -EINVAL;
if (port_select == GMBUS_PIN_1_BXT)
if (port_select == GMBUS_PIN_1)
port = PORT_B;
else if (port_select == GMBUS_PIN_2_BXT)
else if (port_select == GMBUS_PIN_2)
port = PORT_C;
else if (port_select == GMBUS_PIN_3_BXT)
else if (port_select == GMBUS_PIN_3)
port = PORT_D;
return port;
}

View File

@@ -51,7 +51,6 @@
#include <drm/intel/intel_pcode_regs.h>
#include "display/i9xx_display_sr.h"
#include "display/intel_bw.h"
#include "display/intel_cdclk.h"
#include "display/intel_crtc.h"
#include "display/intel_display_device.h"
@@ -60,8 +59,6 @@
#include "display/intel_dmc.h"
#include "display/intel_dp.h"
#include "display/intel_dpt.h"
#include "display/intel_dram.h"
#include "display/intel_encoder.h"
#include "display/intel_fbdev.h"
#include "display/intel_gmbus.h"
#include "display/intel_hotplug.h"
@@ -79,6 +76,7 @@
#include "gem/i915_gem_ioctls.h"
#include "gem/i915_gem_mman.h"
#include "gem/i915_gem_object_frontbuffer.h"
#include "gem/i915_gem_panic.h"
#include "gem/i915_gem_pm.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_pm.h"
@@ -111,7 +109,6 @@
#include "i915_irq.h"
#include "i915_memcpy.h"
#include "i915_overlay.h"
#include "i915_panic.h"
#include "i915_perf.h"
#include "i915_query.h"
#include "i915_reg.h"
@@ -470,7 +467,6 @@ static int i915_pcode_init(struct drm_i915_private *i915)
*/
static int i915_driver_hw_probe(struct drm_i915_private *dev_priv)
{
struct intel_display *display = dev_priv->display;
struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
int ret;
@@ -564,28 +560,14 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv)
drm_dbg(&dev_priv->drm, "can't enable MSI");
}
intel_opregion_setup(display);
ret = i915_pcode_init(dev_priv);
if (ret)
goto err_opregion;
/*
* Fill the dram structure to get the system dram info. This will be
* used for memory latency calculation.
*/
ret = intel_dram_detect(display);
if (ret)
goto err_opregion;
intel_bw_init_hw(display);
goto err_msi;
return 0;
err_opregion:
intel_opregion_cleanup(display);
if (pdev->msi_enabled)
pci_disable_msi(pdev);
err_msi:
pci_disable_msi(pdev);
err_mem_regions:
intel_memory_regions_driver_release(dev_priv);
err_ggtt:
@@ -604,15 +586,11 @@ ALLOW_ERROR_INJECTION(i915_driver_hw_probe, ERRNO);
*/
static void i915_driver_hw_remove(struct drm_i915_private *dev_priv)
{
struct intel_display *display = dev_priv->display;
struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
i915_perf_fini(dev_priv);
intel_opregion_cleanup(display);
if (pdev->msi_enabled)
pci_disable_msi(pdev);
pci_disable_msi(pdev);
}
/**
@@ -803,7 +781,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent)
const struct intel_device_info *match_info =
(struct intel_device_info *)ent->driver_data;
struct drm_i915_private *i915;
struct intel_display *display;
i915 = devm_drm_dev_alloc(&pdev->dev, &i915_drm_driver,
struct drm_i915_private, drm);
@@ -818,12 +795,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Set up device info and initial runtime info. */
intel_device_info_driver_create(i915, pdev->device, match_info);
display = intel_display_device_probe(pdev, &parent);
if (IS_ERR(display))
return ERR_CAST(display);
i915->display = display;
return i915;
}
@@ -856,7 +827,13 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return PTR_ERR(i915);
}
display = i915->display;
display = intel_display_device_probe(pdev, &parent);
if (IS_ERR(display)) {
ret = PTR_ERR(display);
goto out_pci_disable;
}
i915->display = display;
ret = i915_driver_early_probe(i915);
if (ret < 0)
@@ -971,7 +948,6 @@ void i915_driver_remove(struct drm_i915_private *i915)
intel_display_driver_remove(display);
intel_irq_uninstall(i915);
intel_hpd_cancel_work(display);
intel_display_driver_remove_noirq(display);
@@ -1043,43 +1019,24 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
disable_rpm_wakeref_asserts(&i915->runtime_pm);
intel_runtime_pm_disable(&i915->runtime_pm);
intel_display_power_disable(display);
drm_client_dev_suspend(&i915->drm);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(&i915->drm);
intel_display_driver_disable_user_access(display);
drm_atomic_helper_shutdown(&i915->drm);
}
intel_dp_mst_suspend(display);
intel_display_driver_shutdown(display);
intel_irq_suspend(i915);
intel_hpd_cancel_work(display);
if (intel_display_device_present(display))
intel_display_driver_suspend_access(display);
intel_encoder_suspend_all(display);
intel_encoder_shutdown_all(display);
intel_dmc_suspend(display);
i915_gem_suspend(i915);
/*
* The only requirement is to reboot with display DC states disabled,
* for now leaving all display power wells in the INIT power domain
* enabled.
*
* TODO:
* - unify the pci_driver::shutdown sequence here with the
* pci_driver.driver.pm.poweroff,poweroff_late sequence.
* - unify the driver remove and system/runtime suspend sequences with
* the above unified shutdown/poweroff sequence.
*/
intel_display_power_driver_remove(display);
intel_display_driver_shutdown_late(display);
enable_rpm_wakeref_asserts(&i915->runtime_pm);
intel_runtime_pm_driver_last_release(&i915->runtime_pm);
@@ -1108,10 +1065,10 @@ static int i915_drm_prepare(struct drm_device *dev)
intel_pxp_suspend_prepare(i915->pxp);
/*
* NB intel_display_driver_suspend() may issue new requests after we've
* ostensibly marked the GPU as ready-to-sleep here. We need to
* split out that work and pull it forward so that after point,
* the GPU is not woken again.
* NB intel_display_driver_pm_suspend() may issue new requests after
* we've ostensibly marked the GPU as ready-to-sleep here. We need to
* split out that work and pull it forward so that after point, the GPU
* is not woken again.
*/
return i915_gem_backup_suspend(i915);
}
@@ -1124,24 +1081,9 @@ static int i915_drm_suspend(struct drm_device *dev)
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
/* We do a lot of poking in a lot of registers, make sure they work
* properly. */
intel_display_power_disable(display);
drm_client_dev_suspend(dev);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(dev);
intel_display_driver_disable_user_access(display);
}
intel_display_driver_suspend(display);
intel_display_driver_pm_suspend(display);
intel_irq_suspend(dev_priv);
intel_hpd_cancel_work(display);
if (intel_display_device_present(display))
intel_display_driver_suspend_access(display);
intel_encoder_suspend_all(display);
/* Must be called before GGTT is suspended. */
intel_dpt_suspend(display);
@@ -1181,12 +1123,12 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
for_each_gt(gt, dev_priv, i)
intel_uncore_suspend(gt->uncore);
intel_display_power_suspend_late(display, s2idle);
intel_display_driver_pm_suspend_late(display, s2idle);
ret = vlv_suspend_complete(dev_priv);
if (ret) {
drm_err(&dev_priv->drm, "Suspend complete failed: %d\n", ret);
intel_display_power_resume_early(display);
intel_display_driver_pm_resume_early(display);
}
enable_rpm_wakeref_asserts(rpm);
@@ -1309,24 +1251,7 @@ static int i915_drm_resume(struct drm_device *dev)
intel_clock_gating_init(&dev_priv->drm);
if (intel_display_device_present(display))
intel_display_driver_resume_access(display);
intel_hpd_init(display);
intel_display_driver_resume(display);
if (intel_display_device_present(display)) {
intel_display_driver_enable_user_access(display);
drm_kms_helper_poll_enable(dev);
}
intel_hpd_poll_disable(display);
intel_opregion_resume(display);
drm_client_dev_resume(dev);
intel_display_power_enable(display);
intel_display_driver_pm_resume(display);
intel_gvt_resume(dev_priv);
@@ -1362,7 +1287,7 @@ static int i915_drm_resume_early(struct drm_device *dev)
for_each_gt(gt, dev_priv, i)
intel_gt_resume_early(gt);
intel_display_power_resume_early(display);
intel_display_driver_pm_resume_early(display);
enable_rpm_wakeref_asserts(&dev_priv->runtime_pm);

View File

@@ -1,35 +0,0 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
#include <drm/drm_panic.h>
#include <drm/intel/display_parent_interface.h>
#include "display/intel_display_types.h"
#include "display/intel_fb.h"
#include "gem/i915_gem_object.h"
#include "i915_panic.h"
static struct intel_panic *intel_panic_alloc(void)
{
return i915_gem_object_alloc_panic();
}
static int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
{
struct intel_framebuffer *fb = sb->private;
struct drm_gem_object *obj = intel_fb_bo(&fb->base);
return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling);
}
static void intel_panic_finish(struct intel_panic *panic)
{
return i915_gem_object_panic_finish(panic);
}
const struct intel_display_panic_interface i915_display_panic_interface = {
.alloc = intel_panic_alloc,
.setup = intel_panic_setup,
.finish = intel_panic_finish,
};

View File

@@ -1,9 +0,0 @@
/* SPDX-License-Identifier: MIT */
/* Copyright © 2025 Intel Corporation */
#ifndef __I915_PANIC_H__
#define __I915_PANIC_H__
extern const struct intel_display_panic_interface i915_display_panic_interface;
#endif /* __I915_PANIC_H__ */

View File

@@ -278,6 +278,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_dp_aux.o \
i915-display/intel_dp_aux_backlight.o \
i915-display/intel_dp_hdcp.o \
i915-display/intel_dp_link_caps.o \
i915-display/intel_dp_link_training.o \
i915-display/intel_dp_mst.o \
i915-display/intel_dp_test.o \

View File

@@ -19,7 +19,6 @@
#include "intel_acpi.h"
#include "intel_audio.h"
#include "intel_bw.h"
#include "intel_display.h"
#include "intel_display_core.h"
#include "intel_display_device.h"
@@ -29,8 +28,6 @@
#include "intel_dmc.h"
#include "intel_dmc_wl.h"
#include "intel_dp.h"
#include "intel_dram.h"
#include "intel_encoder.h"
#include "intel_fbdev.h"
#include "intel_hdcp.h"
#include "intel_hotplug.h"
@@ -84,10 +81,8 @@ static void xe_display_fini_early(void *arg)
if (!xe->info.probe_display)
return;
intel_hpd_cancel_work(display);
intel_display_driver_remove_nogem(display);
intel_display_driver_remove_noirq(display);
intel_opregion_cleanup(display);
intel_display_driver_remove_nogem(display);
intel_display_power_cleanup(display);
}
@@ -113,22 +108,9 @@ int xe_display_init_early(struct xe_device *xe)
return 0;
}
/* Early display init.. */
intel_opregion_setup(display);
/*
* Fill the dram structure to get the system dram info. This will be
* used for memory latency calculation.
*/
err = intel_dram_detect(display);
if (err)
goto err_opregion;
intel_bw_init_hw(display);
err = intel_display_driver_probe_noirq(display);
if (err)
goto err_opregion;
return err;
err = intel_display_driver_probe_nogem(display);
if (err)
@@ -138,8 +120,7 @@ int xe_display_init_early(struct xe_device *xe)
err_noirq:
intel_display_driver_remove_noirq(display);
intel_display_power_cleanup(display);
err_opregion:
intel_opregion_cleanup(display);
return err;
}
@@ -191,6 +172,30 @@ void xe_display_unregister(struct xe_device *xe)
intel_display_driver_unregister(display);
}
void xe_display_shutdown(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_driver_shutdown(display);
intel_opregion_suspend(display, PCI_D3cold);
intel_dmc_suspend(display);
}
void xe_display_shutdown_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_driver_shutdown_late(display);
}
/* IRQ-related functions */
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl)
@@ -244,6 +249,59 @@ static bool suspend_to_idle(void)
return false;
}
void xe_display_pm_suspend(struct xe_device *xe)
{
struct intel_display *display = xe->display;
bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
intel_display_driver_pm_suspend(display);
intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold);
intel_dmc_suspend(display);
}
void xe_display_pm_suspend_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
intel_display_driver_pm_suspend_late(display, s2idle);
}
void xe_display_pm_resume_early(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_driver_pm_resume_early(display);
}
void xe_display_pm_resume(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_dmc_resume(display);
if (intel_display_device_present(display))
drm_mode_config_reset(&xe->drm);
intel_display_driver_init_hw(display);
intel_display_driver_pm_resume(display);
}
static void xe_display_enable_d3cold(struct xe_device *xe)
{
struct intel_display *display = xe->display;
@@ -291,75 +349,6 @@ static void xe_display_disable_d3cold(struct xe_device *xe)
intel_display_power_enable(display);
}
void xe_display_pm_suspend(struct xe_device *xe)
{
struct intel_display *display = xe->display;
bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
/*
* We do a lot of poking in a lot of registers, make sure they work
* properly.
*/
intel_display_power_disable(display);
drm_client_dev_suspend(&xe->drm);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(&xe->drm);
intel_display_driver_disable_user_access(display);
intel_display_driver_suspend(display);
}
intel_display_flush_cleanup_work(display);
intel_encoder_block_all_hpds(display);
intel_hpd_cancel_work(display);
if (intel_display_device_present(display)) {
intel_display_driver_suspend_access(display);
intel_encoder_suspend_all(display);
}
intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold);
intel_dmc_suspend(display);
}
void xe_display_pm_shutdown(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_power_disable(display);
drm_client_dev_suspend(&xe->drm);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(&xe->drm);
intel_display_driver_disable_user_access(display);
intel_display_driver_suspend(display);
}
intel_display_flush_cleanup_work(display);
intel_dp_mst_suspend(display);
intel_encoder_block_all_hpds(display);
intel_hpd_cancel_work(display);
if (intel_display_device_present(display))
intel_display_driver_suspend_access(display);
intel_encoder_suspend_all(display);
intel_encoder_shutdown_all(display);
intel_opregion_suspend(display, PCI_D3cold);
intel_dmc_suspend(display);
}
void xe_display_pm_runtime_suspend(struct xe_device *xe)
{
struct intel_display *display = xe->display;
@@ -375,17 +364,6 @@ void xe_display_pm_runtime_suspend(struct xe_device *xe)
intel_hpd_poll_enable(display);
}
void xe_display_pm_suspend_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
bool s2idle = suspend_to_idle();
if (!xe->info.probe_display)
return;
intel_display_power_suspend_late(display, s2idle);
}
void xe_display_pm_runtime_suspend_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
@@ -404,68 +382,6 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe)
intel_dmc_wl_flush_release_work(display);
}
void xe_display_pm_shutdown_late(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
/*
* The only requirement is to reboot with display DC states disabled,
* for now leaving all display power wells in the INIT power domain
* enabled.
*/
intel_display_power_driver_remove(display);
}
void xe_display_pm_resume_early(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_display_power_resume_early(display);
}
void xe_display_pm_resume(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
intel_dmc_resume(display);
if (intel_display_device_present(display))
drm_mode_config_reset(&xe->drm);
intel_display_driver_init_hw(display);
if (intel_display_device_present(display))
intel_display_driver_resume_access(display);
intel_hpd_init(display);
intel_encoder_unblock_all_hpds(display);
if (intel_display_device_present(display)) {
intel_display_driver_resume(display);
drm_kms_helper_poll_enable(&xe->drm);
intel_display_driver_enable_user_access(display);
}
if (intel_display_device_present(display))
intel_hpd_poll_disable(display);
intel_opregion_resume(display);
drm_client_dev_resume(&xe->drm);
intel_display_power_enable(display);
}
void xe_display_pm_runtime_resume(struct xe_device *xe)
{
struct intel_display *display = xe->display;

View File

@@ -29,15 +29,16 @@ int xe_display_init(struct xe_device *xe);
void xe_display_register(struct xe_device *xe);
void xe_display_unregister(struct xe_device *xe);
void xe_display_shutdown(struct xe_device *xe);
void xe_display_shutdown_late(struct xe_device *xe);
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl);
void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir);
void xe_display_irq_reset(struct xe_device *xe);
void xe_display_irq_postinstall(struct xe_device *xe);
void xe_display_pm_suspend(struct xe_device *xe);
void xe_display_pm_shutdown(struct xe_device *xe);
void xe_display_pm_suspend_late(struct xe_device *xe);
void xe_display_pm_shutdown_late(struct xe_device *xe);
void xe_display_pm_resume_early(struct xe_device *xe);
void xe_display_pm_resume(struct xe_device *xe);
void xe_display_pm_runtime_suspend(struct xe_device *xe);
@@ -65,15 +66,16 @@ static inline int xe_display_init(struct xe_device *xe) { return 0; }
static inline void xe_display_register(struct xe_device *xe) {}
static inline void xe_display_unregister(struct xe_device *xe) {}
static inline void xe_display_shutdown(struct xe_device *xe) {}
static inline void xe_display_shutdown_late(struct xe_device *xe) {}
static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {}
static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {}
static inline void xe_display_irq_reset(struct xe_device *xe) {}
static inline void xe_display_irq_postinstall(struct xe_device *xe) {}
static inline void xe_display_pm_suspend(struct xe_device *xe) {}
static inline void xe_display_pm_shutdown(struct xe_device *xe) {}
static inline void xe_display_pm_suspend_late(struct xe_device *xe) {}
static inline void xe_display_pm_shutdown_late(struct xe_device *xe) {}
static inline void xe_display_pm_resume_early(struct xe_device *xe) {}
static inline void xe_display_pm_resume(struct xe_device *xe) {}
static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {}

View File

@@ -5,8 +5,6 @@
#include <drm/drm_panic.h>
#include <drm/intel/display_parent_interface.h>
#include "intel_display_types.h"
#include "intel_fb.h"
#include "xe_bo.h"
#include "xe_panic.h"
#include "xe_res_cursor.h"
@@ -16,6 +14,9 @@ struct intel_panic {
struct iosys_map vmap;
int page;
struct xe_bo *bo;
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width);
};
static void xe_panic_kunmap(struct intel_panic *panic)
@@ -36,14 +37,13 @@ static void xe_panic_kunmap(struct intel_panic *panic)
static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x,
unsigned int y, u32 color)
{
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
struct intel_panic *panic = fb->panic;
struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
struct intel_panic *panic = sb->private;
struct xe_bo *bo = panic->bo;
unsigned int new_page;
unsigned int offset;
if (fb->panic_tiling)
offset = fb->panic_tiling(sb->width, x, y);
if (panic->tiling)
offset = panic->tiling(sb->width, x, y);
else
offset = y * sb->pitch[0] + x * sb->format->cpp[0];
@@ -84,16 +84,22 @@ static struct intel_panic *xe_panic_alloc(void)
return panic;
}
static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb,
struct drm_gem_object *obj,
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width))
{
struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
struct xe_bo *bo = gem_to_xe_bo(obj);
if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo))
return -ENODEV;
panic->page = -1;
panic->bo = bo;
panic->tiling = tiling;
sb->private = panic;
sb->set_pixel = xe_panic_page_set_pixel;
return 0;
}

View File

@@ -1129,14 +1129,14 @@ void xe_device_shutdown(struct xe_device *xe)
drm_dbg(&xe->drm, "Shutting down device\n");
xe_display_pm_shutdown(xe);
xe_display_shutdown(xe);
xe_irq_suspend(xe);
for_each_gt(gt, xe, id)
xe_gt_shutdown(gt);
xe_display_pm_shutdown_late(xe);
xe_display_shutdown_late(xe);
if (!xe_driver_flr_disabled(xe)) {
/* BOOM! */

View File

@@ -193,6 +193,7 @@ enum drm_mode_status {
#define DRM_MODE_MATCH_FLAGS (1 << 2)
#define DRM_MODE_MATCH_3D_FLAGS (1 << 3)
#define DRM_MODE_MATCH_ASPECT_RATIO (1 << 4)
#define DRM_MODE_MATCH_TIMINGS_VRR (1 << 5)
/**
* struct drm_display_mode - DRM kernel-internal display mode structure

View File

@@ -167,7 +167,9 @@ struct intel_display_overlay_interface {
struct intel_display_panic_interface {
struct intel_panic *(*alloc)(void);
int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb);
int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb,
struct drm_gem_object *obj,
unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width));
void (*finish)(struct intel_panic *panic);
};

View File

@@ -6,8 +6,6 @@
#ifndef __INTEL_MCHBAR_REGS__
#define __INTEL_MCHBAR_REGS__
#include "i915_reg_defs.h"
/*
* MCHBAR mirror.
*