aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/display')
-rw-r--r--drivers/gpu/drm/i915/display/hsw_ips.c94
-rw-r--r--drivers/gpu/drm/i915/display/hsw_ips.h1
-rw-r--r--drivers/gpu/drm/i915/display/icl_dsi.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic_plane.c64
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic_plane.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_audio.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_bios.c46
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c30
-rw-r--r--drivers/gpu/drm/i915/display/intel_color.c101
-rw-r--r--drivers/gpu/drm/i915/display/intel_color.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_crt.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc.c10
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc_state_dump.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc_state_dump.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c135
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.h6
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c264
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.h10
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_core.h23
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.c434
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.h6
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.c18
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_well.c7
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_types.h27
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc.c36
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c92
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux.c48
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux_regs.h84
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.c48
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c57
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt.c27
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb_regs.h67
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_vbt.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_vbt.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb.c11
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev.c24
-rw-r--r--drivers/gpu/drm/i915/display/intel_fdi.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_fdi_regs.h151
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.c160
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_gsc.c831
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_gsc.h26
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.c8
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug.c9
-rw-r--r--drivers/gpu/drm/i915/display/intel_lvds.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_setup.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_opregion.c40
-rw-r--r--drivers/gpu/drm/i915/display/intel_opregion.h5
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch_display.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_pps.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_pps_regs.h78
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.c390
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr_regs.h260
-rw-r--r--drivers/gpu/drm/i915/display/intel_qp_tables.c187
-rw-r--r--drivers/gpu/drm/i915/display/intel_qp_tables.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite.c183
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite_uapi.c127
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite_uapi.h15
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc.c1425
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc.h9
-rw-r--r--drivers/gpu/drm/i915/display/intel_tv.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_tv_regs.h490
-rw-r--r--drivers/gpu/drm/i915/display/intel_vblank.c92
-rw-r--r--drivers/gpu/drm/i915/display/intel_vblank.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_vdsc.c132
-rw-r--r--drivers/gpu/drm/i915/display/intel_vdsc_regs.h28
-rw-r--r--drivers/gpu/drm/i915/display/intel_wm.c2
-rw-r--r--drivers/gpu/drm/i915/display/skl_scaler.c17
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c7
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.c159
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark_regs.h160
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi.c24
77 files changed, 5358 insertions, 1490 deletions
diff --git a/drivers/gpu/drm/i915/display/hsw_ips.c b/drivers/gpu/drm/i915/display/hsw_ips.c
index 83aa3800245f..8eca0de065b6 100644
--- a/drivers/gpu/drm/i915/display/hsw_ips.c
+++ b/drivers/gpu/drm/i915/display/hsw_ips.c
@@ -14,6 +14,7 @@ static void hsw_ips_enable(const struct intel_crtc_state *crtc_state)
{
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ u32 val;
if (!crtc_state->ips_enabled)
return;
@@ -26,10 +27,15 @@ static void hsw_ips_enable(const struct intel_crtc_state *crtc_state)
drm_WARN_ON(&i915->drm,
!(crtc_state->active_planes & ~BIT(PLANE_CURSOR)));
+ val = IPS_ENABLE;
+
+ if (i915->display.ips.false_color)
+ val |= IPS_FALSE_COLOR;
+
if (IS_BROADWELL(i915)) {
drm_WARN_ON(&i915->drm,
snb_pcode_write(&i915->uncore, DISPLAY_IPS_CONTROL,
- IPS_ENABLE | IPS_PCODE_CONTROL));
+ val | IPS_PCODE_CONTROL));
/*
* Quoting Art Runyan: "its not safe to expect any particular
* value in IPS_CTL bit 31 after enabling IPS through the
@@ -37,7 +43,7 @@ static void hsw_ips_enable(const struct intel_crtc_state *crtc_state)
* so we need to just enable it and continue on.
*/
} else {
- intel_de_write(i915, IPS_CTL, IPS_ENABLE);
+ intel_de_write(i915, IPS_CTL, val);
/*
* The bit only becomes 1 in the next vblank, so this wait here
* is essentially intel_wait_for_vblank. If we don't have this
@@ -267,3 +273,87 @@ void hsw_ips_get_config(struct intel_crtc_state *crtc_state)
crtc_state->ips_enabled = true;
}
}
+
+static int hsw_ips_debugfs_false_color_get(void *data, u64 *val)
+{
+ struct intel_crtc *crtc = data;
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+
+ *val = i915->display.ips.false_color;
+
+ return 0;
+}
+
+static int hsw_ips_debugfs_false_color_set(void *data, u64 val)
+{
+ struct intel_crtc *crtc = data;
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ struct intel_crtc_state *crtc_state;
+ int ret;
+
+ ret = drm_modeset_lock(&crtc->base.mutex, NULL);
+ if (ret)
+ return ret;
+
+ i915->display.ips.false_color = val;
+
+ crtc_state = to_intel_crtc_state(crtc->base.state);
+
+ if (!crtc_state->hw.active)
+ goto unlock;
+
+ if (crtc_state->uapi.commit &&
+ !try_wait_for_completion(&crtc_state->uapi.commit->hw_done))
+ goto unlock;
+
+ hsw_ips_enable(crtc_state);
+
+ unlock:
+ drm_modeset_unlock(&crtc->base.mutex);
+
+ return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(hsw_ips_debugfs_false_color_fops,
+ hsw_ips_debugfs_false_color_get,
+ hsw_ips_debugfs_false_color_set,
+ "%llu\n");
+
+static int hsw_ips_debugfs_status_show(struct seq_file *m, void *unused)
+{
+ struct intel_crtc *crtc = m->private;
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ intel_wakeref_t wakeref;
+
+ wakeref = intel_runtime_pm_get(&i915->runtime_pm);
+
+ seq_printf(m, "Enabled by kernel parameter: %s\n",
+ str_yes_no(i915->params.enable_ips));
+
+ if (DISPLAY_VER(i915) >= 8) {
+ seq_puts(m, "Currently: unknown\n");
+ } else {
+ if (intel_de_read(i915, IPS_CTL) & IPS_ENABLE)
+ seq_puts(m, "Currently: enabled\n");
+ else
+ seq_puts(m, "Currently: disabled\n");
+ }
+
+ intel_runtime_pm_put(&i915->runtime_pm, wakeref);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(hsw_ips_debugfs_status);
+
+void hsw_ips_crtc_debugfs_add(struct intel_crtc *crtc)
+{
+ if (!hsw_crtc_supports_ips(crtc))
+ return;
+
+ debugfs_create_file("i915_ips_false_color", 0644, crtc->base.debugfs_entry,
+ crtc, &hsw_ips_debugfs_false_color_fops);
+
+ debugfs_create_file("i915_ips_status", 0444, crtc->base.debugfs_entry,
+ crtc, &hsw_ips_debugfs_status_fops);
+}
diff --git a/drivers/gpu/drm/i915/display/hsw_ips.h b/drivers/gpu/drm/i915/display/hsw_ips.h
index 4564dee497d7..4eb83b350791 100644
--- a/drivers/gpu/drm/i915/display/hsw_ips.h
+++ b/drivers/gpu/drm/i915/display/hsw_ips.h
@@ -22,5 +22,6 @@ bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state);
int hsw_ips_compute_config(struct intel_atomic_state *state,
struct intel_crtc *crtc);
void hsw_ips_get_config(struct intel_crtc_state *crtc_state);
+void hsw_ips_crtc_debugfs_add(struct intel_crtc *crtc);
#endif /* __HSW_IPS_H__ */
diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index 50dcaa895854..c9aeba0ecf91 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -1140,7 +1140,7 @@ static void gen11_dsi_powerup_panel(struct intel_encoder *encoder)
/* panel power on related mipi dsi vbt sequences */
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_POWER_ON);
- intel_dsi_msleep(intel_dsi, intel_dsi->panel_on_delay);
+ msleep(intel_dsi->panel_on_delay);
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET);
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_INIT_OTP);
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON);
@@ -1500,7 +1500,7 @@ static void gen11_dsi_get_config(struct intel_encoder *encoder,
gen11_dsi_get_timings(encoder, pipe_config);
pipe_config->output_types |= BIT(INTEL_OUTPUT_DSI);
- pipe_config->pipe_bpp = bdw_get_pipemisc_bpp(crtc);
+ pipe_config->pipe_bpp = bdw_get_pipe_misc_bpp(crtc);
/* Get the details on which TE should be enabled */
if (is_cmd_mode(intel_dsi))
@@ -1552,8 +1552,6 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder,
if (crtc_state->dsc.slice_count > 1)
crtc_state->dsc.dsc_split = true;
- vdsc_cfg->convert_rgb = true;
-
/* FIXME: initialize from VBT */
vdsc_cfg->rc_model_size = DSC_RC_MODEL_SIZE_CONST;
diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
index 719a60e278f3..f33164b10292 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
@@ -32,6 +32,7 @@
*/
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
#include "i915_config.h"
@@ -42,7 +43,6 @@
#include "intel_display_types.h"
#include "intel_fb.h"
#include "intel_fb_pin.h"
-#include "intel_sprite.h"
#include "skl_scaler.h"
#include "skl_watermark.h"
@@ -940,6 +940,64 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state,
return 0;
}
+int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state)
+{
+ struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev);
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+ struct drm_rect *src = &plane_state->uapi.src;
+ u32 src_x, src_y, src_w, src_h, hsub, vsub;
+ bool rotated = drm_rotation_90_or_270(plane_state->hw.rotation);
+
+ /*
+ * FIXME hsub/vsub vs. block size is a mess. Pre-tgl CCS
+ * abuses hsub/vsub so we can't use them here. But as they
+ * are limited to 32bpp RGB formats we don't actually need
+ * to check anything.
+ */
+ if (fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
+ fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS)
+ return 0;
+
+ /*
+ * Hardware doesn't handle subpixel coordinates.
+ * Adjust to (macro)pixel boundary, but be careful not to
+ * increase the source viewport size, because that could
+ * push the downscaling factor out of bounds.
+ */
+ src_x = src->x1 >> 16;
+ src_w = drm_rect_width(src) >> 16;
+ src_y = src->y1 >> 16;
+ src_h = drm_rect_height(src) >> 16;
+
+ drm_rect_init(src, src_x << 16, src_y << 16,
+ src_w << 16, src_h << 16);
+
+ if (fb->format->format == DRM_FORMAT_RGB565 && rotated) {
+ hsub = 2;
+ vsub = 2;
+ } else {
+ hsub = fb->format->hsub;
+ vsub = fb->format->vsub;
+ }
+
+ if (rotated)
+ hsub = vsub = max(hsub, vsub);
+
+ if (src_x % hsub || src_w % hsub) {
+ drm_dbg_kms(&i915->drm, "src x/w (%u, %u) must be a multiple of %u (rotated: %s)\n",
+ src_x, src_w, hsub, str_yes_no(rotated));
+ return -EINVAL;
+ }
+
+ if (src_y % vsub || src_h % vsub) {
+ drm_dbg_kms(&i915->drm, "src y/h (%u, %u) must be a multiple of %u (rotated: %s)\n",
+ src_y, src_h, vsub, str_yes_no(rotated));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* intel_prepare_plane_fb - Prepare fb for usage on plane
* @_plane: drm plane to prepare for
@@ -970,7 +1028,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane,
int ret;
if (old_obj) {
- const struct intel_crtc_state *crtc_state =
+ const struct intel_crtc_state *new_crtc_state =
intel_atomic_get_new_crtc_state(state,
to_intel_crtc(old_plane_state->hw.crtc));
@@ -985,7 +1043,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane,
* This should only fail upon a hung GPU, in which case we
* can safely continue.
*/
- if (intel_crtc_needs_modeset(crtc_state)) {
+ if (new_crtc_state && intel_crtc_needs_modeset(new_crtc_state)) {
ret = i915_sw_fence_await_reservation(&state->commit_ready,
old_obj->base.resv,
false, 0,
diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.h b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
index 74b6d3b169a7..191dad0efc8e 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic_plane.h
+++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
@@ -62,6 +62,7 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state,
struct intel_crtc_state *crtc_state,
int min_scale, int max_scale,
bool can_position);
+int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state);
void intel_plane_set_invisible(struct intel_crtc_state *crtc_state,
struct intel_plane_state *plane_state);
void intel_plane_helper_add(struct intel_plane *plane);
diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c
index 65151f5dcb15..3d5a9bbc6fde 100644
--- a/drivers/gpu/drm/i915/display/intel_audio.c
+++ b/drivers/gpu/drm/i915/display/intel_audio.c
@@ -983,11 +983,7 @@ void intel_audio_cdclk_change_pre(struct drm_i915_private *i915)
static void get_aud_ts_cdclk_m_n(int refclk, int cdclk, struct aud_ts_cdclk_m_n *aud_ts)
{
- if (refclk == 24000)
- aud_ts->m = 12;
- else
- aud_ts->m = 15;
-
+ aud_ts->m = 60;
aud_ts->n = cdclk * aud_ts->m / 24000;
}
diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index e54febd34ca9..75e69dffc5e9 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -141,8 +141,8 @@ struct bdb_block_entry {
};
static const void *
-find_section(struct drm_i915_private *i915,
- enum bdb_block_id section_id)
+bdb_find_section(struct drm_i915_private *i915,
+ enum bdb_block_id section_id)
{
struct bdb_block_entry *entry;
@@ -201,7 +201,7 @@ static size_t lfp_data_min_size(struct drm_i915_private *i915)
const struct bdb_lvds_lfp_data_ptrs *ptrs;
size_t size;
- ptrs = find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
+ ptrs = bdb_find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
if (!ptrs)
return 0;
@@ -630,7 +630,7 @@ static int vbt_get_panel_type(struct drm_i915_private *i915,
{
const struct bdb_lvds_options *lvds_options;
- lvds_options = find_section(i915, BDB_LVDS_OPTIONS);
+ lvds_options = bdb_find_section(i915, BDB_LVDS_OPTIONS);
if (!lvds_options)
return -1;
@@ -671,11 +671,11 @@ static int pnpid_get_panel_type(struct drm_i915_private *i915,
dump_pnp_id(i915, edid_id, "EDID");
- ptrs = find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
+ ptrs = bdb_find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
if (!ptrs)
return -1;
- data = find_section(i915, BDB_LVDS_LFP_DATA);
+ data = bdb_find_section(i915, BDB_LVDS_LFP_DATA);
if (!data)
return -1;
@@ -791,7 +791,7 @@ parse_panel_options(struct drm_i915_private *i915,
int panel_type = panel->vbt.panel_type;
int drrs_mode;
- lvds_options = find_section(i915, BDB_LVDS_OPTIONS);
+ lvds_options = bdb_find_section(i915, BDB_LVDS_OPTIONS);
if (!lvds_options)
return;
@@ -881,11 +881,11 @@ parse_lfp_data(struct drm_i915_private *i915,
const struct lvds_pnp_id *pnp_id;
int panel_type = panel->vbt.panel_type;
- ptrs = find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
+ ptrs = bdb_find_section(i915, BDB_LVDS_LFP_DATA_PTRS);
if (!ptrs)
return;
- data = find_section(i915, BDB_LVDS_LFP_DATA);
+ data = bdb_find_section(i915, BDB_LVDS_LFP_DATA);
if (!data)
return;
@@ -932,7 +932,7 @@ parse_generic_dtd(struct drm_i915_private *i915,
if (i915->display.vbt.version < 229)
return;
- generic_dtd = find_section(i915, BDB_GENERIC_DTD);
+ generic_dtd = bdb_find_section(i915, BDB_GENERIC_DTD);
if (!generic_dtd)
return;
@@ -1011,7 +1011,7 @@ parse_lfp_backlight(struct drm_i915_private *i915,
int panel_type = panel->vbt.panel_type;
u16 level;
- backlight_data = find_section(i915, BDB_LVDS_BACKLIGHT);
+ backlight_data = bdb_find_section(i915, BDB_LVDS_BACKLIGHT);
if (!backlight_data)
return;
@@ -1119,14 +1119,14 @@ parse_sdvo_panel_data(struct drm_i915_private *i915,
if (index == -1) {
const struct bdb_sdvo_lvds_options *sdvo_lvds_options;
- sdvo_lvds_options = find_section(i915, BDB_SDVO_LVDS_OPTIONS);
+ sdvo_lvds_options = bdb_find_section(i915, BDB_SDVO_LVDS_OPTIONS);
if (!sdvo_lvds_options)
return;
index = sdvo_lvds_options->panel_type;
}
- dtds = find_section(i915, BDB_SDVO_PANEL_DTDS);
+ dtds = bdb_find_section(i915, BDB_SDVO_PANEL_DTDS);
if (!dtds)
return;
@@ -1162,7 +1162,7 @@ parse_general_features(struct drm_i915_private *i915)
{
const struct bdb_general_features *general;
- general = find_section(i915, BDB_GENERAL_FEATURES);
+ general = bdb_find_section(i915, BDB_GENERAL_FEATURES);
if (!general)
return;
@@ -1285,7 +1285,7 @@ parse_driver_features(struct drm_i915_private *i915)
{
const struct bdb_driver_features *driver;
- driver = find_section(i915, BDB_DRIVER_FEATURES);
+ driver = bdb_find_section(i915, BDB_DRIVER_FEATURES);
if (!driver)
return;
@@ -1322,7 +1322,7 @@ parse_panel_driver_features(struct drm_i915_private *i915,
{
const struct bdb_driver_features *driver;
- driver = find_section(i915, BDB_DRIVER_FEATURES);
+ driver = bdb_find_section(i915, BDB_DRIVER_FEATURES);
if (!driver)
return;
@@ -1362,7 +1362,7 @@ parse_power_conservation_features(struct drm_i915_private *i915,
if (i915->display.vbt.version < 228)
return;
- power = find_section(i915, BDB_LFP_POWER);
+ power = bdb_find_section(i915, BDB_LFP_POWER);
if (!power)
return;
@@ -1402,7 +1402,7 @@ parse_edp(struct drm_i915_private *i915,
const struct edp_fast_link_params *edp_link_params;
int panel_type = panel->vbt.panel_type;
- edp = find_section(i915, BDB_EDP);
+ edp = bdb_find_section(i915, BDB_EDP);
if (!edp)
return;
@@ -1532,7 +1532,7 @@ parse_psr(struct drm_i915_private *i915,
const struct psr_table *psr_table;
int panel_type = panel->vbt.panel_type;
- psr = find_section(i915, BDB_PSR);
+ psr = bdb_find_section(i915, BDB_PSR);
if (!psr) {
drm_dbg_kms(&i915->drm, "No PSR BDB found.\n");
return;
@@ -1693,7 +1693,7 @@ parse_mipi_config(struct drm_i915_private *i915,
/* Parse #52 for panel index used from panel_type already
* parsed
*/
- start = find_section(i915, BDB_MIPI_CONFIG);
+ start = bdb_find_section(i915, BDB_MIPI_CONFIG);
if (!start) {
drm_dbg_kms(&i915->drm, "No MIPI config BDB found");
return;
@@ -2005,7 +2005,7 @@ parse_mipi_sequence(struct drm_i915_private *i915,
if (panel->vbt.dsi.panel_id != MIPI_DSI_GENERIC_PANEL_ID)
return;
- sequence = find_section(i915, BDB_MIPI_SEQUENCE);
+ sequence = bdb_find_section(i915, BDB_MIPI_SEQUENCE);
if (!sequence) {
drm_dbg_kms(&i915->drm,
"No MIPI Sequence found, parsing complete\n");
@@ -2086,7 +2086,7 @@ parse_compression_parameters(struct drm_i915_private *i915)
if (i915->display.vbt.version < 198)
return;
- params = find_section(i915, BDB_COMPRESSION_PARAMETERS);
+ params = bdb_find_section(i915, BDB_COMPRESSION_PARAMETERS);
if (params) {
/* Sanity checks */
if (params->entry_size != sizeof(params->data[0])) {
@@ -2792,7 +2792,7 @@ parse_general_definitions(struct drm_i915_private *i915)
u16 block_size;
int bus_pin;
- defs = find_section(i915, BDB_GENERAL_DEFINITIONS);
+ defs = bdb_find_section(i915, BDB_GENERAL_DEFINITIONS);
if (!defs) {
drm_dbg_kms(&i915->drm,
"No general definition block is found, no devices defined.\n");
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index 084a483f9776..2aaaba090cc0 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -1453,6 +1453,18 @@ static u8 tgl_calc_voltage_level(int cdclk)
return 0;
}
+static u8 rplu_calc_voltage_level(int cdclk)
+{
+ if (cdclk > 556800)
+ return 3;
+ else if (cdclk > 480000)
+ return 2;
+ else if (cdclk > 312000)
+ return 1;
+ else
+ return 0;
+}
+
static void icl_readout_refclk(struct drm_i915_private *dev_priv,
struct intel_cdclk_config *cdclk_config)
{
@@ -3242,6 +3254,13 @@ static const struct intel_cdclk_funcs mtl_cdclk_funcs = {
.calc_voltage_level = tgl_calc_voltage_level,
};
+static const struct intel_cdclk_funcs rplu_cdclk_funcs = {
+ .get_cdclk = bxt_get_cdclk,
+ .set_cdclk = bxt_set_cdclk,
+ .modeset_calc_cdclk = bxt_modeset_calc_cdclk,
+ .calc_voltage_level = rplu_calc_voltage_level,
+};
+
static const struct intel_cdclk_funcs tgl_cdclk_funcs = {
.get_cdclk = bxt_get_cdclk,
.set_cdclk = bxt_set_cdclk,
@@ -3384,14 +3403,17 @@ void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv)
dev_priv->display.funcs.cdclk = &tgl_cdclk_funcs;
dev_priv->display.cdclk.table = dg2_cdclk_table;
} else if (IS_ALDERLAKE_P(dev_priv)) {
- dev_priv->display.funcs.cdclk = &tgl_cdclk_funcs;
/* Wa_22011320316:adl-p[a0] */
- if (IS_ADLP_DISPLAY_STEP(dev_priv, STEP_A0, STEP_B0))
+ if (IS_ADLP_DISPLAY_STEP(dev_priv, STEP_A0, STEP_B0)) {
dev_priv->display.cdclk.table = adlp_a_step_cdclk_table;
- else if (IS_ADLP_RPLU(dev_priv))
+ dev_priv->display.funcs.cdclk = &tgl_cdclk_funcs;
+ } else if (IS_ADLP_RPLU(dev_priv)) {
dev_priv->display.cdclk.table = rplu_cdclk_table;
- else
+ dev_priv->display.funcs.cdclk = &rplu_cdclk_funcs;
+ } else {
dev_priv->display.cdclk.table = adlp_cdclk_table;
+ dev_priv->display.funcs.cdclk = &tgl_cdclk_funcs;
+ }
} else if (IS_ROCKETLAKE(dev_priv)) {
dev_priv->display.funcs.cdclk = &tgl_cdclk_funcs;
dev_priv->display.cdclk.table = rkl_cdclk_table;
diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c
index a6dd08598233..36aac88143ac 100644
--- a/drivers/gpu/drm/i915/display/intel_color.c
+++ b/drivers/gpu/drm/i915/display/intel_color.c
@@ -47,6 +47,11 @@ struct intel_color_funcs {
*/
void (*color_commit_arm)(const struct intel_crtc_state *crtc_state);
/*
+ * Perform any extra tasks needed after all the
+ * double buffered registers have been latched.
+ */
+ void (*color_post_update)(const struct intel_crtc_state *crtc_state);
+ /*
* Load LUTs (and other single buffered color management
* registers). Will (hopefully) be called during the vblank
* following the latching of any double buffered registers
@@ -614,9 +619,33 @@ static void ilk_lut_12p4_pack(struct drm_color_lut *entry, u32 ldw, u32 udw)
static void icl_color_commit_noarm(const struct intel_crtc_state *crtc_state)
{
+ /*
+ * Despite Wa_1406463849, ICL no longer suffers from the SKL
+ * DC5/PSR CSC black screen issue (see skl_color_commit_noarm()).
+ * Possibly due to the extra sticky CSC arming
+ * (see icl_color_post_update()).
+ *
+ * On TGL+ all CSC arming issues have been properly fixed.
+ */
icl_load_csc_matrix(crtc_state);
}
+static void skl_color_commit_noarm(const struct intel_crtc_state *crtc_state)
+{
+ /*
+ * Possibly related to display WA #1184, SKL CSC loses the latched
+ * CSC coeff/offset register values if the CSC registers are disarmed
+ * between DC5 exit and PSR exit. This will cause the plane(s) to
+ * output all black (until CSC_MODE is rearmed and properly latched).
+ * Once PSR exit (and proper register latching) has occurred the
+ * danger is over. Thus when PSR is enabled the CSC coeff/offset
+ * register programming will be peformed from skl_color_commit_arm()
+ * which is called after PSR exit.
+ */
+ if (!crtc_state->has_psr)
+ ilk_load_csc_matrix(crtc_state);
+}
+
static void ilk_color_commit_noarm(const struct intel_crtc_state *crtc_state)
{
ilk_load_csc_matrix(crtc_state);
@@ -659,6 +688,9 @@ static void skl_color_commit_arm(const struct intel_crtc_state *crtc_state)
enum pipe pipe = crtc->pipe;
u32 val = 0;
+ if (crtc_state->has_psr)
+ ilk_load_csc_matrix(crtc_state);
+
/*
* We don't (yet) allow userspace to control the pipe background color,
* so force it to black, but apply pipe gamma and CSC appropriately
@@ -677,6 +709,47 @@ static void skl_color_commit_arm(const struct intel_crtc_state *crtc_state)
crtc_state->csc_mode);
}
+static void icl_color_commit_arm(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ enum pipe pipe = crtc->pipe;
+
+ /*
+ * We don't (yet) allow userspace to control the pipe background color,
+ * so force it to black.
+ */
+ intel_de_write(i915, SKL_BOTTOM_COLOR(pipe), 0);
+
+ intel_de_write(i915, GAMMA_MODE(crtc->pipe),
+ crtc_state->gamma_mode);
+
+ intel_de_write_fw(i915, PIPE_CSC_MODE(crtc->pipe),
+ crtc_state->csc_mode);
+}
+
+static void icl_color_post_update(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+
+ /*
+ * Despite Wa_1406463849, ICL CSC is no longer disarmed by
+ * coeff/offset register *writes*. Instead, once CSC_MODE
+ * is armed it stays armed, even after it has been latched.
+ * Afterwards the coeff/offset registers become effectively
+ * self-arming. That self-arming must be disabled before the
+ * next icl_color_commit_noarm() tries to write the next set
+ * of coeff/offset registers. Fortunately register *reads*
+ * do still disarm the CSC. Naturally this must not be done
+ * until the previously written CSC registers have actually
+ * been latched.
+ *
+ * TGL+ no longer need this workaround.
+ */
+ intel_de_read_fw(i915, PIPE_CSC_PREOFF_HI(crtc->pipe));
+}
+
static struct drm_property_blob *
create_linear_lut(struct drm_i915_private *i915, int lut_size)
{
@@ -1376,6 +1449,14 @@ void intel_color_commit_arm(const struct intel_crtc_state *crtc_state)
i915->display.funcs.color->color_commit_arm(crtc_state);
}
+void intel_color_post_update(const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+
+ if (i915->display.funcs.color->color_post_update)
+ i915->display.funcs.color->color_post_update(crtc_state);
+}
+
void intel_color_prepare_commit(struct intel_crtc_state *crtc_state)
{
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
@@ -3072,10 +3153,20 @@ static const struct intel_color_funcs i9xx_color_funcs = {
.lut_equal = i9xx_lut_equal,
};
+static const struct intel_color_funcs tgl_color_funcs = {
+ .color_check = icl_color_check,
+ .color_commit_noarm = icl_color_commit_noarm,
+ .color_commit_arm = icl_color_commit_arm,
+ .load_luts = icl_load_luts,
+ .read_luts = icl_read_luts,
+ .lut_equal = icl_lut_equal,
+};
+
static const struct intel_color_funcs icl_color_funcs = {
.color_check = icl_color_check,
.color_commit_noarm = icl_color_commit_noarm,
- .color_commit_arm = skl_color_commit_arm,
+ .color_commit_arm = icl_color_commit_arm,
+ .color_post_update = icl_color_post_update,
.load_luts = icl_load_luts,
.read_luts = icl_read_luts,
.lut_equal = icl_lut_equal,
@@ -3083,7 +3174,7 @@ static const struct intel_color_funcs icl_color_funcs = {
static const struct intel_color_funcs glk_color_funcs = {
.color_check = glk_color_check,
- .color_commit_noarm = ilk_color_commit_noarm,
+ .color_commit_noarm = skl_color_commit_noarm,
.color_commit_arm = skl_color_commit_arm,
.load_luts = glk_load_luts,
.read_luts = glk_read_luts,
@@ -3092,7 +3183,7 @@ static const struct intel_color_funcs glk_color_funcs = {
static const struct intel_color_funcs skl_color_funcs = {
.color_check = ivb_color_check,
- .color_commit_noarm = ilk_color_commit_noarm,
+ .color_commit_noarm = skl_color_commit_noarm,
.color_commit_arm = skl_color_commit_arm,
.load_luts = bdw_load_luts,
.read_luts = bdw_read_luts,
@@ -3188,7 +3279,9 @@ void intel_color_init_hooks(struct drm_i915_private *i915)
else
i915->display.funcs.color = &i9xx_color_funcs;
} else {
- if (DISPLAY_VER(i915) >= 11)
+ if (DISPLAY_VER(i915) >= 12)
+ i915->display.funcs.color = &tgl_color_funcs;
+ else if (DISPLAY_VER(i915) == 11)
i915->display.funcs.color = &icl_color_funcs;
else if (DISPLAY_VER(i915) == 10)
i915->display.funcs.color = &glk_color_funcs;
diff --git a/drivers/gpu/drm/i915/display/intel_color.h b/drivers/gpu/drm/i915/display/intel_color.h
index d620b5b1e2a6..8002492be709 100644
--- a/drivers/gpu/drm/i915/display/intel_color.h
+++ b/drivers/gpu/drm/i915/display/intel_color.h
@@ -21,6 +21,7 @@ void intel_color_prepare_commit(struct intel_crtc_state *crtc_state);
void intel_color_cleanup_commit(struct intel_crtc_state *crtc_state);
void intel_color_commit_noarm(const struct intel_crtc_state *crtc_state);
void intel_color_commit_arm(const struct intel_crtc_state *crtc_state);
+void intel_color_post_update(const struct intel_crtc_state *crtc_state);
void intel_color_load_luts(const struct intel_crtc_state *crtc_state);
void intel_color_get_config(struct intel_crtc_state *crtc_state);
bool intel_color_lut_equal(const struct intel_crtc_state *crtc_state,
diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c
index 8f2ebead0826..38e9c61c2344 100644
--- a/drivers/gpu/drm/i915/display/intel_crt.c
+++ b/drivers/gpu/drm/i915/display/intel_crt.c
@@ -44,6 +44,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_fdi.h"
+#include "intel_fdi_regs.h"
#include "intel_fifo_underrun.h"
#include "intel_gmbus.h"
#include "intel_hotplug.h"
diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c
index b79a8834559f..ed45a6934854 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc.c
@@ -212,7 +212,7 @@ static void intel_crtc_destroy(struct drm_crtc *_crtc)
static int intel_crtc_late_register(struct drm_crtc *crtc)
{
- intel_crtc_debugfs_add(crtc);
+ intel_crtc_debugfs_add(to_intel_crtc(crtc));
return 0;
}
@@ -686,6 +686,14 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state)
*/
intel_vrr_send_push(new_crtc_state);
+ /*
+ * Seamless M/N update may need to update frame timings.
+ *
+ * FIXME Should be synchronized with the start of vblank somehow...
+ */
+ if (new_crtc_state->seamless_m_n && intel_crtc_needs_fastset(new_crtc_state))
+ intel_crtc_update_active_timings(new_crtc_state);
+
local_irq_enable();
if (intel_vgpu_active(dev_priv))
diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c
index 766633566fd6..54c8adc0702e 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c
@@ -123,7 +123,7 @@ static const char * const output_format_str[] = {
[INTEL_OUTPUT_FORMAT_YCBCR444] = "YCBCR4:4:4",
};
-static const char *output_formats(enum intel_output_format format)
+const char *intel_output_format_name(enum intel_output_format format)
{
if (format >= ARRAY_SIZE(output_format_str))
return "invalid";
@@ -181,7 +181,7 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config,
"active: %s, output_types: %s (0x%x), output format: %s\n",
str_yes_no(pipe_config->hw.active),
buf, pipe_config->output_types,
- output_formats(pipe_config->output_format));
+ intel_output_format_name(pipe_config->output_format));
drm_dbg_kms(&i915->drm,
"cpu_transcoder: %s, pipe bpp: %i, dithering: %i\n",
diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.h b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.h
index 9399c35b7e5e..780f3f1190d7 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.h
+++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.h
@@ -8,9 +8,11 @@
struct intel_crtc_state;
struct intel_atomic_state;
+enum intel_output_format;
void intel_crtc_state_dump(const struct intel_crtc_state *crtc_state,
struct intel_atomic_state *state,
const char *context);
+const char *intel_output_format_name(enum intel_output_format format);
#endif /* __INTEL_CRTC_STATE_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c
index c3173c0c2068..31bef0427377 100644
--- a/drivers/gpu/drm/i915/display/intel_cursor.c
+++ b/drivers/gpu/drm/i915/display/intel_cursor.c
@@ -21,7 +21,6 @@
#include "intel_fb_pin.h"
#include "intel_frontbuffer.h"
#include "intel_psr.h"
-#include "intel_sprite.h"
#include "skl_watermark.h"
/* Cursor formats */
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index 0950bcfea4c0..3a7b98837516 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -65,7 +65,6 @@
#include "intel_psr.h"
#include "intel_quirks.h"
#include "intel_snps_phy.h"
-#include "intel_sprite.h"
#include "intel_tc.h"
#include "intel_vdsc.h"
#include "intel_vdsc_regs.h"
@@ -2520,6 +2519,10 @@ static void intel_ddi_pre_enable_dp(struct intel_atomic_state *state,
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+ if (HAS_DP20(dev_priv))
+ intel_dp_128b132b_sdp_crc16(enc_to_intel_dp(encoder),
+ crtc_state);
+
if (DISPLAY_VER(dev_priv) >= 12)
tgl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state);
else
@@ -2618,8 +2621,7 @@ static void intel_disable_ddi_buf(struct intel_encoder *encoder,
if (intel_crtc_has_dp_encoder(crtc_state))
intel_de_rmw(dev_priv, dp_tp_ctl_reg(encoder, crtc_state),
- DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK,
- DP_TP_CTL_LINK_TRAIN_PAT1);
+ DP_TP_CTL_ENABLE, 0);
/* Disable FEC in DP Sink */
intel_ddi_disable_fec_state(encoder, crtc_state);
@@ -2718,9 +2720,6 @@ static void intel_ddi_post_disable(struct intel_atomic_state *state,
const struct drm_connector_state *old_conn_state)
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
- enum phy phy = intel_port_to_phy(dev_priv, encoder->port);
- bool is_tc_port = intel_phy_is_tc(dev_priv, phy);
struct intel_crtc *slave_crtc;
if (!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST)) {
@@ -2770,6 +2769,17 @@ static void intel_ddi_post_disable(struct intel_atomic_state *state,
else
intel_ddi_post_disable_dp(state, encoder, old_crtc_state,
old_conn_state);
+}
+
+static void intel_ddi_post_pll_disable(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
+ const struct intel_crtc_state *old_crtc_state,
+ const struct drm_connector_state *old_conn_state)
+{
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+ enum phy phy = intel_port_to_phy(i915, encoder->port);
+ bool is_tc_port = intel_phy_is_tc(i915, phy);
main_link_aux_power_domain_put(dig_port, old_crtc_state);
@@ -3050,37 +3060,23 @@ void intel_ddi_update_pipe(struct intel_atomic_state *state,
intel_hdcp_update_pipe(state, encoder, crtc_state, conn_state);
}
-static void
-intel_ddi_update_prepare(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- struct intel_crtc *crtc)
+void intel_ddi_update_active_dpll(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
+ struct intel_crtc *crtc)
{
- struct drm_i915_private *i915 = to_i915(state->base.dev);
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
struct intel_crtc_state *crtc_state =
- crtc ? intel_atomic_get_new_crtc_state(state, crtc) : NULL;
- int required_lanes = crtc_state ? crtc_state->lane_count : 1;
-
- drm_WARN_ON(state->base.dev, crtc && crtc->active);
-
- intel_tc_port_get_link(enc_to_dig_port(encoder),
- required_lanes);
- if (crtc_state && crtc_state->hw.active) {
- struct intel_crtc *slave_crtc;
-
- intel_update_active_dpll(state, crtc, encoder);
+ intel_atomic_get_new_crtc_state(state, crtc);
+ struct intel_crtc *slave_crtc;
+ enum phy phy = intel_port_to_phy(i915, encoder->port);
- for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc,
- intel_crtc_bigjoiner_slave_pipes(crtc_state))
- intel_update_active_dpll(state, slave_crtc, encoder);
- }
-}
+ if (!intel_phy_is_tc(i915, phy))
+ return;
-static void
-intel_ddi_update_complete(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- struct intel_crtc *crtc)
-{
- intel_tc_port_put_link(enc_to_dig_port(encoder));
+ intel_update_active_dpll(state, crtc, encoder);
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc,
+ intel_crtc_bigjoiner_slave_pipes(crtc_state))
+ intel_update_active_dpll(state, slave_crtc, encoder);
}
static void
@@ -3094,8 +3090,13 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state,
enum phy phy = intel_port_to_phy(dev_priv, encoder->port);
bool is_tc_port = intel_phy_is_tc(dev_priv, phy);
- if (is_tc_port)
+ if (is_tc_port) {
+ struct intel_crtc *master_crtc =
+ to_intel_crtc(crtc_state->uapi.crtc);
+
intel_tc_port_get_link(dig_port, crtc_state->lane_count);
+ intel_ddi_update_active_dpll(state, encoder, master_crtc);
+ }
main_link_aux_power_domain_get(dig_port, crtc_state);
@@ -3140,8 +3141,7 @@ static void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp,
wait = true;
}
- dp_tp_ctl &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK);
- dp_tp_ctl |= DP_TP_CTL_LINK_TRAIN_PAT1;
+ dp_tp_ctl &= ~DP_TP_CTL_ENABLE;
intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), dp_tp_ctl);
intel_de_posting_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state));
@@ -3543,6 +3543,37 @@ static void icl_ddi_combo_get_config(struct intel_encoder *encoder,
intel_ddi_get_config(encoder, crtc_state);
}
+static bool icl_ddi_tc_pll_is_tbt(const struct intel_shared_dpll *pll)
+{
+ return pll->info->id == DPLL_ID_ICL_TBTPLL;
+}
+
+static enum icl_port_dpll_id
+icl_ddi_tc_port_pll_type(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ const struct intel_shared_dpll *pll = crtc_state->shared_dpll;
+
+ if (drm_WARN_ON(&i915->drm, !pll))
+ return ICL_PORT_DPLL_DEFAULT;
+
+ if (icl_ddi_tc_pll_is_tbt(pll))
+ return ICL_PORT_DPLL_DEFAULT;
+ else
+ return ICL_PORT_DPLL_MG_PHY;
+}
+
+enum icl_port_dpll_id
+intel_ddi_port_pll_type(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ if (!encoder->port_pll_type)
+ return ICL_PORT_DPLL_DEFAULT;
+
+ return encoder->port_pll_type(encoder, crtc_state);
+}
+
static void icl_ddi_tc_get_clock(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
struct intel_shared_dpll *pll)
@@ -3555,7 +3586,7 @@ static void icl_ddi_tc_get_clock(struct intel_encoder *encoder,
if (drm_WARN_ON(&i915->drm, !pll))
return;
- if (pll->info->id == DPLL_ID_ICL_TBTPLL)
+ if (icl_ddi_tc_pll_is_tbt(pll))
port_dpll_id = ICL_PORT_DPLL_DEFAULT;
else
port_dpll_id = ICL_PORT_DPLL_MG_PHY;
@@ -3568,7 +3599,7 @@ static void icl_ddi_tc_get_clock(struct intel_encoder *encoder,
icl_set_active_port_dpll(crtc_state, port_dpll_id);
- if (crtc_state->shared_dpll->info->id == DPLL_ID_ICL_TBTPLL)
+ if (icl_ddi_tc_pll_is_tbt(crtc_state->shared_dpll))
crtc_state->port_clock = icl_calc_tbt_pll_link(i915, encoder->port);
else
crtc_state->port_clock = intel_dpll_get_freq(i915, crtc_state->shared_dpll,
@@ -3610,7 +3641,8 @@ static void intel_ddi_sync_state(struct intel_encoder *encoder,
enum phy phy = intel_port_to_phy(i915, encoder->port);
if (intel_phy_is_tc(i915, phy))
- intel_tc_port_sanitize_mode(enc_to_dig_port(encoder));
+ intel_tc_port_sanitize_mode(enc_to_dig_port(encoder),
+ crtc_state);
if (crtc_state && intel_crtc_has_dp_encoder(crtc_state))
intel_dp_sync_state(encoder, crtc_state);
@@ -3810,7 +3842,7 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
intel_dp_encoder_flush_work(encoder);
if (intel_phy_is_tc(i915, phy))
- intel_tc_port_flush_work(dig_port);
+ intel_tc_port_cleanup(dig_port);
intel_display_power_flush_work(i915);
drm_encoder_cleanup(encoder);
@@ -3955,8 +3987,8 @@ static int intel_hdmi_reset_link(struct intel_encoder *encoder,
ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
if (ret < 0) {
- drm_err(&dev_priv->drm, "Failed to read TMDS config: %d\n",
- ret);
+ drm_err(&dev_priv->drm, "[CONNECTOR:%d:%s] Failed to read TMDS config: %d\n",
+ connector->base.base.id, connector->base.name, ret);
return 0;
}
@@ -4251,7 +4283,7 @@ static void intel_ddi_encoder_shutdown(struct intel_encoder *encoder)
if (!intel_phy_is_tc(i915, phy))
return;
- intel_tc_port_flush_work(dig_port);
+ intel_tc_port_cleanup(dig_port);
}
#define port_tc_name(port) ((port) - PORT_TC1 + '1')
@@ -4365,6 +4397,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
encoder->pre_pll_enable = intel_ddi_pre_pll_enable;
encoder->pre_enable = intel_ddi_pre_enable;
encoder->disable = intel_disable_ddi;
+ encoder->post_pll_disable = intel_ddi_post_pll_disable;
encoder->post_disable = intel_ddi_post_disable;
encoder->update_pipe = intel_ddi_update_pipe;
encoder->get_hw_state = intel_ddi_get_hw_state;
@@ -4404,6 +4437,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
encoder->enable_clock = jsl_ddi_tc_enable_clock;
encoder->disable_clock = jsl_ddi_tc_disable_clock;
encoder->is_clock_enabled = jsl_ddi_tc_is_clock_enabled;
+ encoder->port_pll_type = icl_ddi_tc_port_pll_type;
encoder->get_config = icl_ddi_combo_get_config;
} else {
encoder->enable_clock = icl_ddi_combo_enable_clock;
@@ -4416,6 +4450,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
encoder->enable_clock = icl_ddi_tc_enable_clock;
encoder->disable_clock = icl_ddi_tc_disable_clock;
encoder->is_clock_enabled = icl_ddi_tc_is_clock_enabled;
+ encoder->port_pll_type = icl_ddi_tc_port_pll_type;
encoder->get_config = icl_ddi_tc_get_config;
} else {
encoder->enable_clock = icl_ddi_combo_enable_clock;
@@ -4496,10 +4531,18 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
!intel_bios_encoder_supports_typec_usb(devdata) &&
!intel_bios_encoder_supports_tbt(devdata);
- intel_tc_port_init(dig_port, is_legacy);
+ if (!is_legacy && init_hdmi) {
+ is_legacy = !init_dp;
+
+ drm_dbg_kms(&dev_priv->drm,
+ "VBT says port %c is non-legacy TC and has HDMI (with DP: %s), assume it's %s\n",
+ port_name(port),
+ str_yes_no(init_dp),
+ is_legacy ? "legacy" : "non-legacy");
+ }
- encoder->update_prepare = intel_ddi_update_prepare;
- encoder->update_complete = intel_ddi_update_complete;
+ if (intel_tc_port_init(dig_port, is_legacy) < 0)
+ goto err;
}
drm_WARN_ON(&dev_priv->drm, port > PORT_I);
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h
index 361f6874dde5..2bc034042a93 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.h
+++ b/drivers/gpu/drm/i915/display/intel_ddi.h
@@ -40,6 +40,9 @@ void hsw_ddi_enable_clock(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
void hsw_ddi_disable_clock(struct intel_encoder *encoder);
bool hsw_ddi_is_clock_enabled(struct intel_encoder *encoder);
+enum icl_port_dpll_id
+intel_ddi_port_pll_type(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state);
void hsw_ddi_get_config(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state);
struct intel_shared_dpll *icl_ddi_combo_get_pll(struct intel_encoder *encoder);
@@ -69,5 +72,8 @@ void intel_ddi_sanitize_encoder_pll_mapping(struct intel_encoder *encoder);
int intel_ddi_level(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
int lane);
+void intel_ddi_update_active_dpll(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
+ struct intel_crtc *crtc);
#endif /* __INTEL_DDI_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index edbcb1273ca2..0aae9a1eb3d5 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -63,6 +63,7 @@
#include "intel_audio.h"
#include "intel_bw.h"
#include "intel_cdclk.h"
+#include "intel_clock_gating.h"
#include "intel_color.h"
#include "intel_crt.h"
#include "intel_crtc.h"
@@ -105,13 +106,11 @@
#include "intel_pcode.h"
#include "intel_pipe_crc.h"
#include "intel_plane_initial.h"
-#include "intel_pm.h"
#include "intel_pps.h"
#include "intel_psr.h"
#include "intel_quirks.h"
#include "intel_sdvo.h"
#include "intel_snps_phy.h"
-#include "intel_sprite.h"
#include "intel_tc.h"
#include "intel_tv.h"
#include "intel_vblank.h"
@@ -131,7 +130,7 @@
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_pipemisc(const struct intel_crtc_state *crtc_state);
+static void bdw_set_pipe_misc(const struct intel_crtc_state *crtc_state);
static void ilk_pfit_enable(const struct intel_crtc_state *crtc_state);
/* returns HPLL frequency in kHz */
@@ -851,7 +850,7 @@ void intel_display_finish_reset(struct drm_i915_private *i915)
*/
intel_pps_unlock_regs_wa(i915);
intel_modeset_init_hw(i915);
- intel_init_clock_gating(i915);
+ intel_clock_gating_init(i915);
intel_hpd_init(i915);
ret = __intel_display_resume(i915, state, ctx);
@@ -960,7 +959,7 @@ intel_get_crtc_new_encoder(const struct intel_atomic_state *state,
num_encoders++;
}
- drm_WARN(encoder->base.dev, num_encoders != 1,
+ drm_WARN(state->base.dev, num_encoders != 1,
"%d encoders for pipe %c\n",
num_encoders, pipe_name(master_crtc->pipe));
@@ -1116,6 +1115,9 @@ static void intel_post_plane_update(struct intel_atomic_state *state,
if (needs_cursorclk_wa(old_crtc_state) &&
!needs_cursorclk_wa(new_crtc_state))
icl_wa_cursorclkgating(dev_priv, pipe, false);
+
+ if (intel_crtc_needs_color_update(new_crtc_state))
+ intel_color_post_update(new_crtc_state);
}
static void intel_crtc_enable_flip_done(struct intel_atomic_state *state,
@@ -1318,36 +1320,11 @@ static void intel_crtc_disable_planes(struct intel_atomic_state *state,
intel_frontbuffer_flip(dev_priv, fb_bits);
}
-/*
- * intel_connector_primary_encoder - get the primary encoder for a connector
- * @connector: connector for which to return the encoder
- *
- * Returns the primary encoder for a connector. There is a 1:1 mapping from
- * all connectors to their encoder, except for DP-MST connectors which have
- * both a virtual and a primary encoder. These DP-MST primary encoders can be
- * pointed to by as many DP-MST connectors as there are pipes.
- */
-static struct intel_encoder *
-intel_connector_primary_encoder(struct intel_connector *connector)
-{
- struct intel_encoder *encoder;
-
- if (connector->mst_port)
- return &dp_to_dig_port(connector->mst_port)->base;
-
- encoder = intel_attached_encoder(connector);
- drm_WARN_ON(connector->base.dev, !encoder);
-
- return encoder;
-}
-
static void intel_encoders_update_prepare(struct intel_atomic_state *state)
{
struct drm_i915_private *i915 = to_i915(state->base.dev);
struct intel_crtc_state *new_crtc_state, *old_crtc_state;
struct intel_crtc *crtc;
- struct drm_connector_state *new_conn_state;
- struct drm_connector *connector;
int i;
/*
@@ -1363,57 +1340,6 @@ static void intel_encoders_update_prepare(struct intel_atomic_state *state)
new_crtc_state->dpll_hw_state = old_crtc_state->dpll_hw_state;
}
}
-
- if (!state->modeset)
- return;
-
- for_each_new_connector_in_state(&state->base, connector, new_conn_state,
- i) {
- struct intel_connector *intel_connector;
- struct intel_encoder *encoder;
- struct intel_crtc *crtc;
-
- if (!intel_connector_needs_modeset(state, connector))
- continue;
-
- intel_connector = to_intel_connector(connector);
- encoder = intel_connector_primary_encoder(intel_connector);
- if (!encoder->update_prepare)
- continue;
-
- crtc = new_conn_state->crtc ?
- to_intel_crtc(new_conn_state->crtc) : NULL;
- encoder->update_prepare(state, encoder, crtc);
- }
-}
-
-static void intel_encoders_update_complete(struct intel_atomic_state *state)
-{
- struct drm_connector_state *new_conn_state;
- struct drm_connector *connector;
- int i;
-
- if (!state->modeset)
- return;
-
- for_each_new_connector_in_state(&state->base, connector, new_conn_state,
- i) {
- struct intel_connector *intel_connector;
- struct intel_encoder *encoder;
- struct intel_crtc *crtc;
-
- if (!intel_connector_needs_modeset(state, connector))
- continue;
-
- intel_connector = to_intel_connector(connector);
- encoder = intel_connector_primary_encoder(intel_connector);
- if (!encoder->update_complete)
- continue;
-
- crtc = new_conn_state->crtc ?
- to_intel_crtc(new_conn_state->crtc) : NULL;
- encoder->update_complete(state, encoder, crtc);
- }
}
static void intel_encoders_pre_pll_enable(struct intel_atomic_state *state,
@@ -1793,7 +1719,7 @@ static void hsw_crtc_enable(struct intel_atomic_state *state,
intel_set_pipe_src_size(new_crtc_state);
if (DISPLAY_VER(dev_priv) >= 9 || IS_BROADWELL(dev_priv))
- bdw_set_pipemisc(new_crtc_state);
+ bdw_set_pipe_misc(new_crtc_state);
if (!intel_crtc_is_bigjoiner_slave(new_crtc_state) &&
!transcoder_is_dsi(cpu_transcoder))
@@ -1903,6 +1829,8 @@ static void ilk_crtc_disable(struct intel_atomic_state *state,
intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true);
+
+ intel_disable_shared_dpll(old_crtc_state);
}
static void hsw_crtc_disable(struct intel_atomic_state *state,
@@ -1921,7 +1849,19 @@ static void hsw_crtc_disable(struct intel_atomic_state *state,
intel_encoders_post_disable(state, crtc);
}
- intel_dmc_disable_pipe(i915, crtc->pipe);
+ intel_disable_shared_dpll(old_crtc_state);
+
+ if (!intel_crtc_is_bigjoiner_slave(old_crtc_state)) {
+ struct intel_crtc *slave_crtc;
+
+ intel_encoders_post_pll_disable(state, crtc);
+
+ intel_dmc_disable_pipe(i915, crtc->pipe);
+
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc,
+ intel_crtc_bigjoiner_slave_pipes(old_crtc_state))
+ intel_dmc_disable_pipe(i915, slave_crtc->pipe);
+ }
}
static void i9xx_pfit_enable(const struct intel_crtc_state *crtc_state)
@@ -2139,6 +2079,8 @@ static void valleyview_crtc_enable(struct intel_atomic_state *state,
intel_set_pipe_src_size(new_crtc_state);
+ intel_de_write(dev_priv, VLV_PIPE_MSA_MISC(pipe), 0);
+
if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B) {
intel_de_write(dev_priv, CHV_BLEND(pipe), CHV_BLEND_LEGACY);
intel_de_write(dev_priv, CHV_CANVAS(pipe), 0);
@@ -3074,20 +3016,20 @@ static void chv_crtc_clock_get(struct intel_crtc *crtc,
}
static enum intel_output_format
-bdw_get_pipemisc_output_format(struct intel_crtc *crtc)
+bdw_get_pipe_misc_output_format(struct intel_crtc *crtc)
{
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
u32 tmp;
- tmp = intel_de_read(dev_priv, PIPEMISC(crtc->pipe));
+ tmp = intel_de_read(dev_priv, PIPE_MISC(crtc->pipe));
- if (tmp & PIPEMISC_YUV420_ENABLE) {
+ if (tmp & PIPE_MISC_YUV420_ENABLE) {
/* We support 4:2:0 in full blend mode only */
drm_WARN_ON(&dev_priv->drm,
- (tmp & PIPEMISC_YUV420_MODE_FULL_BLEND) == 0);
+ (tmp & PIPE_MISC_YUV420_MODE_FULL_BLEND) == 0);
return INTEL_OUTPUT_FORMAT_YCBCR420;
- } else if (tmp & PIPEMISC_OUTPUT_COLORSPACE_YUV) {
+ } else if (tmp & PIPE_MISC_OUTPUT_COLORSPACE_YUV) {
return INTEL_OUTPUT_FORMAT_YCBCR444;
} else {
return INTEL_OUTPUT_FORMAT_RGB;
@@ -3330,7 +3272,7 @@ static void hsw_set_transconf(const struct intel_crtc_state *crtc_state)
intel_de_posting_read(dev_priv, TRANSCONF(cpu_transcoder));
}
-static void bdw_set_pipemisc(const struct intel_crtc_state *crtc_state)
+static void bdw_set_pipe_misc(const struct intel_crtc_state *crtc_state)
{
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
@@ -3338,18 +3280,18 @@ static void bdw_set_pipemisc(const struct intel_crtc_state *crtc_state)
switch (crtc_state->pipe_bpp) {
case 18:
- val |= PIPEMISC_BPC_6;
+ val |= PIPE_MISC_BPC_6;
break;
case 24:
- val |= PIPEMISC_BPC_8;
+ val |= PIPE_MISC_BPC_8;
break;
case 30:
- val |= PIPEMISC_BPC_10;
+ val |= PIPE_MISC_BPC_10;
break;
case 36:
/* Port output 12BPC defined for ADLP+ */
if (DISPLAY_VER(dev_priv) > 12)
- val |= PIPEMISC_BPC_12_ADLP;
+ val |= PIPE_MISC_BPC_12_ADLP;
break;
default:
MISSING_CASE(crtc_state->pipe_bpp);
@@ -3357,38 +3299,38 @@ static void bdw_set_pipemisc(const struct intel_crtc_state *crtc_state)
}
if (crtc_state->dither)
- val |= PIPEMISC_DITHER_ENABLE | PIPEMISC_DITHER_TYPE_SP;
+ val |= PIPE_MISC_DITHER_ENABLE | PIPE_MISC_DITHER_TYPE_SP;
if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420 ||
crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444)
- val |= PIPEMISC_OUTPUT_COLORSPACE_YUV;
+ val |= PIPE_MISC_OUTPUT_COLORSPACE_YUV;
if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
- val |= PIPEMISC_YUV420_ENABLE |
- PIPEMISC_YUV420_MODE_FULL_BLEND;
+ val |= PIPE_MISC_YUV420_ENABLE |
+ PIPE_MISC_YUV420_MODE_FULL_BLEND;
if (DISPLAY_VER(dev_priv) >= 11 && is_hdr_mode(crtc_state))
- val |= PIPEMISC_HDR_MODE_PRECISION;
+ val |= PIPE_MISC_HDR_MODE_PRECISION;
if (DISPLAY_VER(dev_priv) >= 12)
- val |= PIPEMISC_PIXEL_ROUNDING_TRUNC;
+ val |= PIPE_MISC_PIXEL_ROUNDING_TRUNC;
- intel_de_write(dev_priv, PIPEMISC(crtc->pipe), val);
+ intel_de_write(dev_priv, PIPE_MISC(crtc->pipe), val);
}
-int bdw_get_pipemisc_bpp(struct intel_crtc *crtc)
+int bdw_get_pipe_misc_bpp(struct intel_crtc *crtc)
{
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
u32 tmp;
- tmp = intel_de_read(dev_priv, PIPEMISC(crtc->pipe));
+ tmp = intel_de_read(dev_priv, PIPE_MISC(crtc->pipe));
- switch (tmp & PIPEMISC_BPC_MASK) {
- case PIPEMISC_BPC_6:
+ switch (tmp & PIPE_MISC_BPC_MASK) {
+ case PIPE_MISC_BPC_6:
return 18;
- case PIPEMISC_BPC_8:
+ case PIPE_MISC_BPC_8:
return 24;
- case PIPEMISC_BPC_10:
+ case PIPE_MISC_BPC_10:
return 30;
/*
* PORT OUTPUT 12 BPC defined for ADLP+.
@@ -3400,7 +3342,7 @@ int bdw_get_pipemisc_bpp(struct intel_crtc *crtc)
* on older platforms, need to find a workaround for 12 BPC
* MIPI DSI HW readout.
*/
- case PIPEMISC_BPC_12_ADLP:
+ case PIPE_MISC_BPC_12_ADLP:
if (DISPLAY_VER(dev_priv) > 12)
return 36;
fallthrough;
@@ -3981,7 +3923,7 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc,
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
} else {
pipe_config->output_format =
- bdw_get_pipemisc_output_format(crtc);
+ bdw_get_pipe_misc_output_format(crtc);
}
pipe_config->gamma_mode = intel_de_read(dev_priv,
@@ -5079,6 +5021,7 @@ intel_crtc_prepare_cleared_state(struct intel_atomic_state *state,
* only fields that are know to not cause problems are preserved. */
saved_state->uapi = crtc_state->uapi;
+ saved_state->inherited = crtc_state->inherited;
saved_state->scaler_state = crtc_state->scaler_state;
saved_state->shared_dpll = crtc_state->shared_dpll;
saved_state->dpll_hw_state = crtc_state->dpll_hw_state;
@@ -5904,68 +5847,6 @@ int intel_modeset_all_pipes(struct intel_atomic_state *state,
return 0;
}
-void intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state)
-{
- struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
- struct drm_display_mode adjusted_mode;
-
- drm_mode_init(&adjusted_mode, &crtc_state->hw.adjusted_mode);
-
- if (crtc_state->vrr.enable) {
- adjusted_mode.crtc_vtotal = crtc_state->vrr.vmax;
- adjusted_mode.crtc_vblank_end = crtc_state->vrr.vmax;
- adjusted_mode.crtc_vblank_start = intel_vrr_vmin_vblank_start(crtc_state);
- crtc->vmax_vblank_start = intel_vrr_vmax_vblank_start(crtc_state);
- }
-
- drm_calc_timestamping_constants(&crtc->base, &adjusted_mode);
-
- crtc->mode_flags = crtc_state->mode_flags;
-
- /*
- * The scanline counter increments at the leading edge of hsync.
- *
- * On most platforms it starts counting from vtotal-1 on the
- * first active line. That means the scanline counter value is
- * always one less than what we would expect. Ie. just after
- * start of vblank, which also occurs at start of hsync (on the
- * last active line), the scanline counter will read vblank_start-1.
- *
- * On gen2 the scanline counter starts counting from 1 instead
- * of vtotal-1, so we have to subtract one (or rather add vtotal-1
- * to keep the value positive), instead of adding one.
- *
- * On HSW+ the behaviour of the scanline counter depends on the output
- * type. For DP ports it behaves like most other platforms, but on HDMI
- * there's an extra 1 line difference. So we need to add two instead of
- * one to the value.
- *
- * On VLV/CHV DSI the scanline counter would appear to increment
- * approx. 1/3 of a scanline before start of vblank. Unfortunately
- * that means we can't tell whether we're in vblank or not while
- * we're on that particular line. We must still set scanline_offset
- * to 1 so that the vblank timestamps come out correct when we query
- * the scanline counter from within the vblank interrupt handler.
- * However if queried just before the start of vblank we'll get an
- * answer that's slightly in the future.
- */
- if (DISPLAY_VER(dev_priv) == 2) {
- int vtotal;
-
- vtotal = adjusted_mode.crtc_vtotal;
- if (adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
- vtotal /= 2;
-
- crtc->scanline_offset = vtotal - 1;
- } else if (HAS_DDI(dev_priv) &&
- intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) {
- crtc->scanline_offset = 2;
- } else {
- crtc->scanline_offset = 1;
- }
-}
-
/*
* This implements the workaround described in the "notes" section of the mode
* set sequence documentation. When going from no pipes or single pipe to
@@ -6970,7 +6851,7 @@ static void commit_pipe_pre_planes(struct intel_atomic_state *state,
intel_color_commit_arm(new_crtc_state);
if (DISPLAY_VER(dev_priv) >= 9 || IS_BROADWELL(dev_priv))
- bdw_set_pipemisc(new_crtc_state);
+ bdw_set_pipe_misc(new_crtc_state);
if (intel_crtc_needs_fastset(new_crtc_state))
intel_pipe_fastset(old_crtc_state, new_crtc_state);
@@ -7029,6 +6910,12 @@ static void intel_update_crtc(struct intel_atomic_state *state,
intel_atomic_get_new_crtc_state(state, crtc);
bool modeset = intel_crtc_needs_modeset(new_crtc_state);
+ if (old_crtc_state->inherited ||
+ intel_crtc_needs_modeset(new_crtc_state)) {
+ if (HAS_DPT(i915))
+ intel_dpt_configure(crtc);
+ }
+
if (!modeset) {
if (new_crtc_state->preload_luts &&
intel_crtc_needs_color_update(new_crtc_state))
@@ -7046,6 +6933,8 @@ static void intel_update_crtc(struct intel_atomic_state *state,
intel_fbc_update(state, crtc);
+ drm_WARN_ON(&i915->drm, !intel_display_power_is_enabled(i915, POWER_DOMAIN_DC_OFF));
+
if (!modeset &&
intel_crtc_needs_color_update(new_crtc_state))
intel_color_commit_noarm(new_crtc_state);
@@ -7090,7 +6979,6 @@ static void intel_old_crtc_state_disables(struct intel_atomic_state *state,
dev_priv->display.funcs.display->crtc_disable(state, crtc);
crtc->active = false;
intel_fbc_disable(crtc);
- intel_disable_shared_dpll(old_crtc_state);
if (!new_crtc_state->hw.active)
intel_initial_watermarks(state, crtc);
@@ -7413,8 +7301,28 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
drm_atomic_helper_wait_for_dependencies(&state->base);
drm_dp_mst_atomic_wait_for_dependencies(&state->base);
- if (state->modeset)
- wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_MODESET);
+ /*
+ * During full modesets we write a lot of registers, wait
+ * for PLLs, etc. Doing that while DC states are enabled
+ * is not a good idea.
+ *
+ * During fastsets and other updates we also need to
+ * disable DC states due to the following scenario:
+ * 1. DC5 exit and PSR exit happen
+ * 2. Some or all _noarm() registers are written
+ * 3. Due to some long delay PSR is re-entered
+ * 4. DC5 entry -> DMC saves the already written new
+ * _noarm() registers and the old not yet written
+ * _arm() registers
+ * 5. DC5 exit -> DMC restores a mixture of old and
+ * new register values and arms the update
+ * 6. PSR exit -> hardware latches a mixture of old and
+ * new register values -> corrupted frame, or worse
+ * 7. New _arm() registers are finally written
+ * 8. Hardware finally latches a complete set of new
+ * register values, and subsequent frames will be OK again
+ */
+ wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_DC_OFF);
intel_atomic_prepare_plane_clear_colors(state);
@@ -7469,8 +7377,6 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
/* Now enable the clocks, plane, pipe, and connectors that we set up. */
dev_priv->display.funcs.display->commit_modeset_enables(state);
- intel_encoders_update_complete(state);
-
if (state->modeset)
intel_set_cdclk_post_plane_update(state);
@@ -7563,8 +7469,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
* the culprit.
*/
intel_uncore_arm_unclaimed_mmio_detection(&dev_priv->uncore);
- intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET, wakeref);
}
+ intel_display_power_put(dev_priv, POWER_DOMAIN_DC_OFF, wakeref);
intel_runtime_pm_put(&dev_priv->runtime_pm, state->wakeref);
/*
@@ -8886,14 +8792,14 @@ void intel_display_driver_register(struct drm_i915_private *i915)
if (!HAS_DISPLAY(i915))
return;
- intel_display_debugfs_register(i915);
-
/* Must be done after probing outputs */
intel_opregion_register(i915);
intel_acpi_video_register(i915);
intel_audio_init(i915);
+ intel_display_debugfs_register(i915);
+
/*
* Some ports require correctly set-up hpd registers for
* detection to work properly (leading to ghost connected
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 50285fb4fcf5..287159bdeb0d 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -164,13 +164,6 @@ enum tc_port {
I915_MAX_TC_PORTS
};
-enum tc_port_mode {
- TC_PORT_DISCONNECTED,
- TC_PORT_TBT_ALT,
- TC_PORT_DP_ALT,
- TC_PORT_LEGACY,
-};
-
enum aux_ch {
AUX_CH_NONE = -1,
@@ -422,7 +415,6 @@ bool intel_crtc_get_pipe_config(struct intel_crtc_state *crtc_state);
bool intel_pipe_config_compare(const struct intel_crtc_state *current_config,
const struct intel_crtc_state *pipe_config,
bool fastset);
-void intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state);
void intel_plane_destroy(struct drm_plane *plane);
void i9xx_set_pipeconf(const struct intel_crtc_state *crtc_state);
@@ -511,7 +503,7 @@ void intel_crtc_arm_fifo_underrun(struct intel_crtc *crtc,
struct intel_crtc_state *crtc_state);
void ilk_pfit_disable(const struct intel_crtc_state *old_crtc_state);
-int bdw_get_pipemisc_bpp(struct intel_crtc *crtc);
+int bdw_get_pipe_misc_bpp(struct intel_crtc *crtc);
unsigned int intel_plane_fence_y_offset(const struct intel_plane_state *plane_state);
bool intel_plane_uses_fence(const struct intel_plane_state *plane_state);
diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h
index fdab7bb93a7d..e36f88a39b86 100644
--- a/drivers/gpu/drm/i915/display/intel_display_core.h
+++ b/drivers/gpu/drm/i915/display/intel_display_core.h
@@ -183,6 +183,17 @@ struct intel_hotplug {
* blocked behind the non-DP one.
*/
struct workqueue_struct *dp_wq;
+
+ /*
+ * Flag to track if long HPDs need not to be processed
+ *
+ * Some panels generate long HPDs while keep connected to the port.
+ * This can cause issues with CI tests results. In CI systems we
+ * don't expect to disconnect the panels and could ignore the long
+ * HPDs generated from the faulty panels. This flag can be used as
+ * cue to ignore the long HPDs and can be set / unset using debugfs.
+ */
+ bool ignore_long_hpd;
};
struct intel_vbt_data {
@@ -384,9 +395,15 @@ struct intel_display {
} gmbus;
struct {
- struct i915_hdcp_comp_master *master;
+ struct i915_hdcp_master *master;
bool comp_added;
+ /*
+ * HDCP message struct for allocation of memory which can be
+ * reused when sending message to gsc cs.
+ * this is only populated post Meteorlake
+ */
+ struct intel_hdcp_gsc_message *hdcp_message;
/* Mutex to protect the above hdcp component related values. */
struct mutex comp_mutex;
} hdcp;
@@ -402,6 +419,10 @@ struct intel_display {
} hti;
struct {
+ bool false_color;
+ } ips;
+
+ struct {
struct i915_power_domains domains;
/* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */
diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
index 1e654ddd0815..45113ae107ba 100644
--- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c
+++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
@@ -8,10 +8,12 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_fourcc.h>
+#include "hsw_ips.h"
#include "i915_debugfs.h"
#include "i915_irq.h"
#include "i915_reg.h"
#include "intel_de.h"
+#include "intel_crtc_state_dump.h"
#include "intel_display_debugfs.h"
#include "intel_display_power.h"
#include "intel_display_power_well.h"
@@ -27,6 +29,7 @@
#include "intel_hotplug.h"
#include "intel_panel.h"
#include "intel_psr.h"
+#include "intel_psr_regs.h"
#include "intel_sprite.h"
#include "intel_wm.h"
@@ -48,33 +51,6 @@ static int i915_frontbuffer_tracking(struct seq_file *m, void *unused)
return 0;
}
-static int i915_ips_status(struct seq_file *m, void *unused)
-{
- struct drm_i915_private *dev_priv = node_to_i915(m->private);
- intel_wakeref_t wakeref;
-
- if (!HAS_IPS(dev_priv))
- return -ENODEV;
-
- wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
-
- seq_printf(m, "Enabled by kernel parameter: %s\n",
- str_yes_no(dev_priv->params.enable_ips));
-
- if (DISPLAY_VER(dev_priv) >= 8) {
- seq_puts(m, "Currently: unknown\n");
- } else {
- if (intel_de_read(dev_priv, IPS_CTL) & IPS_ENABLE)
- seq_puts(m, "Currently: enabled\n");
- else
- seq_puts(m, "Currently: disabled\n");
- }
-
- intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
-
- return 0;
-}
-
static int i915_sr_status(struct seq_file *m, void *unused)
{
struct drm_i915_private *dev_priv = node_to_i915(m->private);
@@ -168,269 +144,6 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
return 0;
}
-static int i915_psr_sink_status_show(struct seq_file *m, void *data)
-{
- u8 val;
- static const char * const sink_status[] = {
- "inactive",
- "transition to active, capture and display",
- "active, display from RFB",
- "active, capture and display on sink device timings",
- "transition to inactive, capture and display, timing re-sync",
- "reserved",
- "reserved",
- "sink internal error",
- };
- struct drm_connector *connector = m->private;
- struct intel_dp *intel_dp =
- intel_attached_dp(to_intel_connector(connector));
- int ret;
-
- if (!CAN_PSR(intel_dp)) {
- seq_puts(m, "PSR Unsupported\n");
- return -ENODEV;
- }
-
- if (connector->status != connector_status_connected)
- return -ENODEV;
-
- ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_PSR_STATUS, &val);
-
- if (ret == 1) {
- const char *str = "unknown";
-
- val &= DP_PSR_SINK_STATE_MASK;
- if (val < ARRAY_SIZE(sink_status))
- str = sink_status[val];
- seq_printf(m, "Sink PSR status: 0x%x [%s]\n", val, str);
- } else {
- return ret;
- }
-
- return 0;
-}
-DEFINE_SHOW_ATTRIBUTE(i915_psr_sink_status);
-
-static void
-psr_source_status(struct intel_dp *intel_dp, struct seq_file *m)
-{
- struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
- const char *status = "unknown";
- u32 val, status_val;
-
- if (intel_dp->psr.psr2_enabled) {
- static const char * const live_status[] = {
- "IDLE",
- "CAPTURE",
- "CAPTURE_FS",
- "SLEEP",
- "BUFON_FW",
- "ML_UP",
- "SU_STANDBY",
- "FAST_SLEEP",
- "DEEP_SLEEP",
- "BUF_ON",
- "TG_ON"
- };
- val = intel_de_read(dev_priv,
- EDP_PSR2_STATUS(intel_dp->psr.transcoder));
- status_val = REG_FIELD_GET(EDP_PSR2_STATUS_STATE_MASK, val);
- if (status_val < ARRAY_SIZE(live_status))
- status = live_status[status_val];
- } else {
- static const char * const live_status[] = {
- "IDLE",
- "SRDONACK",
- "SRDENT",
- "BUFOFF",
- "BUFON",
- "AUXACK",
- "SRDOFFACK",
- "SRDENT_ON",
- };
- val = intel_de_read(dev_priv,
- EDP_PSR_STATUS(intel_dp->psr.transcoder));
- status_val = (val & EDP_PSR_STATUS_STATE_MASK) >>
- EDP_PSR_STATUS_STATE_SHIFT;
- if (status_val < ARRAY_SIZE(live_status))
- status = live_status[status_val];
- }
-
- seq_printf(m, "Source PSR status: %s [0x%08x]\n", status, val);
-}
-
-static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp)
-{
- struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
- struct intel_psr *psr = &intel_dp->psr;
- intel_wakeref_t wakeref;
- const char *status;
- bool enabled;
- u32 val;
-
- seq_printf(m, "Sink support: %s", str_yes_no(psr->sink_support));
- if (psr->sink_support)
- seq_printf(m, " [0x%02x]", intel_dp->psr_dpcd[0]);
- seq_puts(m, "\n");
-
- if (!psr->sink_support)
- return 0;
-
- wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
- mutex_lock(&psr->lock);
-
- if (psr->enabled)
- status = psr->psr2_enabled ? "PSR2 enabled" : "PSR1 enabled";
- else
- status = "disabled";
- seq_printf(m, "PSR mode: %s\n", status);
-
- if (!psr->enabled) {
- seq_printf(m, "PSR sink not reliable: %s\n",
- str_yes_no(psr->sink_not_reliable));
-
- goto unlock;
- }
-
- if (psr->psr2_enabled) {
- val = intel_de_read(dev_priv,
- EDP_PSR2_CTL(intel_dp->psr.transcoder));
- enabled = val & EDP_PSR2_ENABLE;
- } else {
- val = intel_de_read(dev_priv,
- EDP_PSR_CTL(intel_dp->psr.transcoder));
- enabled = val & EDP_PSR_ENABLE;
- }
- seq_printf(m, "Source PSR ctl: %s [0x%08x]\n",
- str_enabled_disabled(enabled), val);
- psr_source_status(intel_dp, m);
- seq_printf(m, "Busy frontbuffer bits: 0x%08x\n",
- psr->busy_frontbuffer_bits);
-
- /*
- * SKL+ Perf counter is reset to 0 everytime DC state is entered
- */
- if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
- val = intel_de_read(dev_priv,
- EDP_PSR_PERF_CNT(intel_dp->psr.transcoder));
- val &= EDP_PSR_PERF_CNT_MASK;
- seq_printf(m, "Performance counter: %u\n", val);
- }
-
- if (psr->debug & I915_PSR_DEBUG_IRQ) {
- seq_printf(m, "Last attempted entry at: %lld\n",
- psr->last_entry_attempt);
- seq_printf(m, "Last exit at: %lld\n", psr->last_exit);
- }
-
- if (psr->psr2_enabled) {
- u32 su_frames_val[3];
- int frame;
-
- /*
- * Reading all 3 registers before hand to minimize crossing a
- * frame boundary between register reads
- */
- for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame += 3) {
- val = intel_de_read(dev_priv,
- PSR2_SU_STATUS(intel_dp->psr.transcoder, frame));
- su_frames_val[frame / 3] = val;
- }
-
- seq_puts(m, "Frame:\tPSR2 SU blocks:\n");
-
- for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame++) {
- u32 su_blocks;
-
- su_blocks = su_frames_val[frame / 3] &
- PSR2_SU_STATUS_MASK(frame);
- su_blocks = su_blocks >> PSR2_SU_STATUS_SHIFT(frame);
- seq_printf(m, "%d\t%d\n", frame, su_blocks);
- }
-
- seq_printf(m, "PSR2 selective fetch: %s\n",
- str_enabled_disabled(psr->psr2_sel_fetch_enabled));
- }
-
-unlock:
- mutex_unlock(&psr->lock);
- intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
-
- return 0;
-}
-
-static int i915_edp_psr_status(struct seq_file *m, void *data)
-{
- struct drm_i915_private *dev_priv = node_to_i915(m->private);
- struct intel_dp *intel_dp = NULL;
- struct intel_encoder *encoder;
-
- if (!HAS_PSR(dev_priv))
- return -ENODEV;
-
- /* Find the first EDP which supports PSR */
- for_each_intel_encoder_with_psr(&dev_priv->drm, encoder) {
- intel_dp = enc_to_intel_dp(encoder);
- break;
- }
-
- if (!intel_dp)
- return -ENODEV;
-
- return intel_psr_status(m, intel_dp);
-}
-
-static int
-i915_edp_psr_debug_set(void *data, u64 val)
-{
- struct drm_i915_private *dev_priv = data;
- struct intel_encoder *encoder;
- intel_wakeref_t wakeref;
- int ret = -ENODEV;
-
- if (!HAS_PSR(dev_priv))
- return ret;
-
- for_each_intel_encoder_with_psr(&dev_priv->drm, encoder) {
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-
- drm_dbg_kms(&dev_priv->drm, "Setting PSR debug to %llx\n", val);
-
- wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
-
- // TODO: split to each transcoder's PSR debug state
- ret = intel_psr_debug_set(intel_dp, val);
-
- intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
- }
-
- return ret;
-}
-
-static int
-i915_edp_psr_debug_get(void *data, u64 *val)
-{
- struct drm_i915_private *dev_priv = data;
- struct intel_encoder *encoder;
-
- if (!HAS_PSR(dev_priv))
- return -ENODEV;
-
- for_each_intel_encoder_with_psr(&dev_priv->drm, encoder) {
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-
- // TODO: split to each transcoder's PSR debug state
- *val = READ_ONCE(intel_dp->psr.debug);
- return 0;
- }
-
- return -ENODEV;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(i915_edp_psr_debug_fops,
- i915_edp_psr_debug_get, i915_edp_psr_debug_set,
- "%llu\n");
-
static int i915_power_domain_info(struct seq_file *m, void *unused)
{
struct drm_i915_private *i915 = node_to_i915(m->private);
@@ -831,10 +544,10 @@ static const struct file_operations crtc_updates_fops = {
.write = crtc_updates_write
};
-static void crtc_updates_add(struct drm_crtc *crtc)
+static void crtc_updates_add(struct intel_crtc *crtc)
{
- debugfs_create_file("i915_update_info", 0644, crtc->debugfs_entry,
- to_intel_crtc(crtc), &crtc_updates_fops);
+ debugfs_create_file("i915_update_info", 0644, crtc->base.debugfs_entry,
+ crtc, &crtc_updates_fops);
}
#else
@@ -844,7 +557,7 @@ static void crtc_updates_info(struct seq_file *m,
{
}
-static void crtc_updates_add(struct drm_crtc *crtc)
+static void crtc_updates_add(struct intel_crtc *crtc)
{
}
#endif
@@ -1342,12 +1055,10 @@ static const struct file_operations i915_fifo_underrun_reset_ops = {
static const struct drm_info_list intel_display_debugfs_list[] = {
{"i915_frontbuffer_tracking", i915_frontbuffer_tracking, 0},
- {"i915_ips_status", i915_ips_status, 0},
{"i915_sr_status", i915_sr_status, 0},
{"i915_opregion", i915_opregion, 0},
{"i915_vbt", i915_vbt, 0},
{"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
- {"i915_edp_psr_status", i915_edp_psr_status, 0},
{"i915_power_domain_info", i915_power_domain_info, 0},
{"i915_display_info", i915_display_info, 0},
{"i915_shared_dplls_info", i915_shared_dplls_info, 0},
@@ -1364,7 +1075,6 @@ static const struct {
{"i915_dp_test_data", &i915_displayport_test_data_fops},
{"i915_dp_test_type", &i915_displayport_test_type_fops},
{"i915_dp_test_active", &i915_displayport_test_active_fops},
- {"i915_edp_psr_debug", &i915_edp_psr_debug_fops},
};
void intel_display_debugfs_register(struct drm_i915_private *i915)
@@ -1387,6 +1097,7 @@ void intel_display_debugfs_register(struct drm_i915_private *i915)
intel_dmc_debugfs_register(i915);
intel_fbc_debugfs_register(i915);
intel_hpd_debugfs_register(i915);
+ intel_psr_debugfs_register(i915);
intel_wm_debugfs_register(i915);
}
@@ -1439,16 +1150,6 @@ out:
}
DEFINE_SHOW_ATTRIBUTE(i915_hdcp_sink_capability);
-static int i915_psr_status_show(struct seq_file *m, void *data)
-{
- struct drm_connector *connector = m->private;
- struct intel_dp *intel_dp =
- intel_attached_dp(to_intel_connector(connector));
-
- return intel_psr_status(m, intel_dp);
-}
-DEFINE_SHOW_ATTRIBUTE(i915_psr_status);
-
static int i915_lpsp_capability_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
@@ -1535,6 +1236,13 @@ static int i915_dsc_fec_support_show(struct seq_file *m, void *data)
str_yes_no(crtc_state->dsc.compression_enable));
seq_printf(m, "DSC_Sink_Support: %s\n",
str_yes_no(drm_dp_sink_supports_dsc(intel_dp->dsc_dpcd)));
+ seq_printf(m, "DSC_Output_Format_Sink_Support: RGB: %s YCBCR420: %s YCBCR444: %s\n",
+ str_yes_no(drm_dp_dsc_sink_supports_format(intel_dp->dsc_dpcd,
+ DP_DSC_RGB)),
+ str_yes_no(drm_dp_dsc_sink_supports_format(intel_dp->dsc_dpcd,
+ DP_DSC_YCbCr420_Native)),
+ str_yes_no(drm_dp_dsc_sink_supports_format(intel_dp->dsc_dpcd,
+ DP_DSC_YCbCr444)));
seq_printf(m, "Force_DSC_Enable: %s\n",
str_yes_no(intel_dp->force_dsc_en));
if (!intel_dp_is_edp(intel_dp))
@@ -1660,13 +1368,80 @@ static const struct file_operations i915_dsc_bpc_fops = {
.write = i915_dsc_bpc_write
};
+static int i915_dsc_output_format_show(struct seq_file *m, void *data)
+{
+ struct drm_connector *connector = m->private;
+ struct drm_device *dev = connector->dev;
+ struct drm_crtc *crtc;
+ struct intel_crtc_state *crtc_state;
+ struct intel_encoder *encoder = intel_attached_encoder(to_intel_connector(connector));
+ int ret;
+
+ if (!encoder)
+ return -ENODEV;
+
+ ret = drm_modeset_lock_single_interruptible(&dev->mode_config.connection_mutex);
+ if (ret)
+ return ret;
+
+ crtc = connector->state->crtc;
+ if (connector->status != connector_status_connected || !crtc) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ crtc_state = to_intel_crtc_state(crtc->state);
+ seq_printf(m, "DSC_Output_Format: %s\n",
+ intel_output_format_name(crtc_state->output_format));
+
+out: drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+ return ret;
+}
+
+static ssize_t i915_dsc_output_format_write(struct file *file,
+ const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct drm_connector *connector =
+ ((struct seq_file *)file->private_data)->private;
+ struct intel_encoder *encoder = intel_attached_encoder(to_intel_connector(connector));
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ int dsc_output_format = 0;
+ int ret;
+
+ ret = kstrtoint_from_user(ubuf, len, 0, &dsc_output_format);
+ if (ret < 0)
+ return ret;
+
+ intel_dp->force_dsc_output_format = dsc_output_format;
+ *offp += len;
+
+ return len;
+}
+
+static int i915_dsc_output_format_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, i915_dsc_output_format_show, inode->i_private);
+}
+
+static const struct file_operations i915_dsc_output_format_fops = {
+ .owner = THIS_MODULE,
+ .open = i915_dsc_output_format_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = i915_dsc_output_format_write
+};
+
/*
* Returns the Current CRTC's bpc.
* Example usage: cat /sys/kernel/debug/dri/0/crtc-0/i915_current_bpc
*/
static int i915_current_bpc_show(struct seq_file *m, void *data)
{
- struct intel_crtc *crtc = to_intel_crtc(m->private);
+ struct intel_crtc *crtc = m->private;
struct intel_crtc_state *crtc_state;
int ret;
@@ -1683,9 +1458,20 @@ static int i915_current_bpc_show(struct seq_file *m, void *data)
}
DEFINE_SHOW_ATTRIBUTE(i915_current_bpc);
+/* Pipe may differ from crtc index if pipes are fused off */
+static int intel_crtc_pipe_show(struct seq_file *m, void *unused)
+{
+ struct intel_crtc *crtc = m->private;
+
+ seq_printf(m, "%c\n", pipe_name(crtc->pipe));
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(intel_crtc_pipe);
+
/**
* intel_connector_debugfs_add - add i915 specific connector debugfs files
- * @connector: pointer to a registered drm_connector
+ * @intel_connector: pointer to a registered drm_connector
*
* Cleanup will be done by drm_connector_unregister() through a call to
* drm_debugfs_connector_remove().
@@ -1701,19 +1487,11 @@ void intel_connector_debugfs_add(struct intel_connector *intel_connector)
return;
intel_drrs_connector_debugfs_add(intel_connector);
+ intel_psr_connector_debugfs_add(intel_connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
debugfs_create_file("i915_panel_timings", S_IRUGO, root,
connector, &i915_panel_fops);
- debugfs_create_file("i915_psr_sink_status", S_IRUGO, root,
- connector, &i915_psr_sink_status_fops);
- }
-
- if (HAS_PSR(dev_priv) &&
- connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
- debugfs_create_file("i915_psr_status", 0444, root,
- connector, &i915_psr_status_fops);
- }
if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
@@ -1731,6 +1509,9 @@ void intel_connector_debugfs_add(struct intel_connector *intel_connector)
debugfs_create_file("i915_dsc_bpc", 0644, root,
connector, &i915_dsc_bpc_fops);
+
+ debugfs_create_file("i915_dsc_output_format", 0644, root,
+ connector, &i915_dsc_output_format_fops);
}
if (connector->connector_type == DRM_MODE_CONNECTOR_DSI ||
@@ -1748,15 +1529,20 @@ void intel_connector_debugfs_add(struct intel_connector *intel_connector)
*
* Failure to add debugfs entries should generally be ignored.
*/
-void intel_crtc_debugfs_add(struct drm_crtc *crtc)
+void intel_crtc_debugfs_add(struct intel_crtc *crtc)
{
- if (!crtc->debugfs_entry)
+ struct dentry *root = crtc->base.debugfs_entry;
+
+ if (!root)
return;
crtc_updates_add(crtc);
- intel_drrs_crtc_debugfs_add(to_intel_crtc(crtc));
- intel_fbc_crtc_debugfs_add(to_intel_crtc(crtc));
+ intel_drrs_crtc_debugfs_add(crtc);
+ intel_fbc_crtc_debugfs_add(crtc);
+ hsw_ips_crtc_debugfs_add(crtc);
- debugfs_create_file("i915_current_bpc", 0444, crtc->debugfs_entry, crtc,
+ debugfs_create_file("i915_current_bpc", 0444, root, crtc,
&i915_current_bpc_fops);
+ debugfs_create_file("i915_pipe", 0444, root, crtc,
+ &intel_crtc_pipe_fops);
}
diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.h b/drivers/gpu/drm/i915/display/intel_display_debugfs.h
index d3a79c07c384..e1f479b7acd1 100644
--- a/drivers/gpu/drm/i915/display/intel_display_debugfs.h
+++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.h
@@ -6,18 +6,18 @@
#ifndef __INTEL_DISPLAY_DEBUGFS_H__
#define __INTEL_DISPLAY_DEBUGFS_H__
-struct drm_crtc;
struct drm_i915_private;
struct intel_connector;
+struct intel_crtc;
#ifdef CONFIG_DEBUG_FS
void intel_display_debugfs_register(struct drm_i915_private *i915);
void intel_connector_debugfs_add(struct intel_connector *connector);
-void intel_crtc_debugfs_add(struct drm_crtc *crtc);
+void intel_crtc_debugfs_add(struct intel_crtc *crtc);
#else
static inline void intel_display_debugfs_register(struct drm_i915_private *i915) {}
static inline void intel_connector_debugfs_add(struct intel_connector *connector) {}
-static inline void intel_crtc_debugfs_add(struct drm_crtc *crtc) {}
+static inline void intel_crtc_debugfs_add(struct intel_crtc *crtc) {}
#endif
#endif /* __INTEL_DISPLAY_DEBUGFS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
index f085ae971150..7c9f4288329e 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -19,8 +19,10 @@
#include "intel_mchbar_regs.h"
#include "intel_pch_refclk.h"
#include "intel_pcode.h"
+#include "intel_pps_regs.h"
#include "intel_snps_phy.h"
#include "skl_watermark.h"
+#include "skl_watermark_regs.h"
#include "vlv_sideband.h"
#define for_each_power_domain_well(__dev_priv, __power_well, __domain) \
@@ -697,7 +699,7 @@ out_verify:
}
/**
- * intel_display_power_put_async - release a power domain reference asynchronously
+ * __intel_display_power_put_async - release a power domain reference asynchronously
* @i915: i915 device instance
* @domain: power domain to reference
* @wakeref: wakeref acquired for the reference that is being released
@@ -1182,8 +1184,10 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
"CPU PWM2 enabled\n");
I915_STATE_WARN(intel_de_read(dev_priv, BLC_PWM_PCH_CTL1) & BLM_PCH_PWM_ENABLE,
"PCH PWM1 enabled\n");
- I915_STATE_WARN(intel_de_read(dev_priv, UTIL_PIN_CTL) & UTIL_PIN_ENABLE,
- "Utility pin enabled\n");
+ I915_STATE_WARN((intel_de_read(dev_priv, UTIL_PIN_CTL) &
+ (UTIL_PIN_ENABLE | UTIL_PIN_MODE_MASK)) ==
+ (UTIL_PIN_ENABLE | UTIL_PIN_MODE_PWM),
+ "Utility pin enabled in PWM mode\n");
I915_STATE_WARN(intel_de_read(dev_priv, PCH_GTC_CTL) & PCH_GTC_ENABLE,
"PCH GTC enabled\n");
@@ -1625,6 +1629,10 @@ static void icl_display_core_init(struct drm_i915_private *dev_priv,
intel_power_well_enable(dev_priv, well);
mutex_unlock(&power_domains->lock);
+ if (DISPLAY_VER(dev_priv) == 14)
+ intel_de_rmw(dev_priv, DC_STATE_EN,
+ HOLD_PHY_PG1_LATCH | HOLD_PHY_CLKREQ_PG1_LATCH, 0);
+
/* 4. Enable CDCLK. */
intel_cdclk_init_hw(dev_priv);
@@ -1678,6 +1686,10 @@ static void icl_display_core_uninit(struct drm_i915_private *dev_priv)
/* 3. Disable CD clock */
intel_cdclk_uninit_hw(dev_priv);
+ if (DISPLAY_VER(dev_priv) == 14)
+ intel_de_rmw(dev_priv, DC_STATE_EN, 0,
+ HOLD_PHY_PG1_LATCH | HOLD_PHY_CLKREQ_PG1_LATCH);
+
/*
* 4. Disable Power Well 1 (PG1).
* The AUX IO power wells are toggled on demand, so they are already
diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c
index 1676df1dc066..62bafcbc7937 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power_well.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c
@@ -15,6 +15,7 @@
#include "intel_dkl_phy.h"
#include "intel_dkl_phy_regs.h"
#include "intel_dmc.h"
+#include "intel_dp_aux_regs.h"
#include "intel_dpio_phy.h"
#include "intel_dpll.h"
#include "intel_hotplug.h"
@@ -818,8 +819,10 @@ void gen9_enable_dc5(struct drm_i915_private *dev_priv)
static void assert_can_enable_dc6(struct drm_i915_private *dev_priv)
{
drm_WARN_ONCE(&dev_priv->drm,
- intel_de_read(dev_priv, UTIL_PIN_CTL) & UTIL_PIN_ENABLE,
- "Backlight is not disabled.\n");
+ (intel_de_read(dev_priv, UTIL_PIN_CTL) &
+ (UTIL_PIN_ENABLE | UTIL_PIN_MODE_MASK)) ==
+ (UTIL_PIN_ENABLE | UTIL_PIN_MODE_PWM),
+ "Utility pin enabled in PWM mode\n");
drm_WARN_ONCE(&dev_priv->drm,
(intel_de_read(dev_priv, DC_STATE_EN) &
DC_STATE_EN_UPTO_DC6),
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index c32bfba06ca1..47395b39c8f4 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -43,7 +43,7 @@
#include <drm/drm_rect.h>
#include <drm/drm_vblank.h>
#include <drm/drm_vblank_work.h>
-#include <drm/i915_mei_hdcp_interface.h>
+#include <drm/i915_hdcp_interface.h>
#include <media/cec-notifier.h>
#include "i915_vma.h"
@@ -60,6 +60,7 @@ struct __intel_global_objs_state;
struct intel_ddi_buf_trans;
struct intel_fbc;
struct intel_connector;
+struct intel_tc_port;
/*
* Display related stuff
@@ -169,9 +170,6 @@ struct intel_encoder {
int (*compute_config_late)(struct intel_encoder *,
struct intel_crtc_state *,
struct drm_connector_state *);
- void (*update_prepare)(struct intel_atomic_state *,
- struct intel_encoder *,
- struct intel_crtc *);
void (*pre_pll_enable)(struct intel_atomic_state *,
struct intel_encoder *,
const struct intel_crtc_state *,
@@ -184,9 +182,6 @@ struct intel_encoder {
struct intel_encoder *,
const struct intel_crtc_state *,
const struct drm_connector_state *);
- void (*update_complete)(struct intel_atomic_state *,
- struct intel_encoder *,
- struct intel_crtc *);
void (*disable)(struct intel_atomic_state *,
struct intel_encoder *,
const struct intel_crtc_state *,
@@ -255,6 +250,11 @@ struct intel_encoder {
* Returns whether the port clock is enabled or not.
*/
bool (*is_clock_enabled)(struct intel_encoder *encoder);
+ /*
+ * Returns the PLL type the port uses.
+ */
+ enum icl_port_dpll_id (*port_pll_type)(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state);
const struct intel_ddi_buf_trans *(*get_buf_trans)(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
int *n_entries);
@@ -1152,6 +1152,7 @@ struct intel_crtc_state {
bool has_psr2;
bool enable_psr2_sel_fetch;
bool req_psr2_sdp_prior_scanline;
+ bool wm_level_disabled;
u32 dc3co_exitline;
u16 su_y_granularity;
struct drm_dp_vsc_sdp psr_vsc;
@@ -1735,6 +1736,7 @@ struct intel_dp {
/* Display stream compression testing */
bool force_dsc_en;
+ int force_dsc_output_format;
int force_dsc_bpc;
bool hobl_failed;
@@ -1775,16 +1777,7 @@ struct intel_digital_port {
intel_wakeref_t ddi_io_wakeref;
intel_wakeref_t aux_wakeref;
- struct mutex tc_lock; /* protects the TypeC port mode */
- intel_wakeref_t tc_lock_wakeref;
- enum intel_display_power_domain tc_lock_power_domain;
- struct delayed_work tc_disconnect_phy_work;
- int tc_link_refcount;
- bool tc_legacy_port:1;
- char tc_port_name[8];
- enum tc_port_mode tc_mode;
- enum phy_fia tc_phy_fia;
- u8 tc_phy_fia_idx;
+ struct intel_tc_port *tc;
/* protects num_hdcp_streams reference count, hdcp_port_data and hdcp_auth_status */
struct mutex hdcp_mutex;
diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c
index 6b162f77340e..8a88de67ff0a 100644
--- a/drivers/gpu/drm/i915/display/intel_dmc.c
+++ b/drivers/gpu/drm/i915/display/intel_dmc.c
@@ -89,10 +89,13 @@ static struct intel_dmc *i915_to_dmc(struct drm_i915_private *i915)
__stringify(major) "_" \
__stringify(minor) ".bin"
+#define XELPDP_DMC_MAX_FW_SIZE 0x7000
#define DISPLAY_VER13_DMC_MAX_FW_SIZE 0x20000
-
#define DISPLAY_VER12_DMC_MAX_FW_SIZE ICL_DMC_MAX_FW_SIZE
+#define MTL_DMC_PATH DMC_PATH(mtl)
+MODULE_FIRMWARE(MTL_DMC_PATH);
+
#define DG2_DMC_PATH DMC_LEGACY_PATH(dg2, 2, 08)
MODULE_FIRMWARE(DG2_DMC_PATH);
@@ -424,15 +427,12 @@ static void disable_all_event_handlers(struct drm_i915_private *i915)
}
}
-static void pipedmc_clock_gating_wa(struct drm_i915_private *i915, bool enable)
+static void adlp_pipedmc_clock_gating_wa(struct drm_i915_private *i915, bool enable)
{
enum pipe pipe;
- if (DISPLAY_VER(i915) < 13)
- return;
-
/*
- * Wa_16015201720:adl-p,dg2, mtl
+ * Wa_16015201720:adl-p,dg2
* The WA requires clock gating to be disabled all the time
* for pipe A and B.
* For pipe C and D clock gating needs to be disabled only
@@ -448,6 +448,25 @@ static void pipedmc_clock_gating_wa(struct drm_i915_private *i915, bool enable)
PIPEDMC_GATING_DIS, 0);
}
+static void mtl_pipedmc_clock_gating_wa(struct drm_i915_private *i915)
+{
+ /*
+ * Wa_16015201720
+ * The WA requires clock gating to be disabled all the time
+ * for pipe A and B.
+ */
+ intel_de_rmw(i915, GEN9_CLKGATE_DIS_0, 0,
+ MTL_PIPEDMC_GATING_DIS_A | MTL_PIPEDMC_GATING_DIS_B);
+}
+
+static void pipedmc_clock_gating_wa(struct drm_i915_private *i915, bool enable)
+{
+ if (DISPLAY_VER(i915) >= 14 && enable)
+ mtl_pipedmc_clock_gating_wa(i915);
+ else if (DISPLAY_VER(i915) == 13)
+ adlp_pipedmc_clock_gating_wa(i915, enable);
+}
+
void intel_dmc_enable_pipe(struct drm_i915_private *i915, enum pipe pipe)
{
enum intel_dmc_id dmc_id = PIPE_TO_DMC_ID(pipe);
@@ -979,7 +998,10 @@ void intel_dmc_init(struct drm_i915_private *i915)
INIT_WORK(&dmc->work, dmc_load_work_fn);
- if (IS_DG2(i915)) {
+ if (IS_METEORLAKE(i915)) {
+ dmc->fw_path = MTL_DMC_PATH;
+ dmc->max_fw_size = XELPDP_DMC_MAX_FW_SIZE;
+ } else if (IS_DG2(i915)) {
dmc->fw_path = DG2_DMC_PATH;
dmc->max_fw_size = DISPLAY_VER13_DMC_MAX_FW_SIZE;
} else if (IS_ALDERLAKE_P(i915)) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index aee93b0d810e..529ee22be872 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -76,6 +76,7 @@
#include "intel_tc.h"
#include "intel_vdsc.h"
#include "intel_vrr.h"
+#include "intel_crtc_state_dump.h"
/* DP DSC throughput values used for slice count calculations KPixels/s */
#define DP_DSC_PEAK_PIXEL_RATE 2720000
@@ -687,6 +688,12 @@ u32 intel_dp_dsc_nearest_valid_bpp(struct drm_i915_private *i915, u32 bpp, u32 p
/* From XE_LPD onwards we support from bpc upto uncompressed bpp-1 BPPs */
if (DISPLAY_VER(i915) >= 13) {
bits_per_pixel = min(bits_per_pixel, pipe_bpp - 1);
+
+ /*
+ * According to BSpec, 27 is the max DSC output bpp,
+ * 8 is the min DSC output bpp
+ */
+ bits_per_pixel = clamp_t(u32, bits_per_pixel, 8, 27);
} else {
/* Find the nearest match in the array of known BPPs from VESA */
for (i = 0; i < ARRAY_SIZE(valid_dsc_bpp) - 1; i++) {
@@ -716,9 +723,19 @@ u16 intel_dp_dsc_get_output_bpp(struct drm_i915_private *i915,
* (LinkSymbolClock)* 8 * (TimeSlots / 64)
* for SST -> TimeSlots is 64(i.e all TimeSlots that are available)
* for MST -> TimeSlots has to be calculated, based on mode requirements
+ *
+ * Due to FEC overhead, the available bw is reduced to 97.2261%.
+ * To support the given mode:
+ * Bandwidth required should be <= Available link Bandwidth * FEC Overhead
+ * =>ModeClock * bits_per_pixel <= Available Link Bandwidth * FEC Overhead
+ * =>bits_per_pixel <= Available link Bandwidth * FEC Overhead / ModeClock
+ * =>bits_per_pixel <= (NumberOfLanes * LinkSymbolClock) * 8 (TimeSlots / 64) /
+ * (ModeClock / FEC Overhead)
+ * =>bits_per_pixel <= (NumberOfLanes * LinkSymbolClock * TimeSlots) /
+ * (ModeClock / FEC Overhead * 8)
*/
- bits_per_pixel = DIV_ROUND_UP((link_clock * lane_count) * timeslots,
- intel_dp_mode_to_fec_clock(mode_clock) * 8);
+ bits_per_pixel = ((link_clock * lane_count) * timeslots) /
+ (intel_dp_mode_to_fec_clock(mode_clock) * 8);
drm_dbg_kms(&i915->drm, "Max link bpp is %u for %u timeslots "
"total bw %u pixel clock %u\n",
@@ -771,6 +788,13 @@ u8 intel_dp_dsc_get_slice_count(struct intel_dp *intel_dp,
min_slice_count = DIV_ROUND_UP(mode_clock,
DP_DSC_MAX_ENC_THROUGHPUT_1);
+ /*
+ * Due to some DSC engine BW limitations, we need to enable second
+ * slice and VDSC engine, whenever we approach close enough to max CDCLK
+ */
+ if (mode_clock >= ((i915->display.cdclk.max_cdclk_freq * 85) / 100))
+ min_slice_count = max_t(u8, min_slice_count, 2);
+
max_slice_width = drm_dp_dsc_sink_max_slice_width(intel_dp->dsc_dpcd);
if (max_slice_width < DP_DSC_MIN_SLICE_WIDTH_VALUE) {
drm_dbg_kms(&i915->drm,
@@ -810,6 +834,9 @@ intel_dp_output_format(struct intel_connector *connector,
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
+ if (intel_dp->force_dsc_output_format)
+ return intel_dp->force_dsc_output_format;
+
if (!connector->base.ycbcr_420_allowed || !ycbcr_420_output)
return INTEL_OUTPUT_FORMAT_RGB;
@@ -1467,9 +1494,10 @@ static int intel_dp_dsc_compute_params(struct intel_encoder *encoder,
vdsc_cfg->dsc_version_minor =
min(intel_dp_source_dsc_version_minor(intel_dp),
intel_dp_sink_dsc_version_minor(intel_dp));
-
- vdsc_cfg->convert_rgb = intel_dp->dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT] &
- DP_DSC_RGB;
+ if (vdsc_cfg->convert_rgb)
+ vdsc_cfg->convert_rgb =
+ intel_dp->dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT] &
+ DP_DSC_RGB;
line_buf_depth = drm_dp_dsc_sink_line_buf_depth(intel_dp->dsc_dpcd);
if (!line_buf_depth) {
@@ -1492,6 +1520,31 @@ static int intel_dp_dsc_compute_params(struct intel_encoder *encoder,
return drm_dsc_compute_rc_parameters(vdsc_cfg);
}
+static bool intel_dp_dsc_supports_format(struct intel_dp *intel_dp,
+ enum intel_output_format output_format)
+{
+ u8 sink_dsc_format;
+
+ switch (output_format) {
+ case INTEL_OUTPUT_FORMAT_RGB:
+ sink_dsc_format = DP_DSC_RGB;
+ break;
+ case INTEL_OUTPUT_FORMAT_YCBCR444:
+ sink_dsc_format = DP_DSC_YCbCr444;
+ break;
+ case INTEL_OUTPUT_FORMAT_YCBCR420:
+ if (min(intel_dp_source_dsc_version_minor(intel_dp),
+ intel_dp_sink_dsc_version_minor(intel_dp)) < 2)
+ return false;
+ sink_dsc_format = DP_DSC_YCbCr420_Native;
+ break;
+ default:
+ return false;
+ }
+
+ return drm_dp_dsc_sink_supports_format(intel_dp->dsc_dpcd, sink_dsc_format);
+}
+
int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state,
@@ -1512,6 +1565,9 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
if (!intel_dp_supports_dsc(intel_dp, pipe_config))
return -EINVAL;
+ if (!intel_dp_dsc_supports_format(intel_dp, pipe_config->output_format))
+ return -EINVAL;
+
if (compute_pipe_bpp)
pipe_bpp = intel_dp_dsc_compute_bpp(intel_dp, conn_state->max_requested_bpc);
else
@@ -1545,6 +1601,11 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
pipe_config->dsc.slice_count =
drm_dp_dsc_sink_max_slice_count(intel_dp->dsc_dpcd,
true);
+ if (!pipe_config->dsc.slice_count) {
+ drm_dbg_kms(&dev_priv->drm, "Unsupported Slice Count %d\n",
+ pipe_config->dsc.slice_count);
+ return -EINVAL;
+ }
} else {
u16 dsc_max_output_bpp = 0;
u8 dsc_dp_slice_count;
@@ -1559,6 +1620,15 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
pipe_config->bigjoiner_pipes,
pipe_bpp,
timeslots);
+ /*
+ * According to DSC 1.2a Section 4.1.1 Table 4.1 the maximum
+ * supported PPS value can be 63.9375 and with the further
+ * mention that bpp should be programmed double the target bpp
+ * restricting our target bpp to be 31.9375 at max
+ */
+ if (pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
+ dsc_max_output_bpp = min_t(u16, dsc_max_output_bpp, 31 << 4);
+
if (!dsc_max_output_bpp) {
drm_dbg_kms(&dev_priv->drm,
"Compressed BPP not supported\n");
@@ -1597,16 +1667,8 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
* is greater than the maximum Cdclock and if slice count is even
* then we need to use 2 VDSC instances.
*/
- if (adjusted_mode->crtc_clock > dev_priv->display.cdclk.max_cdclk_freq ||
- pipe_config->bigjoiner_pipes) {
- if (pipe_config->dsc.slice_count > 1) {
- pipe_config->dsc.dsc_split = true;
- } else {
- drm_dbg_kms(&dev_priv->drm,
- "Cannot split stream to use 2 VDSC instances\n");
- return -EINVAL;
- }
- }
+ if (pipe_config->bigjoiner_pipes || pipe_config->dsc.slice_count > 1)
+ pipe_config->dsc.dsc_split = true;
ret = intel_dp_dsc_compute_params(&dig_port->base, pipe_config);
if (ret < 0) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c
index 96967e21c94c..524bd6da260c 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c
@@ -10,6 +10,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_dp_aux.h"
+#include "intel_dp_aux_regs.h"
#include "intel_pps.h"
#include "intel_tc.h"
@@ -118,6 +119,32 @@ static u32 skl_get_aux_clock_divider(struct intel_dp *intel_dp, int index)
return index ? 0 : 1;
}
+static int intel_dp_aux_sync_len(void)
+{
+ int precharge = 16; /* 10-16 */
+ int preamble = 16;
+
+ return precharge + preamble;
+}
+
+static int intel_dp_aux_fw_sync_len(void)
+{
+ int precharge = 10; /* 10-16 */
+ int preamble = 8;
+
+ return precharge + preamble;
+}
+
+static int g4x_dp_aux_precharge_len(void)
+{
+ int precharge_min = 10;
+ int preamble = 16;
+
+ /* HW wants the length of the extra precharge in 2us units */
+ return (intel_dp_aux_sync_len() -
+ precharge_min - preamble) / 2;
+}
+
static u32 g4x_get_aux_send_ctl(struct intel_dp *intel_dp,
int send_bytes,
u32 aux_clock_divider)
@@ -140,7 +167,7 @@ static u32 g4x_get_aux_send_ctl(struct intel_dp *intel_dp,
timeout |
DP_AUX_CH_CTL_RECEIVE_ERROR |
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
- (3 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
+ (g4x_dp_aux_precharge_len() << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT);
}
@@ -164,8 +191,8 @@ static u32 skl_get_aux_send_ctl(struct intel_dp *intel_dp,
DP_AUX_CH_CTL_TIME_OUT_MAX |
DP_AUX_CH_CTL_RECEIVE_ERROR |
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
- DP_AUX_CH_CTL_FW_SYNC_PULSE_SKL(32) |
- DP_AUX_CH_CTL_SYNC_PULSE_SKL(32);
+ DP_AUX_CH_CTL_FW_SYNC_PULSE_SKL(intel_dp_aux_fw_sync_len()) |
+ DP_AUX_CH_CTL_SYNC_PULSE_SKL(intel_dp_aux_sync_len());
if (intel_tc_port_in_tbt_alt_mode(dig_port))
ret |= DP_AUX_CH_CTL_TBT_IO;
@@ -205,8 +232,19 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp,
for (i = 0; i < ARRAY_SIZE(ch_data); i++)
ch_data[i] = intel_dp->aux_ch_data_reg(intel_dp, i);
- if (is_tc_port)
+ if (is_tc_port) {
intel_tc_port_lock(dig_port);
+ /*
+ * Abort transfers on a disconnected port as required by
+ * DP 1.4a link CTS 4.2.1.5, also avoiding the long AUX
+ * timeouts that would otherwise happen.
+ * TODO: abort the transfer on non-TC ports as well.
+ */
+ if (!intel_tc_port_connected_locked(&dig_port->base)) {
+ ret = -ENXIO;
+ goto out_unlock;
+ }
+ }
aux_domain = intel_aux_power_domain(dig_port);
@@ -367,7 +405,7 @@ out:
intel_pps_unlock(intel_dp, pps_wakeref);
intel_display_power_put_async(i915, aux_domain, aux_wakeref);
-
+out_unlock:
if (is_tc_port)
intel_tc_port_unlock(dig_port);
diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_regs.h b/drivers/gpu/drm/i915/display/intel_dp_aux_regs.h
new file mode 100644
index 000000000000..5702f318d537
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_regs.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_DP_AUX_REGS_H__
+#define __INTEL_DP_AUX_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+/*
+ * The aux channel provides a way to talk to the signal sink for DDC etc. Max
+ * packet size supported is 20 bytes in each direction, hence the 5 fixed data
+ * registers
+ */
+#define _DPA_AUX_CH_CTL (DISPLAY_MMIO_BASE(dev_priv) + 0x64010)
+#define _DPA_AUX_CH_DATA1 (DISPLAY_MMIO_BASE(dev_priv) + 0x64014)
+
+#define _DPB_AUX_CH_CTL (DISPLAY_MMIO_BASE(dev_priv) + 0x64110)
+#define _DPB_AUX_CH_DATA1 (DISPLAY_MMIO_BASE(dev_priv) + 0x64114)
+
+#define DP_AUX_CH_CTL(aux_ch) _MMIO_PORT(aux_ch, _DPA_AUX_CH_CTL, _DPB_AUX_CH_CTL)
+#define DP_AUX_CH_DATA(aux_ch, i) _MMIO(_PORT(aux_ch, _DPA_AUX_CH_DATA1, _DPB_AUX_CH_DATA1) + (i) * 4) /* 5 registers */
+
+#define _XELPDP_USBC1_AUX_CH_CTL 0x16F210
+#define _XELPDP_USBC2_AUX_CH_CTL 0x16F410
+#define _XELPDP_USBC3_AUX_CH_CTL 0x16F610
+#define _XELPDP_USBC4_AUX_CH_CTL 0x16F810
+
+#define XELPDP_DP_AUX_CH_CTL(aux_ch) _MMIO(_PICK(aux_ch, \
+ _DPA_AUX_CH_CTL, \
+ _DPB_AUX_CH_CTL, \
+ 0, /* port/aux_ch C is non-existent */ \
+ _XELPDP_USBC1_AUX_CH_CTL, \
+ _XELPDP_USBC2_AUX_CH_CTL, \
+ _XELPDP_USBC3_AUX_CH_CTL, \
+ _XELPDP_USBC4_AUX_CH_CTL))
+
+#define _XELPDP_USBC1_AUX_CH_DATA1 0x16F214
+#define _XELPDP_USBC2_AUX_CH_DATA1 0x16F414
+#define _XELPDP_USBC3_AUX_CH_DATA1 0x16F614
+#define _XELPDP_USBC4_AUX_CH_DATA1 0x16F814
+
+#define XELPDP_DP_AUX_CH_DATA(aux_ch, i) _MMIO(_PICK(aux_ch, \
+ _DPA_AUX_CH_DATA1, \
+ _DPB_AUX_CH_DATA1, \
+ 0, /* port/aux_ch C is non-existent */ \
+ _XELPDP_USBC1_AUX_CH_DATA1, \
+ _XELPDP_USBC2_AUX_CH_DATA1, \
+ _XELPDP_USBC3_AUX_CH_DATA1, \
+ _XELPDP_USBC4_AUX_CH_DATA1) + (i) * 4)
+
+#define DP_AUX_CH_CTL_SEND_BUSY (1 << 31)
+#define DP_AUX_CH_CTL_DONE (1 << 30)
+#define DP_AUX_CH_CTL_INTERRUPT (1 << 29)
+#define DP_AUX_CH_CTL_TIME_OUT_ERROR (1 << 28)
+#define DP_AUX_CH_CTL_TIME_OUT_400us (0 << 26)
+#define DP_AUX_CH_CTL_TIME_OUT_600us (1 << 26)
+#define DP_AUX_CH_CTL_TIME_OUT_800us (2 << 26)
+#define DP_AUX_CH_CTL_TIME_OUT_MAX (3 << 26) /* Varies per platform */
+#define DP_AUX_CH_CTL_TIME_OUT_MASK (3 << 26)
+#define DP_AUX_CH_CTL_RECEIVE_ERROR (1 << 25)
+#define DP_AUX_CH_CTL_MESSAGE_SIZE_MASK (0x1f << 20)
+#define DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT 20
+#define XELPDP_DP_AUX_CH_CTL_POWER_REQUEST REG_BIT(19)
+#define XELPDP_DP_AUX_CH_CTL_POWER_STATUS REG_BIT(18)
+#define DP_AUX_CH_CTL_PRECHARGE_2US_MASK (0xf << 16)
+#define DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT 16
+#define DP_AUX_CH_CTL_AUX_AKSV_SELECT (1 << 15)
+#define DP_AUX_CH_CTL_MANCHESTER_TEST (1 << 14)
+#define DP_AUX_CH_CTL_SYNC_TEST (1 << 13)
+#define DP_AUX_CH_CTL_DEGLITCH_TEST (1 << 12)
+#define DP_AUX_CH_CTL_PRECHARGE_TEST (1 << 11)
+#define DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK (0x7ff)
+#define DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT 0
+#define DP_AUX_CH_CTL_PSR_DATA_AUX_REG_SKL (1 << 14)
+#define DP_AUX_CH_CTL_FS_DATA_AUX_REG_SKL (1 << 13)
+#define DP_AUX_CH_CTL_GTC_DATA_AUX_REG_SKL (1 << 12)
+#define DP_AUX_CH_CTL_TBT_IO (1 << 11)
+#define DP_AUX_CH_CTL_FW_SYNC_PULSE_SKL_MASK (0x1f << 5)
+#define DP_AUX_CH_CTL_FW_SYNC_PULSE_SKL(c) (((c) - 1) << 5)
+#define DP_AUX_CH_CTL_SYNC_PULSE_SKL(c) ((c) - 1)
+
+#endif /* __INTEL_DP_AUX_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
index 3d3efcf02011..d638054c74ac 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
@@ -1379,10 +1379,6 @@ intel_dp_128b132b_lane_cds(struct intel_dp *intel_dp,
}
}
- /* FIXME: Should DP_TRAINING_PATTERN_DISABLE be written first? */
- if (intel_dp->set_idle_link_train)
- intel_dp->set_idle_link_train(intel_dp, crtc_state);
-
return true;
}
@@ -1433,7 +1429,11 @@ intel_dp_128b132b_link_train(struct intel_dp *intel_dp,
void intel_dp_start_link_train(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
{
+ struct drm_i915_private *i915 = dp_to_i915(intel_dp);
+ struct intel_connector *connector = intel_dp->attached_connector;
+ struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
bool passed;
+
/*
* TODO: Reiniting LTTPRs here won't be needed once proper connector
* HW state readout is added.
@@ -1451,6 +1451,46 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp,
else
passed = intel_dp_link_train_all_phys(intel_dp, crtc_state, lttpr_count);
+ /*
+ * Ignore the link failure in CI
+ *
+ * In fixed enviroments like CI, sometimes unexpected long HPDs are
+ * generated by the displays. If ignore_long_hpd flag is set, such long
+ * HPDs are ignored. And probably as a consequence of these ignored
+ * long HPDs, subsequent link trainings are failed resulting into CI
+ * execution failures.
+ *
+ * For test cases which rely on the link training or processing of HPDs
+ * ignore_long_hpd flag can unset from the testcase.
+ */
+ if (!passed && i915->display.hotplug.ignore_long_hpd) {
+ drm_dbg_kms(&i915->drm,
+ "[CONNECTOR:%d:%s][ENCODER:%d:%s] Ignore the link failure\n",
+ connector->base.base.id, connector->base.name,
+ encoder->base.base.id, encoder->base.name);
+ return;
+ }
+
if (!passed)
intel_dp_schedule_fallback_link_training(intel_dp, crtc_state);
}
+
+void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *i915 = dp_to_i915(intel_dp);
+
+ /*
+ * VIDEO_DIP_CTL register bit 31 should be set to '0' to not
+ * disable SDP CRC. This is applicable for Display version 13.
+ * Default value of bit 31 is '0' hence discarding the write
+ * TODO: Corrective actions on SDP corruption yet to be defined
+ */
+ if (intel_dp_is_uhbr(crtc_state))
+ /* DP v2.0 SCR on SDP CRC16 for 128b/132b Link Layer */
+ drm_dp_dpcd_writeb(&intel_dp->aux,
+ DP_SDP_ERROR_DETECTION_CONFIGURATION,
+ DP_SDP_CRC16_128B132B_EN);
+
+ drm_dbg_kms(&i915->drm, "DP2.0 SDP CRC16 for 128b/132b enabled\n");
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h
index 7fa1c0833096..2c8f2775891b 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h
@@ -39,4 +39,6 @@ static inline u8 intel_dp_training_pattern_symbol(u8 pattern)
return pattern & ~DP_LINK_SCRAMBLING_DISABLE;
}
+void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state);
#endif /* __INTEL_DP_LINK_TRAINING_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index a860cbc5dbea..2c49d9ab86a2 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -45,6 +45,27 @@
#include "intel_hotplug.h"
#include "skl_scaler.h"
+static int intel_dp_mst_check_constraints(struct drm_i915_private *i915, int bpp,
+ const struct drm_display_mode *adjusted_mode,
+ struct intel_crtc_state *crtc_state,
+ bool dsc)
+{
+ if (intel_dp_is_uhbr(crtc_state) && DISPLAY_VER(i915) <= 13 && dsc) {
+ int output_bpp = bpp;
+ /* DisplayPort 2 128b/132b, bits per lane is always 32 */
+ int symbol_clock = crtc_state->port_clock / 32;
+
+ if (output_bpp * adjusted_mode->crtc_clock >=
+ symbol_clock * 72) {
+ drm_dbg_kms(&i915->drm, "UHBR check failed(required bw %d available %d)\n",
+ output_bpp * adjusted_mode->crtc_clock, symbol_clock * 72);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
int max_bpp,
@@ -81,12 +102,16 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
}
for (bpp = max_bpp; bpp >= min_bpp; bpp -= step) {
+ drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp);
+
+ ret = intel_dp_mst_check_constraints(i915, bpp, adjusted_mode, crtc_state, dsc);
+ if (ret)
+ continue;
+
crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
dsc ? bpp << 4 : bpp,
dsc);
- drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp);
-
slots = drm_dp_atomic_find_time_slots(state, &intel_dp->mst_mgr,
connector->port,
crtc_state->pbn);
@@ -104,8 +129,8 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
}
}
- /* Despite slots are non-zero, we still failed the atomic check */
- if (ret && slots >= 0)
+ /* We failed to find a proper bpp/timeslots, return error */
+ if (ret)
slots = ret;
if (slots < 0) {
@@ -232,7 +257,7 @@ static int intel_dp_dsc_mst_compute_link_config(struct intel_encoder *encoder,
return slots;
}
- intel_link_compute_m_n(crtc_state->pipe_bpp,
+ intel_link_compute_m_n(crtc_state->dsc.compressed_bpp,
crtc_state->lane_count,
adjusted_mode->crtc_clock,
crtc_state->port_clock,
@@ -623,6 +648,20 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
intel_dp->active_mst_links);
}
+static void intel_mst_post_pll_disable_dp(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
+ const struct intel_crtc_state *old_crtc_state,
+ const struct drm_connector_state *old_conn_state)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
+ struct intel_digital_port *dig_port = intel_mst->primary;
+ struct intel_dp *intel_dp = &dig_port->dp;
+
+ if (intel_dp->active_mst_links == 0 &&
+ dig_port->base.post_pll_disable)
+ dig_port->base.post_pll_disable(state, encoder, old_crtc_state, old_conn_state);
+}
+
static void intel_mst_pre_pll_enable_dp(struct intel_atomic_state *state,
struct intel_encoder *encoder,
const struct intel_crtc_state *pipe_config,
@@ -635,6 +674,13 @@ static void intel_mst_pre_pll_enable_dp(struct intel_atomic_state *state,
if (intel_dp->active_mst_links == 0)
dig_port->base.pre_pll_enable(state, &dig_port->base,
pipe_config, NULL);
+ else
+ /*
+ * The port PLL state needs to get updated for secondary
+ * streams as for the primary stream.
+ */
+ intel_ddi_update_active_dpll(state, &dig_port->base,
+ to_intel_crtc(pipe_config->uapi.crtc));
}
static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
@@ -1146,6 +1192,7 @@ intel_dp_create_fake_mst_encoder(struct intel_digital_port *dig_port, enum pipe
intel_encoder->compute_config_late = intel_dp_mst_compute_config_late;
intel_encoder->disable = intel_mst_disable_dp;
intel_encoder->post_disable = intel_mst_post_disable_dp;
+ intel_encoder->post_pll_disable = intel_mst_post_pll_disable_dp;
intel_encoder->update_pipe = intel_ddi_update_pipe;
intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
intel_encoder->pre_enable = intel_mst_pre_enable_dp;
diff --git a/drivers/gpu/drm/i915/display/intel_dpt.c b/drivers/gpu/drm/i915/display/intel_dpt.c
index ad1a37b515fb..b8027392144d 100644
--- a/drivers/gpu/drm/i915/display/intel_dpt.c
+++ b/drivers/gpu/drm/i915/display/intel_dpt.c
@@ -9,6 +9,8 @@
#include "gt/gen8_ppgtt.h"
#include "i915_drv.h"
+#include "i915_reg.h"
+#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_dpt.h"
#include "intel_fb.h"
@@ -301,6 +303,7 @@ intel_dpt_create(struct intel_framebuffer *fb)
vm->pte_encode = gen8_ggtt_pte_encode;
dpt->obj = dpt_obj;
+ dpt->obj->is_dpt = true;
return &dpt->vm;
}
@@ -309,5 +312,29 @@ void intel_dpt_destroy(struct i915_address_space *vm)
{
struct i915_dpt *dpt = i915_vm_to_dpt(vm);
+ dpt->obj->is_dpt = false;
i915_vm_put(&dpt->vm);
}
+
+void intel_dpt_configure(struct intel_crtc *crtc)
+{
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+
+ if (DISPLAY_VER(i915) == 14) {
+ enum pipe pipe = crtc->pipe;
+ enum plane_id plane_id;
+
+ for_each_plane_id_on_crtc(crtc, plane_id) {
+ if (plane_id == PLANE_CURSOR)
+ continue;
+
+ intel_de_rmw(i915, PLANE_CHICKEN(pipe, plane_id),
+ PLANE_CHICKEN_DISABLE_DPT,
+ i915->params.enable_dpt ? 0 : PLANE_CHICKEN_DISABLE_DPT);
+ }
+ } else if (DISPLAY_VER(i915) == 13) {
+ intel_de_rmw(i915, CHICKEN_MISC_2,
+ CHICKEN_MISC_DISABLE_DPT,
+ i915->params.enable_dpt ? 0 : CHICKEN_MISC_DISABLE_DPT);
+ }
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dpt.h b/drivers/gpu/drm/i915/display/intel_dpt.h
index e18a9f767b11..d9a166550185 100644
--- a/drivers/gpu/drm/i915/display/intel_dpt.h
+++ b/drivers/gpu/drm/i915/display/intel_dpt.h
@@ -10,6 +10,7 @@ struct drm_i915_private;
struct i915_address_space;
struct i915_vma;
+struct intel_crtc;
struct intel_framebuffer;
void intel_dpt_destroy(struct i915_address_space *vm);
@@ -19,5 +20,6 @@ void intel_dpt_suspend(struct drm_i915_private *i915);
void intel_dpt_resume(struct drm_i915_private *i915);
struct i915_address_space *
intel_dpt_create(struct intel_framebuffer *fb);
+void intel_dpt_configure(struct intel_crtc *crtc);
#endif /* __INTEL_DPT_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c
index 19e422da57dc..bed058d2c3ac 100644
--- a/drivers/gpu/drm/i915/display/intel_dsb.c
+++ b/drivers/gpu/drm/i915/display/intel_dsb.c
@@ -11,6 +11,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_dsb.h"
+#include "intel_dsb_regs.h"
struct i915_vma;
diff --git a/drivers/gpu/drm/i915/display/intel_dsb_regs.h b/drivers/gpu/drm/i915/display/intel_dsb_regs.h
new file mode 100644
index 000000000000..12535d478775
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dsb_regs.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_DSB_REGS_H__
+#define __INTEL_DSB_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+/* This register controls the Display State Buffer (DSB) engines. */
+#define _DSBSL_INSTANCE_BASE 0x70B00
+#define DSBSL_INSTANCE(pipe, id) (_DSBSL_INSTANCE_BASE + \
+ (pipe) * 0x1000 + (id) * 0x100)
+#define DSB_HEAD(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x0)
+#define DSB_TAIL(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x4)
+#define DSB_CTRL(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x8)
+#define DSB_ENABLE REG_BIT(31)
+#define DSB_BUF_REITERATE REG_BIT(29)
+#define DSB_WAIT_FOR_VBLANK REG_BIT(28)
+#define DSB_WAIT_FOR_LINE_IN REG_BIT(27)
+#define DSB_HALT REG_BIT(16)
+#define DSB_NON_POSTED REG_BIT(8)
+#define DSB_STATUS_BUSY REG_BIT(0)
+#define DSB_MMIOCTRL(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0xc)
+#define DSB_MMIO_DEAD_CLOCKS_ENABLE REG_BIT(31)
+#define DSB_MMIO_DEAD_CLOCKS_COUNT_MASK REG_GENMASK(15, 8)
+#define DSB_MMIO_DEAD_CLOCKS_COUNT(x) REG_FIELD_PREP(DSB_MMIO_DEAD_CLOCK_COUNT_MASK, (x))
+#define DSB_MMIO_CYCLES_MASK REG_GENMASK(7, 0)
+#define DSB_MMIO_CYCLES(x) REG_FIELD_PREP(DSB_MMIO_CYCLES_MASK, (x))
+#define DSB_POLLFUNC(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x10)
+#define DSB_POLL_ENABLE REG_BIT(31)
+#define DSB_POLL_WAIT_MASK REG_GENMASK(30, 23)
+#define DSB_POLL_WAIT(x) REG_FIELD_PREP(DSB_POLL_WAIT_MASK, (x)) /* usec */
+#define DSB_POLL_COUNT_MASK REG_GENMASK(22, 15)
+#define DSB_POLL_COUNT(x) REG_FIELD_PREP(DSB_POLL_COUNT_MASK, (x))
+#define DSB_DEBUG(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x14)
+#define DSB_POLLMASK(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x1c)
+#define DSB_STATUS(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x24)
+#define DSB_INTERRUPT(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x28)
+#define DSB_ATS_FAULT_INT_EN REG_BIT(20)
+#define DSB_GTT_FAULT_INT_EN REG_BIT(19)
+#define DSB_RSPTIMEOUT_INT_EN REG_BIT(18)
+#define DSB_POLL_ERR_INT_EN REG_BIT(17)
+#define DSB_PROG_INT_EN REG_BIT(16)
+#define DSB_ATS_FAULT_INT_STATUS REG_BIT(4)
+#define DSB_GTT_FAULT_INT_STATUS REG_BIT(3)
+#define DSB_RSPTIMEOUT_INT_STATUS REG_BIT(2)
+#define DSB_POLL_ERR_INT_STATUS REG_BIT(1)
+#define DSB_PROG_INT_STATUS REG_BIT(0)
+#define DSB_CURRENT_HEAD(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x2c)
+#define DSB_RM_TIMEOUT(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x30)
+#define DSB_RM_CLAIM_TIMEOUT REG_BIT(31)
+#define DSB_RM_READY_TIMEOUT REG_BIT(30)
+#define DSB_RM_CLAIM_TIMEOUT_COUNT_MASK REG_GENMASK(23, 16)
+#define DSB_RM_CLAIM_TIMEOUT_COUNT(x) REG_FIELD_PREP(DSB_RM_CLAIM_TIMEOUT_COUNT_MASK, (x)) /* clocks */
+#define DSB_RM_READY_TIMEOUT_VALUE_MASK REG_GENMASK(15, 0)
+#define DSB_RM_READY_TIMEOUT_VALUE(x) REG_FIELD_PREP(DSB_RM_READY_TIMEOUT_VALUE, (x)) /* usec */
+#define DSB_RMTIMEOUTREG_CAPTURE(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x34)
+#define DSB_PMCTRL(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x38)
+#define DSB_PMCTRL_2(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x3c)
+#define DSB_PF_LN_LOWER(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x40)
+#define DSB_PF_LN_UPPER(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x44)
+#define DSB_BUFRPT_CNT(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x48)
+#define DSB_CHICKEN(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0xf0)
+
+#endif /* __INTEL_DSB_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
index 2cbc1292ab38..c7935ea498c4 100644
--- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
+++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
@@ -46,6 +46,7 @@
#include "intel_dsi.h"
#include "intel_dsi_vbt.h"
#include "intel_gmbus_regs.h"
+#include "intel_pps_regs.h"
#include "vlv_dsi.h"
#include "vlv_dsi_regs.h"
#include "vlv_sideband.h"
@@ -762,17 +763,6 @@ void intel_dsi_vbt_exec_sequence(struct intel_dsi *intel_dsi,
gpiod_set_value_cansleep(intel_dsi->gpio_backlight, 0);
}
-void intel_dsi_msleep(struct intel_dsi *intel_dsi, int msec)
-{
- struct intel_connector *connector = intel_dsi->attached_connector;
-
- /* For v3 VBTs in vid-mode the delays are part of the VBT sequences */
- if (is_vid_mode(intel_dsi) && connector->panel.vbt.dsi.seq_version >= 3)
- return;
-
- msleep(msec);
-}
-
void intel_dsi_log_params(struct intel_dsi *intel_dsi)
{
struct drm_i915_private *i915 = to_i915(intel_dsi->base.base.dev);
diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.h b/drivers/gpu/drm/i915/display/intel_dsi_vbt.h
index dc642c1fe7ef..468d873fab1a 100644
--- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.h
+++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.h
@@ -16,7 +16,6 @@ void intel_dsi_vbt_gpio_init(struct intel_dsi *intel_dsi, bool panel_is_on);
void intel_dsi_vbt_gpio_cleanup(struct intel_dsi *intel_dsi);
void intel_dsi_vbt_exec_sequence(struct intel_dsi *intel_dsi,
enum mipi_seq seq_id);
-void intel_dsi_msleep(struct intel_dsi *intel_dsi, int msec);
void intel_dsi_log_params(struct intel_dsi *intel_dsi);
#endif /* __INTEL_DSI_VBT_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c
index 799bdc81a6a9..e5f637897b5e 100644
--- a/drivers/gpu/drm/i915/display/intel_fb.c
+++ b/drivers/gpu/drm/i915/display/intel_fb.c
@@ -716,14 +716,15 @@ static unsigned int intel_fb_modifier_to_tiling(u64 fb_modifier)
}
}
-static bool intel_modifier_uses_dpt(struct drm_i915_private *i915, u64 modifier)
+bool intel_fb_modifier_uses_dpt(struct drm_i915_private *i915, u64 modifier)
{
- return DISPLAY_VER(i915) >= 13 && modifier != DRM_FORMAT_MOD_LINEAR;
+ return HAS_DPT(i915) && modifier != DRM_FORMAT_MOD_LINEAR;
}
bool intel_fb_uses_dpt(const struct drm_framebuffer *fb)
{
- return fb && intel_modifier_uses_dpt(to_i915(fb->dev), fb->modifier);
+ return fb && to_i915(fb->dev)->params.enable_dpt &&
+ intel_fb_modifier_uses_dpt(to_i915(fb->dev), fb->modifier);
}
unsigned int intel_cursor_alignment(const struct drm_i915_private *i915)
@@ -1189,7 +1190,7 @@ bool intel_fb_needs_pot_stride_remap(const struct intel_framebuffer *fb)
{
struct drm_i915_private *i915 = to_i915(fb->base.dev);
- return IS_ALDERLAKE_P(i915) && fb->base.modifier != DRM_FORMAT_MOD_LINEAR;
+ return IS_ALDERLAKE_P(i915) && intel_fb_uses_dpt(&fb->base);
}
static int intel_fb_pitch(const struct intel_framebuffer *fb, int color_plane, unsigned int rotation)
@@ -1705,7 +1706,7 @@ u32 intel_fb_max_stride(struct drm_i915_private *dev_priv,
* The new CCS hash mode makes remapping impossible
*/
if (DISPLAY_VER(dev_priv) < 4 || intel_fb_is_ccs_modifier(modifier) ||
- intel_modifier_uses_dpt(dev_priv, modifier))
+ intel_fb_modifier_uses_dpt(dev_priv, modifier))
return intel_plane_fb_max_stride(dev_priv, pixel_format, modifier);
else if (DISPLAY_VER(dev_priv) >= 7)
return 256 * 1024;
diff --git a/drivers/gpu/drm/i915/display/intel_fb.h b/drivers/gpu/drm/i915/display/intel_fb.h
index 4662b812b934..e85167d6bc34 100644
--- a/drivers/gpu/drm/i915/display/intel_fb.h
+++ b/drivers/gpu/drm/i915/display/intel_fb.h
@@ -92,6 +92,7 @@ intel_user_framebuffer_create(struct drm_device *dev,
struct drm_file *filp,
const struct drm_mode_fb_cmd2 *user_mode_cmd);
+bool intel_fb_modifier_uses_dpt(struct drm_i915_private *i915, u64 modifier);
bool intel_fb_uses_dpt(const struct drm_framebuffer *fb);
#endif /* __INTEL_FB_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
index 3659350061a7..673bcdfb7ff6 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
@@ -210,6 +210,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
bool prealloc = false;
void __iomem *vaddr;
struct drm_i915_gem_object *obj;
+ struct i915_gem_ww_ctx ww;
int ret;
mutex_lock(&ifbdev->hpd_lock);
@@ -283,13 +284,24 @@ static int intelfb_create(struct drm_fb_helper *helper,
info->fix.smem_len = vma->size;
}
- vaddr = i915_vma_pin_iomap(vma);
- if (IS_ERR(vaddr)) {
- drm_err(&dev_priv->drm,
- "Failed to remap framebuffer into virtual memory (%pe)\n", vaddr);
- ret = PTR_ERR(vaddr);
- goto out_unpin;
+ for_i915_gem_ww(&ww, ret, false) {
+ ret = i915_gem_object_lock(vma->obj, &ww);
+
+ if (ret)
+ continue;
+
+ vaddr = i915_vma_pin_iomap(vma);
+ if (IS_ERR(vaddr)) {
+ drm_err(&dev_priv->drm,
+ "Failed to remap framebuffer into virtual memory (%pe)\n", vaddr);
+ ret = PTR_ERR(vaddr);
+ continue;
+ }
}
+
+ if (ret)
+ goto out_unpin;
+
info->screen_base = vaddr;
info->screen_size = vma->size;
diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c
index f55b4893c00f..55283677c45a 100644
--- a/drivers/gpu/drm/i915/display/intel_fdi.c
+++ b/drivers/gpu/drm/i915/display/intel_fdi.c
@@ -12,6 +12,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_fdi.h"
+#include "intel_fdi_regs.h"
struct intel_fdi_funcs {
void (*fdi_link_train)(struct intel_crtc *crtc,
@@ -845,9 +846,7 @@ void hsw_fdi_link_train(struct intel_encoder *encoder,
intel_de_posting_read(dev_priv, DDI_BUF_CTL(PORT_E));
/* Disable DP_TP_CTL and FDI_RX_CTL and retry */
- intel_de_rmw(dev_priv, DP_TP_CTL(PORT_E),
- DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK,
- DP_TP_CTL_LINK_TRAIN_PAT1);
+ intel_de_rmw(dev_priv, DP_TP_CTL(PORT_E), DP_TP_CTL_ENABLE, 0);
intel_de_posting_read(dev_priv, DP_TP_CTL(PORT_E));
intel_wait_ddi_buf_idle(dev_priv, PORT_E);
diff --git a/drivers/gpu/drm/i915/display/intel_fdi_regs.h b/drivers/gpu/drm/i915/display/intel_fdi_regs.h
new file mode 100644
index 000000000000..853b834c35a9
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_fdi_regs.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_FDI_REGS_H__
+#define __INTEL_FDI_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+#define FDI_PLL_BIOS_0 _MMIO(0x46000)
+#define FDI_PLL_FB_CLOCK_MASK 0xff
+#define FDI_PLL_BIOS_1 _MMIO(0x46004)
+#define FDI_PLL_BIOS_2 _MMIO(0x46008)
+#define DISPLAY_PORT_PLL_BIOS_0 _MMIO(0x4600c)
+#define DISPLAY_PORT_PLL_BIOS_1 _MMIO(0x46010)
+#define DISPLAY_PORT_PLL_BIOS_2 _MMIO(0x46014)
+
+#define FDI_PLL_FREQ_CTL _MMIO(0x46030)
+#define FDI_PLL_FREQ_CHANGE_REQUEST (1 << 24)
+#define FDI_PLL_FREQ_LOCK_LIMIT_MASK 0xfff00
+#define FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK 0xff
+
+#define _FDI_RXA_CHICKEN 0xc200c
+#define _FDI_RXB_CHICKEN 0xc2010
+#define FDI_RX_PHASE_SYNC_POINTER_OVR (1 << 1)
+#define FDI_RX_PHASE_SYNC_POINTER_EN (1 << 0)
+#define FDI_RX_CHICKEN(pipe) _MMIO_PIPE(pipe, _FDI_RXA_CHICKEN, _FDI_RXB_CHICKEN)
+
+/* CPU: FDI_TX */
+#define _FDI_TXA_CTL 0x60100
+#define _FDI_TXB_CTL 0x61100
+#define FDI_TX_CTL(pipe) _MMIO_PIPE(pipe, _FDI_TXA_CTL, _FDI_TXB_CTL)
+#define FDI_TX_DISABLE (0 << 31)
+#define FDI_TX_ENABLE (1 << 31)
+#define FDI_LINK_TRAIN_PATTERN_1 (0 << 28)
+#define FDI_LINK_TRAIN_PATTERN_2 (1 << 28)
+#define FDI_LINK_TRAIN_PATTERN_IDLE (2 << 28)
+#define FDI_LINK_TRAIN_NONE (3 << 28)
+#define FDI_LINK_TRAIN_VOLTAGE_0_4V (0 << 25)
+#define FDI_LINK_TRAIN_VOLTAGE_0_6V (1 << 25)
+#define FDI_LINK_TRAIN_VOLTAGE_0_8V (2 << 25)
+#define FDI_LINK_TRAIN_VOLTAGE_1_2V (3 << 25)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_NONE (0 << 22)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_1_5X (1 << 22)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_2X (2 << 22)
+#define FDI_LINK_TRAIN_PRE_EMPHASIS_3X (3 << 22)
+/* ILK always use 400mV 0dB for voltage swing and pre-emphasis level.
+ SNB has different settings. */
+/* SNB A-stepping */
+#define FDI_LINK_TRAIN_400MV_0DB_SNB_A (0x38 << 22)
+#define FDI_LINK_TRAIN_400MV_6DB_SNB_A (0x02 << 22)
+#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01 << 22)
+#define FDI_LINK_TRAIN_800MV_0DB_SNB_A (0x0 << 22)
+/* SNB B-stepping */
+#define FDI_LINK_TRAIN_400MV_0DB_SNB_B (0x0 << 22)
+#define FDI_LINK_TRAIN_400MV_6DB_SNB_B (0x3a << 22)
+#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39 << 22)
+#define FDI_LINK_TRAIN_800MV_0DB_SNB_B (0x38 << 22)
+#define FDI_LINK_TRAIN_VOL_EMP_MASK (0x3f << 22)
+#define FDI_DP_PORT_WIDTH_SHIFT 19
+#define FDI_DP_PORT_WIDTH_MASK (7 << FDI_DP_PORT_WIDTH_SHIFT)
+#define FDI_DP_PORT_WIDTH(width) (((width) - 1) << FDI_DP_PORT_WIDTH_SHIFT)
+#define FDI_TX_ENHANCE_FRAME_ENABLE (1 << 18)
+/* Ironlake: hardwired to 1 */
+#define FDI_TX_PLL_ENABLE (1 << 14)
+
+/* Ivybridge has different bits for lolz */
+#define FDI_LINK_TRAIN_PATTERN_1_IVB (0 << 8)
+#define FDI_LINK_TRAIN_PATTERN_2_IVB (1 << 8)
+#define FDI_LINK_TRAIN_PATTERN_IDLE_IVB (2 << 8)
+#define FDI_LINK_TRAIN_NONE_IVB (3 << 8)
+
+/* both Tx and Rx */
+#define FDI_COMPOSITE_SYNC (1 << 11)
+#define FDI_LINK_TRAIN_AUTO (1 << 10)
+#define FDI_SCRAMBLING_ENABLE (0 << 7)
+#define FDI_SCRAMBLING_DISABLE (1 << 7)
+
+/* FDI_RX, FDI_X is hard-wired to Transcoder_X */
+#define _FDI_RXA_CTL 0xf000c
+#define _FDI_RXB_CTL 0xf100c
+#define FDI_RX_CTL(pipe) _MMIO_PIPE(pipe, _FDI_RXA_CTL, _FDI_RXB_CTL)
+#define FDI_RX_ENABLE (1 << 31)
+/* train, dp width same as FDI_TX */
+#define FDI_FS_ERRC_ENABLE (1 << 27)
+#define FDI_FE_ERRC_ENABLE (1 << 26)
+#define FDI_RX_POLARITY_REVERSED_LPT (1 << 16)
+#define FDI_8BPC (0 << 16)
+#define FDI_10BPC (1 << 16)
+#define FDI_6BPC (2 << 16)
+#define FDI_12BPC (3 << 16)
+#define FDI_RX_LINK_REVERSAL_OVERRIDE (1 << 15)
+#define FDI_DMI_LINK_REVERSE_MASK (1 << 14)
+#define FDI_RX_PLL_ENABLE (1 << 13)
+#define FDI_FS_ERR_CORRECT_ENABLE (1 << 11)
+#define FDI_FE_ERR_CORRECT_ENABLE (1 << 10)
+#define FDI_FS_ERR_REPORT_ENABLE (1 << 9)
+#define FDI_FE_ERR_REPORT_ENABLE (1 << 8)
+#define FDI_RX_ENHANCE_FRAME_ENABLE (1 << 6)
+#define FDI_PCDCLK (1 << 4)
+/* CPT */
+#define FDI_AUTO_TRAINING (1 << 10)
+#define FDI_LINK_TRAIN_PATTERN_1_CPT (0 << 8)
+#define FDI_LINK_TRAIN_PATTERN_2_CPT (1 << 8)
+#define FDI_LINK_TRAIN_PATTERN_IDLE_CPT (2 << 8)
+#define FDI_LINK_TRAIN_NORMAL_CPT (3 << 8)
+#define FDI_LINK_TRAIN_PATTERN_MASK_CPT (3 << 8)
+
+#define _FDI_RXA_MISC 0xf0010
+#define _FDI_RXB_MISC 0xf1010
+#define FDI_RX_PWRDN_LANE1_MASK (3 << 26)
+#define FDI_RX_PWRDN_LANE1_VAL(x) ((x) << 26)
+#define FDI_RX_PWRDN_LANE0_MASK (3 << 24)
+#define FDI_RX_PWRDN_LANE0_VAL(x) ((x) << 24)
+#define FDI_RX_TP1_TO_TP2_48 (2 << 20)
+#define FDI_RX_TP1_TO_TP2_64 (3 << 20)
+#define FDI_RX_FDI_DELAY_90 (0x90 << 0)
+#define FDI_RX_MISC(pipe) _MMIO_PIPE(pipe, _FDI_RXA_MISC, _FDI_RXB_MISC)
+
+#define _FDI_RXA_TUSIZE1 0xf0030
+#define _FDI_RXA_TUSIZE2 0xf0038
+#define _FDI_RXB_TUSIZE1 0xf1030
+#define _FDI_RXB_TUSIZE2 0xf1038
+#define FDI_RX_TUSIZE1(pipe) _MMIO_PIPE(pipe, _FDI_RXA_TUSIZE1, _FDI_RXB_TUSIZE1)
+#define FDI_RX_TUSIZE2(pipe) _MMIO_PIPE(pipe, _FDI_RXA_TUSIZE2, _FDI_RXB_TUSIZE2)
+
+/* FDI_RX interrupt register format */
+#define FDI_RX_INTER_LANE_ALIGN (1 << 10)
+#define FDI_RX_SYMBOL_LOCK (1 << 9) /* train 2 */
+#define FDI_RX_BIT_LOCK (1 << 8) /* train 1 */
+#define FDI_RX_TRAIN_PATTERN_2_FAIL (1 << 7)
+#define FDI_RX_FS_CODE_ERR (1 << 6)
+#define FDI_RX_FE_CODE_ERR (1 << 5)
+#define FDI_RX_SYMBOL_ERR_RATE_ABOVE (1 << 4)
+#define FDI_RX_HDCP_LINK_FAIL (1 << 3)
+#define FDI_RX_PIXEL_FIFO_OVERFLOW (1 << 2)
+#define FDI_RX_CROSS_CLOCK_OVERFLOW (1 << 1)
+#define FDI_RX_SYMBOL_QUEUE_OVERFLOW (1 << 0)
+
+#define _FDI_RXA_IIR 0xf0014
+#define _FDI_RXA_IMR 0xf0018
+#define _FDI_RXB_IIR 0xf1014
+#define _FDI_RXB_IMR 0xf1018
+#define FDI_RX_IIR(pipe) _MMIO_PIPE(pipe, _FDI_RXA_IIR, _FDI_RXB_IIR)
+#define FDI_RX_IMR(pipe) _MMIO_PIPE(pipe, _FDI_RXA_IMR, _FDI_RXB_IMR)
+
+#define FDI_PLL_CTL_1 _MMIO(0xfe000)
+#define FDI_PLL_CTL_2 _MMIO(0xfe004)
+
+#endif /* __INTEL_FDI_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c
index 2984d2810e42..b183efab04a1 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.c
@@ -23,6 +23,7 @@
#include "intel_display_power_well.h"
#include "intel_display_types.h"
#include "intel_hdcp.h"
+#include "intel_hdcp_gsc.h"
#include "intel_hdcp_regs.h"
#include "intel_pcode.h"
@@ -209,7 +210,16 @@ bool intel_hdcp2_capable(struct intel_connector *connector)
if (!hdcp->hdcp2_supported)
return false;
- /* MEI interface is solid */
+ /* If MTL+ make sure gsc is loaded and proxy is setup */
+ if (intel_hdcp_gsc_cs_required(dev_priv)) {
+ struct intel_gt *gt = dev_priv->media_gt;
+ struct intel_gsc_uc *gsc = gt ? &gt->uc.gsc : NULL;
+
+ if (!gsc || !intel_uc_fw_is_running(&gsc->fw))
+ return false;
+ }
+
+ /* MEI/GSC interface is solid depending on which is used */
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
if (!dev_priv->display.hdcp.comp_added || !dev_priv->display.hdcp.master) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
@@ -1142,18 +1152,18 @@ hdcp2_prepare_ake_init(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->initiate_hdcp2_session(comp->mei_dev, data, ake_data);
+ ret = arbiter->ops->initiate_hdcp2_session(arbiter->hdcp_dev, data, ake_data);
if (ret)
drm_dbg_kms(&dev_priv->drm, "Prepare_ake_init failed. %d\n",
ret);
@@ -1172,18 +1182,18 @@ hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->verify_receiver_cert_prepare_km(comp->mei_dev, data,
+ ret = arbiter->ops->verify_receiver_cert_prepare_km(arbiter->hdcp_dev, data,
rx_cert, paired,
ek_pub_km, msg_sz);
if (ret < 0)
@@ -1200,18 +1210,18 @@ static int hdcp2_verify_hprime(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->verify_hprime(comp->mei_dev, data, rx_hprime);
+ ret = arbiter->ops->verify_hprime(arbiter->hdcp_dev, data, rx_hprime);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm, "Verify hprime failed. %d\n", ret);
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
@@ -1226,18 +1236,18 @@ hdcp2_store_pairing_info(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->store_pairing_info(comp->mei_dev, data, pairing_info);
+ ret = arbiter->ops->store_pairing_info(arbiter->hdcp_dev, data, pairing_info);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm, "Store pairing info failed. %d\n",
ret);
@@ -1253,18 +1263,18 @@ hdcp2_prepare_lc_init(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->initiate_locality_check(comp->mei_dev, data, lc_init);
+ ret = arbiter->ops->initiate_locality_check(arbiter->hdcp_dev, data, lc_init);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm, "Prepare lc_init failed. %d\n",
ret);
@@ -1280,18 +1290,18 @@ hdcp2_verify_lprime(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->verify_lprime(comp->mei_dev, data, rx_lprime);
+ ret = arbiter->ops->verify_lprime(arbiter->hdcp_dev, data, rx_lprime);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm, "Verify L_Prime failed. %d\n",
ret);
@@ -1306,18 +1316,18 @@ static int hdcp2_prepare_skey(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->get_session_key(comp->mei_dev, data, ske_data);
+ ret = arbiter->ops->get_session_key(arbiter->hdcp_dev, data, ske_data);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm, "Get session key failed. %d\n",
ret);
@@ -1335,20 +1345,21 @@ hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->repeater_check_flow_prepare_ack(comp->mei_dev, data,
- rep_topology,
- rep_send_ack);
+ ret = arbiter->ops->repeater_check_flow_prepare_ack(arbiter->hdcp_dev,
+ data,
+ rep_topology,
+ rep_send_ack);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm,
"Verify rep topology failed. %d\n", ret);
@@ -1364,18 +1375,18 @@ hdcp2_verify_mprime(struct intel_connector *connector,
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->verify_mprime(comp->mei_dev, data, stream_ready);
+ ret = arbiter->ops->verify_mprime(arbiter->hdcp_dev, data, stream_ready);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm, "Verify mprime failed. %d\n", ret);
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
@@ -1388,18 +1399,18 @@ static int hdcp2_authenticate_port(struct intel_connector *connector)
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct hdcp_port_data *data = &dig_port->hdcp_port_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->enable_hdcp_authentication(comp->mei_dev, data);
+ ret = arbiter->ops->enable_hdcp_authentication(arbiter->hdcp_dev, data);
if (ret < 0)
drm_dbg_kms(&dev_priv->drm, "Enable hdcp auth failed. %d\n",
ret);
@@ -1408,22 +1419,22 @@ static int hdcp2_authenticate_port(struct intel_connector *connector)
return ret;
}
-static int hdcp2_close_mei_session(struct intel_connector *connector)
+static int hdcp2_close_session(struct intel_connector *connector)
{
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct i915_hdcp_comp_master *comp;
+ struct i915_hdcp_master *arbiter;
int ret;
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- comp = dev_priv->display.hdcp.master;
+ arbiter = dev_priv->display.hdcp.master;
- if (!comp || !comp->ops) {
+ if (!arbiter || !arbiter->ops) {
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return -EINVAL;
}
- ret = comp->ops->close_hdcp_session(comp->mei_dev,
+ ret = arbiter->ops->close_hdcp_session(arbiter->hdcp_dev,
&dig_port->hdcp_port_data);
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
@@ -1432,7 +1443,7 @@ static int hdcp2_close_mei_session(struct intel_connector *connector)
static int hdcp2_deauthenticate_port(struct intel_connector *connector)
{
- return hdcp2_close_mei_session(connector);
+ return hdcp2_close_session(connector);
}
/* Authentication flow starts from here */
@@ -2142,8 +2153,8 @@ static int i915_hdcp_component_bind(struct device *i915_kdev,
drm_dbg(&dev_priv->drm, "I915 HDCP comp bind\n");
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
- dev_priv->display.hdcp.master = (struct i915_hdcp_comp_master *)data;
- dev_priv->display.hdcp.master->mei_dev = mei_kdev;
+ dev_priv->display.hdcp.master = (struct i915_hdcp_master *)data;
+ dev_priv->display.hdcp.master->hdcp_dev = mei_kdev;
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
return 0;
@@ -2160,30 +2171,30 @@ static void i915_hdcp_component_unbind(struct device *i915_kdev,
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
}
-static const struct component_ops i915_hdcp_component_ops = {
+static const struct component_ops i915_hdcp_ops = {
.bind = i915_hdcp_component_bind,
.unbind = i915_hdcp_component_unbind,
};
-static enum mei_fw_ddi intel_get_mei_fw_ddi_index(enum port port)
+static enum hdcp_ddi intel_get_hdcp_ddi_index(enum port port)
{
switch (port) {
case PORT_A:
- return MEI_DDI_A;
+ return HDCP_DDI_A;
case PORT_B ... PORT_F:
- return (enum mei_fw_ddi)port;
+ return (enum hdcp_ddi)port;
default:
- return MEI_DDI_INVALID_PORT;
+ return HDCP_DDI_INVALID_PORT;
}
}
-static enum mei_fw_tc intel_get_mei_fw_tc(enum transcoder cpu_transcoder)
+static enum hdcp_transcoder intel_get_hdcp_transcoder(enum transcoder cpu_transcoder)
{
switch (cpu_transcoder) {
case TRANSCODER_A ... TRANSCODER_D:
- return (enum mei_fw_tc)(cpu_transcoder | 0x10);
+ return (enum hdcp_transcoder)(cpu_transcoder | 0x10);
default: /* eDP, DSI TRANSCODERS are non HDCP capable */
- return MEI_INVALID_TRANSCODER;
+ return HDCP_INVALID_TRANSCODER;
}
}
@@ -2197,20 +2208,20 @@ static int initialize_hdcp_port_data(struct intel_connector *connector,
enum port port = dig_port->base.port;
if (DISPLAY_VER(dev_priv) < 12)
- data->fw_ddi = intel_get_mei_fw_ddi_index(port);
+ data->hdcp_ddi = intel_get_hdcp_ddi_index(port);
else
/*
- * As per ME FW API expectation, for GEN 12+, fw_ddi is filled
+ * As per ME FW API expectation, for GEN 12+, hdcp_ddi is filled
* with zero(INVALID PORT index).
*/
- data->fw_ddi = MEI_DDI_INVALID_PORT;
+ data->hdcp_ddi = HDCP_DDI_INVALID_PORT;
/*
- * As associated transcoder is set and modified at modeset, here fw_tc
+ * As associated transcoder is set and modified at modeset, here hdcp_transcoder
* is initialized to zero (invalid transcoder index). This will be
* retained for <Gen12 forever.
*/
- data->fw_tc = MEI_INVALID_TRANSCODER;
+ data->hdcp_transcoder = HDCP_INVALID_TRANSCODER;
data->port_type = (u8)HDCP_PORT_TYPE_INTEGRATED;
data->protocol = (u8)shim->protocol;
@@ -2232,6 +2243,9 @@ static int initialize_hdcp_port_data(struct intel_connector *connector,
static bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
+ if (intel_hdcp_gsc_cs_required(dev_priv))
+ return true;
+
if (!IS_ENABLED(CONFIG_INTEL_MEI_HDCP))
return false;
@@ -2253,10 +2267,14 @@ void intel_hdcp_component_init(struct drm_i915_private *dev_priv)
dev_priv->display.hdcp.comp_added = true;
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
- ret = component_add_typed(dev_priv->drm.dev, &i915_hdcp_component_ops,
- I915_COMPONENT_HDCP);
+ if (intel_hdcp_gsc_cs_required(dev_priv))
+ ret = intel_hdcp_gsc_init(dev_priv);
+ else
+ ret = component_add_typed(dev_priv->drm.dev, &i915_hdcp_ops,
+ I915_COMPONENT_HDCP);
+
if (ret < 0) {
- drm_dbg_kms(&dev_priv->drm, "Failed at component add(%d)\n",
+ drm_dbg_kms(&dev_priv->drm, "Failed at fw component add(%d)\n",
ret);
mutex_lock(&dev_priv->display.hdcp.comp_mutex);
dev_priv->display.hdcp.comp_added = false;
@@ -2347,7 +2365,8 @@ int intel_hdcp_enable(struct intel_connector *connector,
}
if (DISPLAY_VER(dev_priv) >= 12)
- dig_port->hdcp_port_data.fw_tc = intel_get_mei_fw_tc(hdcp->cpu_transcoder);
+ dig_port->hdcp_port_data.hdcp_transcoder =
+ intel_get_hdcp_transcoder(hdcp->cpu_transcoder);
/*
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
@@ -2482,7 +2501,10 @@ void intel_hdcp_component_fini(struct drm_i915_private *dev_priv)
dev_priv->display.hdcp.comp_added = false;
mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
- component_del(dev_priv->drm.dev, &i915_hdcp_component_ops);
+ if (intel_hdcp_gsc_cs_required(dev_priv))
+ intel_hdcp_gsc_fini(dev_priv);
+ else
+ component_del(dev_priv->drm.dev, &i915_hdcp_ops);
}
void intel_hdcp_cleanup(struct intel_connector *connector)
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp_gsc.c b/drivers/gpu/drm/i915/display/intel_hdcp_gsc.c
new file mode 100644
index 000000000000..7e52aea6aa17
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_hdcp_gsc.c
@@ -0,0 +1,831 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2023, Intel Corporation.
+ */
+
+#include <drm/i915_hdcp_interface.h>
+
+#include "display/intel_hdcp_gsc.h"
+#include "gem/i915_gem_region.h"
+#include "gt/uc/intel_gsc_uc_heci_cmd_submit.h"
+#include "i915_drv.h"
+#include "i915_utils.h"
+
+bool intel_hdcp_gsc_cs_required(struct drm_i915_private *i915)
+{
+ return DISPLAY_VER(i915) >= 14;
+}
+
+static int
+gsc_hdcp_initiate_session(struct device *dev, struct hdcp_port_data *data,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct wired_cmd_initiate_hdcp2_session_in session_init_in = { { 0 } };
+ struct wired_cmd_initiate_hdcp2_session_out
+ session_init_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data || !ake_data)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ session_init_in.header.api_version = HDCP_API_VERSION;
+ session_init_in.header.command_id = WIRED_INITIATE_HDCP2_SESSION;
+ session_init_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ session_init_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_INITIATE_HDCP2_SESSION_IN;
+
+ session_init_in.port.integrated_port_type = data->port_type;
+ session_init_in.port.physical_port = (u8)data->hdcp_ddi;
+ session_init_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+ session_init_in.protocol = data->protocol;
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&session_init_in,
+ sizeof(session_init_in),
+ (u8 *)&session_init_out,
+ sizeof(session_init_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (session_init_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X Failed. Status: 0x%X\n",
+ WIRED_INITIATE_HDCP2_SESSION,
+ session_init_out.header.status);
+ return -EIO;
+ }
+
+ ake_data->msg_id = HDCP_2_2_AKE_INIT;
+ ake_data->tx_caps = session_init_out.tx_caps;
+ memcpy(ake_data->r_tx, session_init_out.r_tx, HDCP_2_2_RTX_LEN);
+
+ return 0;
+}
+
+static int
+gsc_hdcp_verify_receiver_cert_prepare_km(struct device *dev,
+ struct hdcp_port_data *data,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct wired_cmd_verify_receiver_cert_in verify_rxcert_in = { { 0 } };
+ struct wired_cmd_verify_receiver_cert_out verify_rxcert_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data || !rx_cert || !km_stored || !ek_pub_km || !msg_sz)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ verify_rxcert_in.header.api_version = HDCP_API_VERSION;
+ verify_rxcert_in.header.command_id = WIRED_VERIFY_RECEIVER_CERT;
+ verify_rxcert_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ verify_rxcert_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_IN;
+
+ verify_rxcert_in.port.integrated_port_type = data->port_type;
+ verify_rxcert_in.port.physical_port = (u8)data->hdcp_ddi;
+ verify_rxcert_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ verify_rxcert_in.cert_rx = rx_cert->cert_rx;
+ memcpy(verify_rxcert_in.r_rx, &rx_cert->r_rx, HDCP_2_2_RRX_LEN);
+ memcpy(verify_rxcert_in.rx_caps, rx_cert->rx_caps, HDCP_2_2_RXCAPS_LEN);
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&verify_rxcert_in,
+ sizeof(verify_rxcert_in),
+ (u8 *)&verify_rxcert_out,
+ sizeof(verify_rxcert_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed: %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_rxcert_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X Failed. Status: 0x%X\n",
+ WIRED_VERIFY_RECEIVER_CERT,
+ verify_rxcert_out.header.status);
+ return -EIO;
+ }
+
+ *km_stored = !!verify_rxcert_out.km_stored;
+ if (verify_rxcert_out.km_stored) {
+ ek_pub_km->msg_id = HDCP_2_2_AKE_STORED_KM;
+ *msg_sz = sizeof(struct hdcp2_ake_stored_km);
+ } else {
+ ek_pub_km->msg_id = HDCP_2_2_AKE_NO_STORED_KM;
+ *msg_sz = sizeof(struct hdcp2_ake_no_stored_km);
+ }
+
+ memcpy(ek_pub_km->e_kpub_km, &verify_rxcert_out.ekm_buff,
+ sizeof(verify_rxcert_out.ekm_buff));
+
+ return 0;
+}
+
+static int
+gsc_hdcp_verify_hprime(struct device *dev, struct hdcp_port_data *data,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct wired_cmd_ake_send_hprime_in send_hprime_in = { { 0 } };
+ struct wired_cmd_ake_send_hprime_out send_hprime_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data || !rx_hprime)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ send_hprime_in.header.api_version = HDCP_API_VERSION;
+ send_hprime_in.header.command_id = WIRED_AKE_SEND_HPRIME;
+ send_hprime_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ send_hprime_in.header.buffer_len = WIRED_CMD_BUF_LEN_AKE_SEND_HPRIME_IN;
+
+ send_hprime_in.port.integrated_port_type = data->port_type;
+ send_hprime_in.port.physical_port = (u8)data->hdcp_ddi;
+ send_hprime_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ memcpy(send_hprime_in.h_prime, rx_hprime->h_prime,
+ HDCP_2_2_H_PRIME_LEN);
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&send_hprime_in,
+ sizeof(send_hprime_in),
+ (u8 *)&send_hprime_out,
+ sizeof(send_hprime_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (send_hprime_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X Failed. Status: 0x%X\n",
+ WIRED_AKE_SEND_HPRIME, send_hprime_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+gsc_hdcp_store_pairing_info(struct device *dev, struct hdcp_port_data *data,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct wired_cmd_ake_send_pairing_info_in pairing_info_in = { { 0 } };
+ struct wired_cmd_ake_send_pairing_info_out pairing_info_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data || !pairing_info)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ pairing_info_in.header.api_version = HDCP_API_VERSION;
+ pairing_info_in.header.command_id = WIRED_AKE_SEND_PAIRING_INFO;
+ pairing_info_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ pairing_info_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_SEND_PAIRING_INFO_IN;
+
+ pairing_info_in.port.integrated_port_type = data->port_type;
+ pairing_info_in.port.physical_port = (u8)data->hdcp_ddi;
+ pairing_info_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ memcpy(pairing_info_in.e_kh_km, pairing_info->e_kh_km,
+ HDCP_2_2_E_KH_KM_LEN);
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&pairing_info_in,
+ sizeof(pairing_info_in),
+ (u8 *)&pairing_info_out,
+ sizeof(pairing_info_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (pairing_info_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X failed. Status: 0x%X\n",
+ WIRED_AKE_SEND_PAIRING_INFO,
+ pairing_info_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+gsc_hdcp_initiate_locality_check(struct device *dev,
+ struct hdcp_port_data *data,
+ struct hdcp2_lc_init *lc_init_data)
+{
+ struct wired_cmd_init_locality_check_in lc_init_in = { { 0 } };
+ struct wired_cmd_init_locality_check_out lc_init_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data || !lc_init_data)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ lc_init_in.header.api_version = HDCP_API_VERSION;
+ lc_init_in.header.command_id = WIRED_INIT_LOCALITY_CHECK;
+ lc_init_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ lc_init_in.header.buffer_len = WIRED_CMD_BUF_LEN_INIT_LOCALITY_CHECK_IN;
+
+ lc_init_in.port.integrated_port_type = data->port_type;
+ lc_init_in.port.physical_port = (u8)data->hdcp_ddi;
+ lc_init_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&lc_init_in, sizeof(lc_init_in),
+ (u8 *)&lc_init_out, sizeof(lc_init_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (lc_init_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X Failed. status: 0x%X\n",
+ WIRED_INIT_LOCALITY_CHECK, lc_init_out.header.status);
+ return -EIO;
+ }
+
+ lc_init_data->msg_id = HDCP_2_2_LC_INIT;
+ memcpy(lc_init_data->r_n, lc_init_out.r_n, HDCP_2_2_RN_LEN);
+
+ return 0;
+}
+
+static int
+gsc_hdcp_verify_lprime(struct device *dev, struct hdcp_port_data *data,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct wired_cmd_validate_locality_in verify_lprime_in = { { 0 } };
+ struct wired_cmd_validate_locality_out verify_lprime_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data || !rx_lprime)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ verify_lprime_in.header.api_version = HDCP_API_VERSION;
+ verify_lprime_in.header.command_id = WIRED_VALIDATE_LOCALITY;
+ verify_lprime_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ verify_lprime_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_VALIDATE_LOCALITY_IN;
+
+ verify_lprime_in.port.integrated_port_type = data->port_type;
+ verify_lprime_in.port.physical_port = (u8)data->hdcp_ddi;
+ verify_lprime_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ memcpy(verify_lprime_in.l_prime, rx_lprime->l_prime,
+ HDCP_2_2_L_PRIME_LEN);
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&verify_lprime_in,
+ sizeof(verify_lprime_in),
+ (u8 *)&verify_lprime_out,
+ sizeof(verify_lprime_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_lprime_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_VALIDATE_LOCALITY,
+ verify_lprime_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gsc_hdcp_get_session_key(struct device *dev,
+ struct hdcp_port_data *data,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct wired_cmd_get_session_key_in get_skey_in = { { 0 } };
+ struct wired_cmd_get_session_key_out get_skey_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data || !ske_data)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ get_skey_in.header.api_version = HDCP_API_VERSION;
+ get_skey_in.header.command_id = WIRED_GET_SESSION_KEY;
+ get_skey_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ get_skey_in.header.buffer_len = WIRED_CMD_BUF_LEN_GET_SESSION_KEY_IN;
+
+ get_skey_in.port.integrated_port_type = data->port_type;
+ get_skey_in.port.physical_port = (u8)data->hdcp_ddi;
+ get_skey_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&get_skey_in, sizeof(get_skey_in),
+ (u8 *)&get_skey_out, sizeof(get_skey_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (get_skey_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_GET_SESSION_KEY, get_skey_out.header.status);
+ return -EIO;
+ }
+
+ ske_data->msg_id = HDCP_2_2_SKE_SEND_EKS;
+ memcpy(ske_data->e_dkey_ks, get_skey_out.e_dkey_ks,
+ HDCP_2_2_E_DKEY_KS_LEN);
+ memcpy(ske_data->riv, get_skey_out.r_iv, HDCP_2_2_RIV_LEN);
+
+ return 0;
+}
+
+static int
+gsc_hdcp_repeater_check_flow_prepare_ack(struct device *dev,
+ struct hdcp_port_data *data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack)
+{
+ struct wired_cmd_verify_repeater_in verify_repeater_in = { { 0 } };
+ struct wired_cmd_verify_repeater_out verify_repeater_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !rep_topology || !rep_send_ack || !data)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ verify_repeater_in.header.api_version = HDCP_API_VERSION;
+ verify_repeater_in.header.command_id = WIRED_VERIFY_REPEATER;
+ verify_repeater_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ verify_repeater_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_VERIFY_REPEATER_IN;
+
+ verify_repeater_in.port.integrated_port_type = data->port_type;
+ verify_repeater_in.port.physical_port = (u8)data->hdcp_ddi;
+ verify_repeater_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ memcpy(verify_repeater_in.rx_info, rep_topology->rx_info,
+ HDCP_2_2_RXINFO_LEN);
+ memcpy(verify_repeater_in.seq_num_v, rep_topology->seq_num_v,
+ HDCP_2_2_SEQ_NUM_LEN);
+ memcpy(verify_repeater_in.v_prime, rep_topology->v_prime,
+ HDCP_2_2_V_PRIME_HALF_LEN);
+ memcpy(verify_repeater_in.receiver_ids, rep_topology->receiver_ids,
+ HDCP_2_2_RECEIVER_IDS_MAX_LEN);
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&verify_repeater_in,
+ sizeof(verify_repeater_in),
+ (u8 *)&verify_repeater_out,
+ sizeof(verify_repeater_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_repeater_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_VERIFY_REPEATER,
+ verify_repeater_out.header.status);
+ return -EIO;
+ }
+
+ memcpy(rep_send_ack->v, verify_repeater_out.v,
+ HDCP_2_2_V_PRIME_HALF_LEN);
+ rep_send_ack->msg_id = HDCP_2_2_REP_SEND_ACK;
+
+ return 0;
+}
+
+static int gsc_hdcp_verify_mprime(struct device *dev,
+ struct hdcp_port_data *data,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct wired_cmd_repeater_auth_stream_req_in *verify_mprime_in;
+ struct wired_cmd_repeater_auth_stream_req_out
+ verify_mprime_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+ size_t cmd_size;
+
+ if (!dev || !stream_ready || !data)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ cmd_size = struct_size(verify_mprime_in, streams, data->k);
+ if (cmd_size == SIZE_MAX)
+ return -EINVAL;
+
+ verify_mprime_in = kzalloc(cmd_size, GFP_KERNEL);
+ if (!verify_mprime_in)
+ return -ENOMEM;
+
+ verify_mprime_in->header.api_version = HDCP_API_VERSION;
+ verify_mprime_in->header.command_id = WIRED_REPEATER_AUTH_STREAM_REQ;
+ verify_mprime_in->header.status = FW_HDCP_STATUS_SUCCESS;
+ verify_mprime_in->header.buffer_len = cmd_size - sizeof(verify_mprime_in->header);
+
+ verify_mprime_in->port.integrated_port_type = data->port_type;
+ verify_mprime_in->port.physical_port = (u8)data->hdcp_ddi;
+ verify_mprime_in->port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ memcpy(verify_mprime_in->m_prime, stream_ready->m_prime, HDCP_2_2_MPRIME_LEN);
+ drm_hdcp_cpu_to_be24(verify_mprime_in->seq_num_m, data->seq_num_m);
+
+ memcpy(verify_mprime_in->streams, data->streams,
+ array_size(data->k, sizeof(*data->streams)));
+
+ verify_mprime_in->k = cpu_to_be16(data->k);
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)verify_mprime_in, cmd_size,
+ (u8 *)&verify_mprime_out,
+ sizeof(verify_mprime_out));
+ kfree(verify_mprime_in);
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_mprime_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_REPEATER_AUTH_STREAM_REQ,
+ verify_mprime_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gsc_hdcp_enable_authentication(struct device *dev,
+ struct hdcp_port_data *data)
+{
+ struct wired_cmd_enable_auth_in enable_auth_in = { { 0 } };
+ struct wired_cmd_enable_auth_out enable_auth_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ enable_auth_in.header.api_version = HDCP_API_VERSION;
+ enable_auth_in.header.command_id = WIRED_ENABLE_AUTH;
+ enable_auth_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ enable_auth_in.header.buffer_len = WIRED_CMD_BUF_LEN_ENABLE_AUTH_IN;
+
+ enable_auth_in.port.integrated_port_type = data->port_type;
+ enable_auth_in.port.physical_port = (u8)data->hdcp_ddi;
+ enable_auth_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+ enable_auth_in.stream_type = data->streams[0].stream_type;
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&enable_auth_in,
+ sizeof(enable_auth_in),
+ (u8 *)&enable_auth_out,
+ sizeof(enable_auth_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (enable_auth_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "FW cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_ENABLE_AUTH, enable_auth_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+gsc_hdcp_close_session(struct device *dev, struct hdcp_port_data *data)
+{
+ struct wired_cmd_close_session_in session_close_in = { { 0 } };
+ struct wired_cmd_close_session_out session_close_out = { { 0 } };
+ struct drm_i915_private *i915;
+ ssize_t byte;
+
+ if (!dev || !data)
+ return -EINVAL;
+
+ i915 = kdev_to_i915(dev);
+ if (!i915) {
+ dev_err(dev, "DRM not initialized, aborting HDCP.\n");
+ return -ENODEV;
+ }
+
+ session_close_in.header.api_version = HDCP_API_VERSION;
+ session_close_in.header.command_id = WIRED_CLOSE_SESSION;
+ session_close_in.header.status = FW_HDCP_STATUS_SUCCESS;
+ session_close_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_CLOSE_SESSION_IN;
+
+ session_close_in.port.integrated_port_type = data->port_type;
+ session_close_in.port.physical_port = (u8)data->hdcp_ddi;
+ session_close_in.port.attached_transcoder = (u8)data->hdcp_transcoder;
+
+ byte = intel_hdcp_gsc_msg_send(i915, (u8 *)&session_close_in,
+ sizeof(session_close_in),
+ (u8 *)&session_close_out,
+ sizeof(session_close_out));
+ if (byte < 0) {
+ drm_dbg_kms(&i915->drm, "intel_hdcp_gsc_msg_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (session_close_out.header.status != FW_HDCP_STATUS_SUCCESS) {
+ drm_dbg_kms(&i915->drm, "Session Close Failed. status: 0x%X\n",
+ session_close_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct i915_hdcp_ops gsc_hdcp_ops = {
+ .initiate_hdcp2_session = gsc_hdcp_initiate_session,
+ .verify_receiver_cert_prepare_km =
+ gsc_hdcp_verify_receiver_cert_prepare_km,
+ .verify_hprime = gsc_hdcp_verify_hprime,
+ .store_pairing_info = gsc_hdcp_store_pairing_info,
+ .initiate_locality_check = gsc_hdcp_initiate_locality_check,
+ .verify_lprime = gsc_hdcp_verify_lprime,
+ .get_session_key = gsc_hdcp_get_session_key,
+ .repeater_check_flow_prepare_ack =
+ gsc_hdcp_repeater_check_flow_prepare_ack,
+ .verify_mprime = gsc_hdcp_verify_mprime,
+ .enable_hdcp_authentication = gsc_hdcp_enable_authentication,
+ .close_hdcp_session = gsc_hdcp_close_session,
+};
+
+/*This function helps allocate memory for the command that we will send to gsc cs */
+static int intel_hdcp_gsc_initialize_message(struct drm_i915_private *i915,
+ struct intel_hdcp_gsc_message *hdcp_message)
+{
+ struct intel_gt *gt = i915->media_gt;
+ struct drm_i915_gem_object *obj = NULL;
+ struct i915_vma *vma = NULL;
+ void *cmd;
+ int err;
+
+ /* allocate object of one page for HDCP command memory and store it */
+ obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
+
+ if (IS_ERR(obj)) {
+ drm_err(&i915->drm, "Failed to allocate HDCP streaming command!\n");
+ return PTR_ERR(obj);
+ }
+
+ cmd = i915_gem_object_pin_map_unlocked(obj, i915_coherent_map_type(i915, obj, true));
+ if (IS_ERR(cmd)) {
+ drm_err(&i915->drm, "Failed to map gsc message page!\n");
+ err = PTR_ERR(cmd);
+ goto out_unpin;
+ }
+
+ vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
+ if (IS_ERR(vma)) {
+ err = PTR_ERR(vma);
+ goto out_unmap;
+ }
+
+ err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
+ if (err)
+ goto out_unmap;
+
+ memset(cmd, 0, obj->base.size);
+
+ hdcp_message->hdcp_cmd = cmd;
+ hdcp_message->vma = vma;
+
+ return 0;
+
+out_unmap:
+ i915_gem_object_unpin_map(obj);
+out_unpin:
+ i915_gem_object_put(obj);
+ return err;
+}
+
+static int intel_hdcp_gsc_hdcp2_init(struct drm_i915_private *i915)
+{
+ struct intel_hdcp_gsc_message *hdcp_message;
+ int ret;
+
+ hdcp_message = kzalloc(sizeof(*hdcp_message), GFP_KERNEL);
+
+ if (!hdcp_message)
+ return -ENOMEM;
+
+ /*
+ * NOTE: No need to lock the comp mutex here as it is already
+ * going to be taken before this function called
+ */
+ i915->display.hdcp.hdcp_message = hdcp_message;
+ ret = intel_hdcp_gsc_initialize_message(i915, hdcp_message);
+
+ if (ret)
+ drm_err(&i915->drm, "Could not initialize hdcp_message\n");
+
+ return ret;
+}
+
+static void intel_hdcp_gsc_free_message(struct drm_i915_private *i915)
+{
+ struct intel_hdcp_gsc_message *hdcp_message =
+ i915->display.hdcp.hdcp_message;
+
+ i915_vma_unpin_and_release(&hdcp_message->vma, I915_VMA_RELEASE_MAP);
+ kfree(hdcp_message);
+}
+
+int intel_hdcp_gsc_init(struct drm_i915_private *i915)
+{
+ struct i915_hdcp_master *data;
+ int ret;
+
+ data = kzalloc(sizeof(struct i915_hdcp_master), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_lock(&i915->display.hdcp.comp_mutex);
+ i915->display.hdcp.master = data;
+ i915->display.hdcp.master->hdcp_dev = i915->drm.dev;
+ i915->display.hdcp.master->ops = &gsc_hdcp_ops;
+ ret = intel_hdcp_gsc_hdcp2_init(i915);
+ mutex_unlock(&i915->display.hdcp.comp_mutex);
+
+ return ret;
+}
+
+void intel_hdcp_gsc_fini(struct drm_i915_private *i915)
+{
+ intel_hdcp_gsc_free_message(i915);
+ kfree(i915->display.hdcp.master);
+}
+
+static int intel_gsc_send_sync(struct drm_i915_private *i915,
+ struct intel_gsc_mtl_header *header, u64 addr,
+ size_t msg_out_len)
+{
+ struct intel_gt *gt = i915->media_gt;
+ int ret;
+
+ header->flags = 0;
+ ret = intel_gsc_uc_heci_cmd_submit_packet(&gt->uc.gsc, addr,
+ header->message_size,
+ addr,
+ msg_out_len + sizeof(*header));
+ if (ret) {
+ drm_err(&i915->drm, "failed to send gsc HDCP msg (%d)\n", ret);
+ return ret;
+ }
+
+ /*
+ * Checking validity marker for memory sanity
+ */
+ if (header->validity_marker != GSC_HECI_VALIDITY_MARKER) {
+ drm_err(&i915->drm, "invalid validity marker\n");
+ return -EINVAL;
+ }
+
+ if (header->status != 0) {
+ drm_err(&i915->drm, "header status indicates error %d\n",
+ header->status);
+ return -EINVAL;
+ }
+
+ if (header->flags & GSC_OUTFLAG_MSG_PENDING)
+ return -EAGAIN;
+
+ return 0;
+}
+
+/*
+ * This function can now be used for sending requests and will also handle
+ * receipt of reply messages hence no different function of message retrieval
+ * is required. We will initialize intel_hdcp_gsc_message structure then add
+ * gsc cs memory header as stated in specs after which the normal HDCP payload
+ * will follow
+ */
+ssize_t intel_hdcp_gsc_msg_send(struct drm_i915_private *i915, u8 *msg_in,
+ size_t msg_in_len, u8 *msg_out,
+ size_t msg_out_len)
+{
+ struct intel_gt *gt = i915->media_gt;
+ struct intel_gsc_mtl_header *header;
+ const size_t max_msg_size = PAGE_SIZE - sizeof(*header);
+ struct intel_hdcp_gsc_message *hdcp_message;
+ u64 addr, host_session_id;
+ u32 reply_size, msg_size;
+ int ret, tries = 0;
+
+ if (!intel_uc_uses_gsc_uc(&gt->uc))
+ return -ENODEV;
+
+ if (msg_in_len > max_msg_size || msg_out_len > max_msg_size)
+ return -ENOSPC;
+
+ hdcp_message = i915->display.hdcp.hdcp_message;
+ header = hdcp_message->hdcp_cmd;
+ addr = i915_ggtt_offset(hdcp_message->vma);
+
+ msg_size = msg_in_len + sizeof(*header);
+ memset(header, 0, msg_size);
+ get_random_bytes(&host_session_id, sizeof(u64));
+ intel_gsc_uc_heci_cmd_emit_mtl_header(header, HECI_MEADDRESS_HDCP,
+ msg_size, host_session_id);
+ memcpy(hdcp_message->hdcp_cmd + sizeof(*header), msg_in, msg_in_len);
+
+ /*
+ * Keep sending request in case the pending bit is set no need to add
+ * message handle as we are using same address hence loc. of header is
+ * same and it will contain the message handle. we will send the message
+ * 20 times each message 50 ms apart
+ */
+ do {
+ ret = intel_gsc_send_sync(i915, header, addr, msg_out_len);
+
+ /* Only try again if gsc says so */
+ if (ret != -EAGAIN)
+ break;
+
+ msleep(50);
+
+ } while (++tries < 20);
+
+ if (ret)
+ goto err;
+
+ /* we use the same mem for the reply, so header is in the same loc */
+ reply_size = header->message_size - sizeof(*header);
+ if (reply_size > msg_out_len) {
+ drm_warn(&i915->drm, "caller with insufficient HDCP reply size %u (%d)\n",
+ reply_size, (u32)msg_out_len);
+ reply_size = msg_out_len;
+ } else if (reply_size != msg_out_len) {
+ drm_dbg_kms(&i915->drm, "caller unexpected HCDP reply size %u (%d)\n",
+ reply_size, (u32)msg_out_len);
+ }
+
+ memcpy(msg_out, hdcp_message->hdcp_cmd + sizeof(*header), msg_out_len);
+
+err:
+ return ret;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp_gsc.h b/drivers/gpu/drm/i915/display/intel_hdcp_gsc.h
new file mode 100644
index 000000000000..5cc9fd2e88f6
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_hdcp_gsc.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_HDCP_GSC_H__
+#define __INTEL_HDCP_GSC_H__
+
+#include <linux/err.h>
+#include <linux/types.h>
+
+struct drm_i915_private;
+
+struct intel_hdcp_gsc_message {
+ struct i915_vma *vma;
+ void *hdcp_cmd;
+};
+
+bool intel_hdcp_gsc_cs_required(struct drm_i915_private *i915);
+ssize_t intel_hdcp_gsc_msg_send(struct drm_i915_private *i915, u8 *msg_in,
+ size_t msg_in_len, u8 *msg_out,
+ size_t msg_out_len);
+int intel_hdcp_gsc_init(struct drm_i915_private *i915);
+void intel_hdcp_gsc_fini(struct drm_i915_private *i915);
+
+#endif /* __INTEL_HDCP_GCS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index c7e9e1fbed37..a690a5616506 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -2646,11 +2646,8 @@ bool intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder,
bool scrambling)
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
struct drm_scrambling *sink_scrambling =
&connector->display_info.hdmi.scdc.scrambling;
- struct i2c_adapter *adapter =
- intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus);
if (!sink_scrambling->supported)
return true;
@@ -2661,9 +2658,8 @@ bool intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder,
str_yes_no(scrambling), high_tmds_clock_ratio ? 40 : 10);
/* Set TMDS bit clock ratio to 1/40 or 1/10, and enable/disable scrambling */
- return drm_scdc_set_high_tmds_clock_ratio(adapter,
- high_tmds_clock_ratio) &&
- drm_scdc_set_scrambling(adapter, scrambling);
+ return drm_scdc_set_high_tmds_clock_ratio(connector, high_tmds_clock_ratio) &&
+ drm_scdc_set_scrambling(connector, scrambling);
}
static u8 chv_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port)
diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c
index 907ab7526cb4..b12900446828 100644
--- a/drivers/gpu/drm/i915/display/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/display/intel_hotplug.c
@@ -389,6 +389,13 @@ static void i915_hotplug_work_func(struct work_struct *work)
spin_unlock_irq(&dev_priv->irq_lock);
+ /* Skip calling encode hotplug handlers if ignore long HPD set*/
+ if (dev_priv->display.hotplug.ignore_long_hpd) {
+ drm_dbg_kms(&dev_priv->drm, "Ignore HPD flag on - skip encoder hotplug handlers\n");
+ mutex_unlock(&dev_priv->drm.mode_config.mutex);
+ return;
+ }
+
drm_connector_list_iter_begin(&dev_priv->drm, &conn_iter);
for_each_intel_connector_iter(connector, &conn_iter) {
enum hpd_pin pin;
@@ -940,4 +947,6 @@ void intel_hpd_debugfs_register(struct drm_i915_private *i915)
i915, &i915_hpd_storm_ctl_fops);
debugfs_create_file("i915_hpd_short_storm_ctl", 0644, minor->debugfs_root,
i915, &i915_hpd_short_storm_ctl_fops);
+ debugfs_create_bool("i915_ignore_long_hpd", 0644, minor->debugfs_root,
+ &i915->display.hotplug.ignore_long_hpd);
}
diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
index a504b3a7fbd5..0de44b3631cd 100644
--- a/drivers/gpu/drm/i915/display/intel_lvds.c
+++ b/drivers/gpu/drm/i915/display/intel_lvds.c
@@ -51,6 +51,7 @@
#include "intel_lvds.h"
#include "intel_lvds_regs.h"
#include "intel_panel.h"
+#include "intel_pps_regs.h"
/* Private structure for the integrated LVDS support */
struct intel_lvds_pps {
diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
index 1d0c9e247c42..134b943f1953 100644
--- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c
+++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
@@ -26,6 +26,7 @@
#include "intel_fifo_underrun.h"
#include "intel_modeset_setup.h"
#include "intel_pch_display.h"
+#include "intel_vblank.h"
#include "intel_wm.h"
#include "skl_watermark.h"
@@ -99,7 +100,6 @@ static void intel_crtc_disable_noatomic(struct intel_crtc *crtc,
intel_fbc_disable(crtc);
intel_update_watermarks(i915);
- intel_disable_shared_dpll(crtc_state);
intel_display_power_put_all_in_set(i915, &crtc->enabled_power_domains);
diff --git a/drivers/gpu/drm/i915/display/intel_opregion.c b/drivers/gpu/drm/i915/display/intel_opregion.c
index b8dce0576512..b7973a05d022 100644
--- a/drivers/gpu/drm/i915/display/intel_opregion.c
+++ b/drivers/gpu/drm/i915/display/intel_opregion.c
@@ -1159,13 +1159,10 @@ void intel_opregion_register(struct drm_i915_private *i915)
intel_opregion_resume(i915);
}
-void intel_opregion_resume(struct drm_i915_private *i915)
+static void intel_opregion_resume_display(struct drm_i915_private *i915)
{
struct intel_opregion *opregion = &i915->display.opregion;
- if (!opregion->header)
- return;
-
if (opregion->acpi) {
intel_didl_outputs(i915);
intel_setup_cadls(i915);
@@ -1186,18 +1183,24 @@ void intel_opregion_resume(struct drm_i915_private *i915)
/* Some platforms abuse the _DSM to enable MUX */
intel_dsm_get_bios_data_funcs_supported(i915);
-
- intel_opregion_notify_adapter(i915, PCI_D0);
}
-void intel_opregion_suspend(struct drm_i915_private *i915, pci_power_t state)
+void intel_opregion_resume(struct drm_i915_private *i915)
{
struct intel_opregion *opregion = &i915->display.opregion;
if (!opregion->header)
return;
- intel_opregion_notify_adapter(i915, state);
+ if (HAS_DISPLAY(i915))
+ intel_opregion_resume_display(i915);
+
+ intel_opregion_notify_adapter(i915, PCI_D0);
+}
+
+static void intel_opregion_suspend_display(struct drm_i915_private *i915)
+{
+ struct intel_opregion *opregion = &i915->display.opregion;
if (opregion->asle)
opregion->asle->ardy = ASLE_ARDY_NOT_READY;
@@ -1208,6 +1211,19 @@ void intel_opregion_suspend(struct drm_i915_private *i915, pci_power_t state)
opregion->acpi->drdy = 0;
}
+void intel_opregion_suspend(struct drm_i915_private *i915, pci_power_t state)
+{
+ struct intel_opregion *opregion = &i915->display.opregion;
+
+ if (!opregion->header)
+ return;
+
+ intel_opregion_notify_adapter(i915, state);
+
+ if (HAS_DISPLAY(i915))
+ intel_opregion_suspend_display(i915);
+}
+
void intel_opregion_unregister(struct drm_i915_private *i915)
{
struct intel_opregion *opregion = &i915->display.opregion;
@@ -1221,6 +1237,14 @@ void intel_opregion_unregister(struct drm_i915_private *i915)
unregister_acpi_notifier(&opregion->acpi_notifier);
opregion->acpi_notifier.notifier_call = NULL;
}
+}
+
+void intel_opregion_cleanup(struct drm_i915_private *i915)
+{
+ struct intel_opregion *opregion = &i915->display.opregion;
+
+ if (!opregion->header)
+ return;
/* just clear all opregion memory pointers now */
memunmap(opregion->header);
diff --git a/drivers/gpu/drm/i915/display/intel_opregion.h b/drivers/gpu/drm/i915/display/intel_opregion.h
index d02e6696a050..fd2ea8ef0fa2 100644
--- a/drivers/gpu/drm/i915/display/intel_opregion.h
+++ b/drivers/gpu/drm/i915/display/intel_opregion.h
@@ -60,6 +60,7 @@ struct intel_opregion {
#ifdef CONFIG_ACPI
int intel_opregion_setup(struct drm_i915_private *dev_priv);
+void intel_opregion_cleanup(struct drm_i915_private *i915);
void intel_opregion_register(struct drm_i915_private *dev_priv);
void intel_opregion_unregister(struct drm_i915_private *dev_priv);
@@ -85,6 +86,10 @@ static inline int intel_opregion_setup(struct drm_i915_private *dev_priv)
return 0;
}
+static inline void intel_opregion_cleanup(struct drm_i915_private *i915)
+{
+}
+
static inline void intel_opregion_register(struct drm_i915_private *dev_priv)
{
}
diff --git a/drivers/gpu/drm/i915/display/intel_pch_display.c b/drivers/gpu/drm/i915/display/intel_pch_display.c
index 22507da0b5f0..2411fe4dee8b 100644
--- a/drivers/gpu/drm/i915/display/intel_pch_display.c
+++ b/drivers/gpu/drm/i915/display/intel_pch_display.c
@@ -9,6 +9,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_fdi.h"
+#include "intel_fdi_regs.h"
#include "intel_lvds.h"
#include "intel_lvds_regs.h"
#include "intel_pch_display.h"
diff --git a/drivers/gpu/drm/i915/display/intel_pps.c b/drivers/gpu/drm/i915/display/intel_pps.c
index 24b5b12f7732..7f9926672a6a 100644
--- a/drivers/gpu/drm/i915/display/intel_pps.c
+++ b/drivers/gpu/drm/i915/display/intel_pps.c
@@ -15,6 +15,7 @@
#include "intel_lvds.h"
#include "intel_lvds_regs.h"
#include "intel_pps.h"
+#include "intel_pps_regs.h"
#include "intel_quirks.h"
static void vlv_steal_power_sequencer(struct drm_i915_private *dev_priv,
diff --git a/drivers/gpu/drm/i915/display/intel_pps_regs.h b/drivers/gpu/drm/i915/display/intel_pps_regs.h
new file mode 100644
index 000000000000..60edd2a27100
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_pps_regs.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_PPS_REGS_H__
+#define __INTEL_PPS_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+/* Panel power sequencing */
+#define PPS_BASE 0x61200
+#define VLV_PPS_BASE (VLV_DISPLAY_BASE + PPS_BASE)
+#define PCH_PPS_BASE 0xC7200
+
+#define _MMIO_PPS(pps_idx, reg) _MMIO(dev_priv->display.pps.mmio_base - \
+ PPS_BASE + (reg) + \
+ (pps_idx) * 0x100)
+
+#define _PP_STATUS 0x61200
+#define PP_STATUS(pps_idx) _MMIO_PPS(pps_idx, _PP_STATUS)
+#define PP_ON REG_BIT(31)
+/*
+ * Indicates that all dependencies of the panel are on:
+ *
+ * - PLL enabled
+ * - pipe enabled
+ * - LVDS/DVOB/DVOC on
+ */
+#define PP_READY REG_BIT(30)
+#define PP_SEQUENCE_MASK REG_GENMASK(29, 28)
+#define PP_SEQUENCE_NONE REG_FIELD_PREP(PP_SEQUENCE_MASK, 0)
+#define PP_SEQUENCE_POWER_UP REG_FIELD_PREP(PP_SEQUENCE_MASK, 1)
+#define PP_SEQUENCE_POWER_DOWN REG_FIELD_PREP(PP_SEQUENCE_MASK, 2)
+#define PP_CYCLE_DELAY_ACTIVE REG_BIT(27)
+#define PP_SEQUENCE_STATE_MASK REG_GENMASK(3, 0)
+#define PP_SEQUENCE_STATE_OFF_IDLE REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0x0)
+#define PP_SEQUENCE_STATE_OFF_S0_1 REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0x1)
+#define PP_SEQUENCE_STATE_OFF_S0_2 REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0x2)
+#define PP_SEQUENCE_STATE_OFF_S0_3 REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0x3)
+#define PP_SEQUENCE_STATE_ON_IDLE REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0x8)
+#define PP_SEQUENCE_STATE_ON_S1_1 REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0x9)
+#define PP_SEQUENCE_STATE_ON_S1_2 REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0xa)
+#define PP_SEQUENCE_STATE_ON_S1_3 REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0xb)
+#define PP_SEQUENCE_STATE_RESET REG_FIELD_PREP(PP_SEQUENCE_STATE_MASK, 0xf)
+
+#define _PP_CONTROL 0x61204
+#define PP_CONTROL(pps_idx) _MMIO_PPS(pps_idx, _PP_CONTROL)
+#define PANEL_UNLOCK_MASK REG_GENMASK(31, 16)
+#define PANEL_UNLOCK_REGS REG_FIELD_PREP(PANEL_UNLOCK_MASK, 0xabcd)
+#define BXT_POWER_CYCLE_DELAY_MASK REG_GENMASK(8, 4)
+#define EDP_FORCE_VDD REG_BIT(3)
+#define EDP_BLC_ENABLE REG_BIT(2)
+#define PANEL_POWER_RESET REG_BIT(1)
+#define PANEL_POWER_ON REG_BIT(0)
+
+#define _PP_ON_DELAYS 0x61208
+#define PP_ON_DELAYS(pps_idx) _MMIO_PPS(pps_idx, _PP_ON_DELAYS)
+#define PANEL_PORT_SELECT_MASK REG_GENMASK(31, 30)
+#define PANEL_PORT_SELECT_LVDS REG_FIELD_PREP(PANEL_PORT_SELECT_MASK, 0)
+#define PANEL_PORT_SELECT_DPA REG_FIELD_PREP(PANEL_PORT_SELECT_MASK, 1)
+#define PANEL_PORT_SELECT_DPC REG_FIELD_PREP(PANEL_PORT_SELECT_MASK, 2)
+#define PANEL_PORT_SELECT_DPD REG_FIELD_PREP(PANEL_PORT_SELECT_MASK, 3)
+#define PANEL_PORT_SELECT_VLV(port) REG_FIELD_PREP(PANEL_PORT_SELECT_MASK, port)
+#define PANEL_POWER_UP_DELAY_MASK REG_GENMASK(28, 16)
+#define PANEL_LIGHT_ON_DELAY_MASK REG_GENMASK(12, 0)
+
+#define _PP_OFF_DELAYS 0x6120C
+#define PP_OFF_DELAYS(pps_idx) _MMIO_PPS(pps_idx, _PP_OFF_DELAYS)
+#define PANEL_POWER_DOWN_DELAY_MASK REG_GENMASK(28, 16)
+#define PANEL_LIGHT_OFF_DELAY_MASK REG_GENMASK(12, 0)
+
+#define _PP_DIVISOR 0x61210
+#define PP_DIVISOR(pps_idx) _MMIO_PPS(pps_idx, _PP_DIVISOR)
+#define PP_REFERENCE_DIVIDER_MASK REG_GENMASK(31, 8)
+#define PANEL_POWER_CYCLE_DELAY_MASK REG_GENMASK(4, 0)
+
+#endif /* __INTEL_PPS_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index 44610b20cd29..6badfff2b4a2 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -34,6 +34,7 @@
#include "intel_dp_aux.h"
#include "intel_hdmi.h"
#include "intel_psr.h"
+#include "intel_psr_regs.h"
#include "intel_snps_phy.h"
#include "skl_universal_plane.h"
@@ -519,6 +520,17 @@ static u32 intel_psr2_get_tp_time(struct intel_dp *intel_dp)
return val;
}
+static int psr2_block_count_lines(struct intel_dp *intel_dp)
+{
+ return intel_dp->psr.io_wake_lines < 9 &&
+ intel_dp->psr.fast_wake_lines < 9 ? 8 : 12;
+}
+
+static int psr2_block_count(struct intel_dp *intel_dp)
+{
+ return psr2_block_count_lines(intel_dp) / 4;
+}
+
static void hsw_activate_psr2(struct intel_dp *intel_dp)
{
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
@@ -536,11 +548,10 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp)
val |= intel_psr2_get_tp_time(intel_dp);
if (DISPLAY_VER(dev_priv) >= 12) {
- if (intel_dp->psr.io_wake_lines < 9 &&
- intel_dp->psr.fast_wake_lines < 9)
- val |= TGL_EDP_PSR2_BLOCK_COUNT_NUM_2;
- else
+ if (psr2_block_count(intel_dp) > 2)
val |= TGL_EDP_PSR2_BLOCK_COUNT_NUM_3;
+ else
+ val |= TGL_EDP_PSR2_BLOCK_COUNT_NUM_2;
}
/* Wa_22012278275:adl-p */
@@ -958,6 +969,15 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
return false;
}
+ /* Vblank >= PSR2_CTL Block Count Number maximum line count */
+ if (crtc_state->hw.adjusted_mode.crtc_vblank_end -
+ crtc_state->hw.adjusted_mode.crtc_vblank_start <
+ psr2_block_count_lines(intel_dp)) {
+ drm_dbg_kms(&dev_priv->drm,
+ "PSR2 not enabled, too short vblank time\n");
+ return false;
+ }
+
if (HAS_PSR2_SEL_FETCH(dev_priv)) {
if (!intel_psr2_sel_fetch_config_valid(intel_dp, crtc_state) &&
!HAS_PSR_HW_TRACKING(dev_priv)) {
@@ -1134,6 +1154,34 @@ static u32 wa_16013835468_bit_get(struct intel_dp *intel_dp)
}
}
+/*
+ * Wa_16013835468
+ * Wa_14015648006
+ */
+static void wm_optimization_wa(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+ bool set_wa_bit = false;
+
+ /* Wa_14015648006 */
+ if (IS_MTL_DISPLAY_STEP(dev_priv, STEP_A0, STEP_B0) ||
+ IS_DISPLAY_VER(dev_priv, 11, 13))
+ set_wa_bit |= crtc_state->wm_level_disabled;
+
+ /* Wa_16013835468 */
+ if (DISPLAY_VER(dev_priv) == 12)
+ set_wa_bit |= crtc_state->hw.adjusted_mode.crtc_vblank_start !=
+ crtc_state->hw.adjusted_mode.crtc_vdisplay;
+
+ if (set_wa_bit)
+ intel_de_rmw(dev_priv, GEN8_CHICKEN_DCPR_1,
+ 0, wa_16013835468_bit_get(intel_dp));
+ else
+ intel_de_rmw(dev_priv, GEN8_CHICKEN_DCPR_1,
+ wa_16013835468_bit_get(intel_dp), 0);
+}
+
static void intel_psr_enable_source(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
{
@@ -1177,13 +1225,7 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp,
* Wa_16013835468
* Wa_14015648006
*/
- if (IS_MTL_DISPLAY_STEP(dev_priv, STEP_A0, STEP_B0) ||
- IS_DISPLAY_VER(dev_priv, 12, 13)) {
- if (crtc_state->hw.adjusted_mode.crtc_vblank_start !=
- crtc_state->hw.adjusted_mode.crtc_vdisplay)
- intel_de_rmw(dev_priv, GEN8_CHICKEN_DCPR_1, 0,
- wa_16013835468_bit_get(intel_dp));
- }
+ wm_optimization_wa(intel_dp, crtc_state);
if (intel_dp->psr.psr2_enabled) {
if (DISPLAY_VER(dev_priv) == 9)
@@ -1361,8 +1403,7 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp)
* Wa_16013835468
* Wa_14015648006
*/
- if (IS_MTL_DISPLAY_STEP(dev_priv, STEP_A0, STEP_B0) ||
- IS_DISPLAY_VER(dev_priv, 12, 13))
+ if (DISPLAY_VER(dev_priv) >= 11)
intel_de_rmw(dev_priv, GEN8_CHICKEN_DCPR_1,
wa_16013835468_bit_get(intel_dp), 0);
@@ -1928,14 +1969,20 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state,
* - PSR disabled in new state
* - All planes will go inactive
* - Changing between PSR versions
+ * - Display WA #1136: skl, bxt
*/
needs_to_disable |= intel_crtc_needs_modeset(new_crtc_state);
needs_to_disable |= !new_crtc_state->has_psr;
needs_to_disable |= !new_crtc_state->active_planes;
needs_to_disable |= new_crtc_state->has_psr2 != psr->psr2_enabled;
+ needs_to_disable |= DISPLAY_VER(i915) < 11 &&
+ new_crtc_state->wm_level_disabled;
if (psr->enabled && needs_to_disable)
intel_psr_disable_locked(intel_dp);
+ else if (psr->enabled && new_crtc_state->wm_level_disabled)
+ /* Wa_14015648006 */
+ wm_optimization_wa(intel_dp, new_crtc_state);
mutex_unlock(&psr->lock);
}
@@ -1954,23 +2001,29 @@ static void _intel_psr_post_plane_update(const struct intel_atomic_state *state,
crtc_state->uapi.encoder_mask) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct intel_psr *psr = &intel_dp->psr;
+ bool keep_disabled = false;
mutex_lock(&psr->lock);
- if (psr->sink_not_reliable)
- goto exit;
-
drm_WARN_ON(&dev_priv->drm, psr->enabled && !crtc_state->active_planes);
- /* Only enable if there is active planes */
- if (!psr->enabled && crtc_state->active_planes)
+ keep_disabled |= psr->sink_not_reliable;
+ keep_disabled |= !crtc_state->active_planes;
+
+ /* Display WA #1136: skl, bxt */
+ keep_disabled |= DISPLAY_VER(dev_priv) < 11 &&
+ crtc_state->wm_level_disabled;
+
+ if (!psr->enabled && !keep_disabled)
intel_psr_enable_locked(intel_dp, crtc_state);
+ else if (psr->enabled && !crtc_state->wm_level_disabled)
+ /* Wa_14015648006 */
+ wm_optimization_wa(intel_dp, crtc_state);
/* Force a PSR exit when enabling CRC to avoid CRC timeouts */
if (crtc_state->crc_enabled && psr->enabled)
psr_force_hw_tracking_exit(intel_dp);
-exit:
mutex_unlock(&psr->lock);
}
}
@@ -2644,3 +2697,302 @@ void intel_psr_unlock(const struct intel_crtc_state *crtc_state)
break;
}
}
+
+static void
+psr_source_status(struct intel_dp *intel_dp, struct seq_file *m)
+{
+ struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+ const char *status = "unknown";
+ u32 val, status_val;
+
+ if (intel_dp->psr.psr2_enabled) {
+ static const char * const live_status[] = {
+ "IDLE",
+ "CAPTURE",
+ "CAPTURE_FS",
+ "SLEEP",
+ "BUFON_FW",
+ "ML_UP",
+ "SU_STANDBY",
+ "FAST_SLEEP",
+ "DEEP_SLEEP",
+ "BUF_ON",
+ "TG_ON"
+ };
+ val = intel_de_read(dev_priv,
+ EDP_PSR2_STATUS(intel_dp->psr.transcoder));
+ status_val = REG_FIELD_GET(EDP_PSR2_STATUS_STATE_MASK, val);
+ if (status_val < ARRAY_SIZE(live_status))
+ status = live_status[status_val];
+ } else {
+ static const char * const live_status[] = {
+ "IDLE",
+ "SRDONACK",
+ "SRDENT",
+ "BUFOFF",
+ "BUFON",
+ "AUXACK",
+ "SRDOFFACK",
+ "SRDENT_ON",
+ };
+ val = intel_de_read(dev_priv,
+ EDP_PSR_STATUS(intel_dp->psr.transcoder));
+ status_val = (val & EDP_PSR_STATUS_STATE_MASK) >>
+ EDP_PSR_STATUS_STATE_SHIFT;
+ if (status_val < ARRAY_SIZE(live_status))
+ status = live_status[status_val];
+ }
+
+ seq_printf(m, "Source PSR status: %s [0x%08x]\n", status, val);
+}
+
+static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp)
+{
+ struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+ struct intel_psr *psr = &intel_dp->psr;
+ intel_wakeref_t wakeref;
+ const char *status;
+ bool enabled;
+ u32 val;
+
+ seq_printf(m, "Sink support: %s", str_yes_no(psr->sink_support));
+ if (psr->sink_support)
+ seq_printf(m, " [0x%02x]", intel_dp->psr_dpcd[0]);
+ seq_puts(m, "\n");
+
+ if (!psr->sink_support)
+ return 0;
+
+ wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
+ mutex_lock(&psr->lock);
+
+ if (psr->enabled)
+ status = psr->psr2_enabled ? "PSR2 enabled" : "PSR1 enabled";
+ else
+ status = "disabled";
+ seq_printf(m, "PSR mode: %s\n", status);
+
+ if (!psr->enabled) {
+ seq_printf(m, "PSR sink not reliable: %s\n",
+ str_yes_no(psr->sink_not_reliable));
+
+ goto unlock;
+ }
+
+ if (psr->psr2_enabled) {
+ val = intel_de_read(dev_priv,
+ EDP_PSR2_CTL(intel_dp->psr.transcoder));
+ enabled = val & EDP_PSR2_ENABLE;
+ } else {
+ val = intel_de_read(dev_priv,
+ EDP_PSR_CTL(intel_dp->psr.transcoder));
+ enabled = val & EDP_PSR_ENABLE;
+ }
+ seq_printf(m, "Source PSR ctl: %s [0x%08x]\n",
+ str_enabled_disabled(enabled), val);
+ psr_source_status(intel_dp, m);
+ seq_printf(m, "Busy frontbuffer bits: 0x%08x\n",
+ psr->busy_frontbuffer_bits);
+
+ /*
+ * SKL+ Perf counter is reset to 0 everytime DC state is entered
+ */
+ if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
+ val = intel_de_read(dev_priv,
+ EDP_PSR_PERF_CNT(intel_dp->psr.transcoder));
+ val &= EDP_PSR_PERF_CNT_MASK;
+ seq_printf(m, "Performance counter: %u\n", val);
+ }
+
+ if (psr->debug & I915_PSR_DEBUG_IRQ) {
+ seq_printf(m, "Last attempted entry at: %lld\n",
+ psr->last_entry_attempt);
+ seq_printf(m, "Last exit at: %lld\n", psr->last_exit);
+ }
+
+ if (psr->psr2_enabled) {
+ u32 su_frames_val[3];
+ int frame;
+
+ /*
+ * Reading all 3 registers before hand to minimize crossing a
+ * frame boundary between register reads
+ */
+ for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame += 3) {
+ val = intel_de_read(dev_priv,
+ PSR2_SU_STATUS(intel_dp->psr.transcoder, frame));
+ su_frames_val[frame / 3] = val;
+ }
+
+ seq_puts(m, "Frame:\tPSR2 SU blocks:\n");
+
+ for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame++) {
+ u32 su_blocks;
+
+ su_blocks = su_frames_val[frame / 3] &
+ PSR2_SU_STATUS_MASK(frame);
+ su_blocks = su_blocks >> PSR2_SU_STATUS_SHIFT(frame);
+ seq_printf(m, "%d\t%d\n", frame, su_blocks);
+ }
+
+ seq_printf(m, "PSR2 selective fetch: %s\n",
+ str_enabled_disabled(psr->psr2_sel_fetch_enabled));
+ }
+
+unlock:
+ mutex_unlock(&psr->lock);
+ intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
+
+ return 0;
+}
+
+static int i915_edp_psr_status_show(struct seq_file *m, void *data)
+{
+ struct drm_i915_private *dev_priv = m->private;
+ struct intel_dp *intel_dp = NULL;
+ struct intel_encoder *encoder;
+
+ if (!HAS_PSR(dev_priv))
+ return -ENODEV;
+
+ /* Find the first EDP which supports PSR */
+ for_each_intel_encoder_with_psr(&dev_priv->drm, encoder) {
+ intel_dp = enc_to_intel_dp(encoder);
+ break;
+ }
+
+ if (!intel_dp)
+ return -ENODEV;
+
+ return intel_psr_status(m, intel_dp);
+}
+DEFINE_SHOW_ATTRIBUTE(i915_edp_psr_status);
+
+static int
+i915_edp_psr_debug_set(void *data, u64 val)
+{
+ struct drm_i915_private *dev_priv = data;
+ struct intel_encoder *encoder;
+ intel_wakeref_t wakeref;
+ int ret = -ENODEV;
+
+ if (!HAS_PSR(dev_priv))
+ return ret;
+
+ for_each_intel_encoder_with_psr(&dev_priv->drm, encoder) {
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ drm_dbg_kms(&dev_priv->drm, "Setting PSR debug to %llx\n", val);
+
+ wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
+
+ // TODO: split to each transcoder's PSR debug state
+ ret = intel_psr_debug_set(intel_dp, val);
+
+ intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
+ }
+
+ return ret;
+}
+
+static int
+i915_edp_psr_debug_get(void *data, u64 *val)
+{
+ struct drm_i915_private *dev_priv = data;
+ struct intel_encoder *encoder;
+
+ if (!HAS_PSR(dev_priv))
+ return -ENODEV;
+
+ for_each_intel_encoder_with_psr(&dev_priv->drm, encoder) {
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ // TODO: split to each transcoder's PSR debug state
+ *val = READ_ONCE(intel_dp->psr.debug);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(i915_edp_psr_debug_fops,
+ i915_edp_psr_debug_get, i915_edp_psr_debug_set,
+ "%llu\n");
+
+void intel_psr_debugfs_register(struct drm_i915_private *i915)
+{
+ struct drm_minor *minor = i915->drm.primary;
+
+ debugfs_create_file("i915_edp_psr_debug", 0644, minor->debugfs_root,
+ i915, &i915_edp_psr_debug_fops);
+
+ debugfs_create_file("i915_edp_psr_status", 0444, minor->debugfs_root,
+ i915, &i915_edp_psr_status_fops);
+}
+
+static int i915_psr_sink_status_show(struct seq_file *m, void *data)
+{
+ struct intel_connector *connector = m->private;
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ static const char * const sink_status[] = {
+ "inactive",
+ "transition to active, capture and display",
+ "active, display from RFB",
+ "active, capture and display on sink device timings",
+ "transition to inactive, capture and display, timing re-sync",
+ "reserved",
+ "reserved",
+ "sink internal error",
+ };
+ const char *str;
+ int ret;
+ u8 val;
+
+ if (!CAN_PSR(intel_dp)) {
+ seq_puts(m, "PSR Unsupported\n");
+ return -ENODEV;
+ }
+
+ if (connector->base.status != connector_status_connected)
+ return -ENODEV;
+
+ ret = drm_dp_dpcd_readb(&intel_dp->aux, DP_PSR_STATUS, &val);
+ if (ret != 1)
+ return ret < 0 ? ret : -EIO;
+
+ val &= DP_PSR_SINK_STATE_MASK;
+ if (val < ARRAY_SIZE(sink_status))
+ str = sink_status[val];
+ else
+ str = "unknown";
+
+ seq_printf(m, "Sink PSR status: 0x%x [%s]\n", val, str);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(i915_psr_sink_status);
+
+static int i915_psr_status_show(struct seq_file *m, void *data)
+{
+ struct intel_connector *connector = m->private;
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+
+ return intel_psr_status(m, intel_dp);
+}
+DEFINE_SHOW_ATTRIBUTE(i915_psr_status);
+
+void intel_psr_connector_debugfs_add(struct intel_connector *connector)
+{
+ struct drm_i915_private *i915 = to_i915(connector->base.dev);
+ struct dentry *root = connector->base.debugfs_entry;
+
+ if (connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
+ return;
+
+ debugfs_create_file("i915_psr_sink_status", 0444, root,
+ connector, &i915_psr_sink_status_fops);
+
+ if (HAS_PSR(i915))
+ debugfs_create_file("i915_psr_status", 0444, root,
+ connector, &i915_psr_status_fops);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h
index 7a38a9e7fa5b..0b95e8aa615f 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.h
+++ b/drivers/gpu/drm/i915/display/intel_psr.h
@@ -13,6 +13,7 @@ struct drm_connector;
struct drm_connector_state;
struct drm_i915_private;
struct intel_atomic_state;
+struct intel_connector;
struct intel_crtc;
struct intel_crtc_state;
struct intel_dp;
@@ -61,5 +62,7 @@ void intel_psr_resume(struct intel_dp *intel_dp);
void intel_psr_lock(const struct intel_crtc_state *crtc_state);
void intel_psr_unlock(const struct intel_crtc_state *crtc_state);
+void intel_psr_connector_debugfs_add(struct intel_connector *connector);
+void intel_psr_debugfs_register(struct drm_i915_private *i915);
#endif /* __INTEL_PSR_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_psr_regs.h b/drivers/gpu/drm/i915/display/intel_psr_regs.h
new file mode 100644
index 000000000000..958d8cabc44b
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_psr_regs.h
@@ -0,0 +1,260 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_PSR_REGS_H__
+#define __INTEL_PSR_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+#define TRANS_EXITLINE(trans) _MMIO_TRANS2((trans), _TRANS_EXITLINE_A)
+#define EXITLINE_ENABLE REG_BIT(31)
+#define EXITLINE_MASK REG_GENMASK(12, 0)
+#define EXITLINE_SHIFT 0
+
+/*
+ * HSW+ eDP PSR registers
+ *
+ * HSW PSR registers are relative to DDIA(_DDI_BUF_CTL_A + 0x800) with just one
+ * instance of it
+ */
+#define _SRD_CTL_A 0x60800
+#define _SRD_CTL_EDP 0x6f800
+#define EDP_PSR_CTL(tran) _MMIO_TRANS2(tran, _SRD_CTL_A)
+#define EDP_PSR_ENABLE (1 << 31)
+#define BDW_PSR_SINGLE_FRAME (1 << 30)
+#define EDP_PSR_RESTORE_PSR_ACTIVE_CTX_MASK (1 << 29) /* SW can't modify */
+#define EDP_PSR_LINK_STANDBY (1 << 27)
+#define EDP_PSR_MIN_LINK_ENTRY_TIME_MASK (3 << 25)
+#define EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES (0 << 25)
+#define EDP_PSR_MIN_LINK_ENTRY_TIME_4_LINES (1 << 25)
+#define EDP_PSR_MIN_LINK_ENTRY_TIME_2_LINES (2 << 25)
+#define EDP_PSR_MIN_LINK_ENTRY_TIME_0_LINES (3 << 25)
+#define EDP_PSR_MAX_SLEEP_TIME_SHIFT 20
+#define EDP_PSR_SKIP_AUX_EXIT (1 << 12)
+#define EDP_PSR_TP1_TP2_SEL (0 << 11)
+#define EDP_PSR_TP1_TP3_SEL (1 << 11)
+#define EDP_PSR_CRC_ENABLE (1 << 10) /* BDW+ */
+#define EDP_PSR_TP2_TP3_TIME_500us (0 << 8)
+#define EDP_PSR_TP2_TP3_TIME_100us (1 << 8)
+#define EDP_PSR_TP2_TP3_TIME_2500us (2 << 8)
+#define EDP_PSR_TP2_TP3_TIME_0us (3 << 8)
+#define EDP_PSR_TP4_TIME_0US (3 << 6) /* ICL+ */
+#define EDP_PSR_TP1_TIME_500us (0 << 4)
+#define EDP_PSR_TP1_TIME_100us (1 << 4)
+#define EDP_PSR_TP1_TIME_2500us (2 << 4)
+#define EDP_PSR_TP1_TIME_0us (3 << 4)
+#define EDP_PSR_IDLE_FRAME_SHIFT 0
+
+/*
+ * Until TGL, IMR/IIR are fixed at 0x648xx. On TGL+ those registers are relative
+ * to transcoder and bits defined for each one as if using no shift (i.e. as if
+ * it was for TRANSCODER_EDP)
+ */
+#define EDP_PSR_IMR _MMIO(0x64834)
+#define EDP_PSR_IIR _MMIO(0x64838)
+#define _PSR_IMR_A 0x60814
+#define _PSR_IIR_A 0x60818
+#define TRANS_PSR_IMR(tran) _MMIO_TRANS2(tran, _PSR_IMR_A)
+#define TRANS_PSR_IIR(tran) _MMIO_TRANS2(tran, _PSR_IIR_A)
+#define _EDP_PSR_TRANS_SHIFT(trans) ((trans) == TRANSCODER_EDP ? \
+ 0 : ((trans) - TRANSCODER_A + 1) * 8)
+#define TGL_PSR_MASK REG_GENMASK(2, 0)
+#define TGL_PSR_ERROR REG_BIT(2)
+#define TGL_PSR_POST_EXIT REG_BIT(1)
+#define TGL_PSR_PRE_ENTRY REG_BIT(0)
+#define EDP_PSR_MASK(trans) (TGL_PSR_MASK << \
+ _EDP_PSR_TRANS_SHIFT(trans))
+#define EDP_PSR_ERROR(trans) (TGL_PSR_ERROR << \
+ _EDP_PSR_TRANS_SHIFT(trans))
+#define EDP_PSR_POST_EXIT(trans) (TGL_PSR_POST_EXIT << \
+ _EDP_PSR_TRANS_SHIFT(trans))
+#define EDP_PSR_PRE_ENTRY(trans) (TGL_PSR_PRE_ENTRY << \
+ _EDP_PSR_TRANS_SHIFT(trans))
+
+#define _SRD_AUX_DATA_A 0x60814
+#define _SRD_AUX_DATA_EDP 0x6f814
+#define EDP_PSR_AUX_DATA(tran, i) _MMIO_TRANS2(tran, _SRD_AUX_DATA_A + (i) + 4) /* 5 registers */
+
+#define _SRD_STATUS_A 0x60840
+#define _SRD_STATUS_EDP 0x6f840
+#define EDP_PSR_STATUS(tran) _MMIO_TRANS2(tran, _SRD_STATUS_A)
+#define EDP_PSR_STATUS_STATE_MASK (7 << 29)
+#define EDP_PSR_STATUS_STATE_SHIFT 29
+#define EDP_PSR_STATUS_STATE_IDLE (0 << 29)
+#define EDP_PSR_STATUS_STATE_SRDONACK (1 << 29)
+#define EDP_PSR_STATUS_STATE_SRDENT (2 << 29)
+#define EDP_PSR_STATUS_STATE_BUFOFF (3 << 29)
+#define EDP_PSR_STATUS_STATE_BUFON (4 << 29)
+#define EDP_PSR_STATUS_STATE_AUXACK (5 << 29)
+#define EDP_PSR_STATUS_STATE_SRDOFFACK (6 << 29)
+#define EDP_PSR_STATUS_LINK_MASK (3 << 26)
+#define EDP_PSR_STATUS_LINK_FULL_OFF (0 << 26)
+#define EDP_PSR_STATUS_LINK_FULL_ON (1 << 26)
+#define EDP_PSR_STATUS_LINK_STANDBY (2 << 26)
+#define EDP_PSR_STATUS_MAX_SLEEP_TIMER_SHIFT 20
+#define EDP_PSR_STATUS_MAX_SLEEP_TIMER_MASK 0x1f
+#define EDP_PSR_STATUS_COUNT_SHIFT 16
+#define EDP_PSR_STATUS_COUNT_MASK 0xf
+#define EDP_PSR_STATUS_AUX_ERROR (1 << 15)
+#define EDP_PSR_STATUS_AUX_SENDING (1 << 12)
+#define EDP_PSR_STATUS_SENDING_IDLE (1 << 9)
+#define EDP_PSR_STATUS_SENDING_TP2_TP3 (1 << 8)
+#define EDP_PSR_STATUS_SENDING_TP1 (1 << 4)
+#define EDP_PSR_STATUS_IDLE_MASK 0xf
+
+#define _SRD_PERF_CNT_A 0x60844
+#define _SRD_PERF_CNT_EDP 0x6f844
+#define EDP_PSR_PERF_CNT(tran) _MMIO_TRANS2(tran, _SRD_PERF_CNT_A)
+#define EDP_PSR_PERF_CNT_MASK 0xffffff
+
+/* PSR_MASK on SKL+ */
+#define _SRD_DEBUG_A 0x60860
+#define _SRD_DEBUG_EDP 0x6f860
+#define EDP_PSR_DEBUG(tran) _MMIO_TRANS2(tran, _SRD_DEBUG_A)
+#define EDP_PSR_DEBUG_MASK_MAX_SLEEP (1 << 28)
+#define EDP_PSR_DEBUG_MASK_LPSP (1 << 27)
+#define EDP_PSR_DEBUG_MASK_MEMUP (1 << 26)
+#define EDP_PSR_DEBUG_MASK_HPD (1 << 25)
+#define EDP_PSR_DEBUG_MASK_DISP_REG_WRITE (1 << 16) /* Reserved in ICL+ */
+#define EDP_PSR_DEBUG_EXIT_ON_PIXEL_UNDERRUN (1 << 15) /* SKL+ */
+
+#define _PSR2_CTL_A 0x60900
+#define _PSR2_CTL_EDP 0x6f900
+#define EDP_PSR2_CTL(tran) _MMIO_TRANS2(tran, _PSR2_CTL_A)
+#define EDP_PSR2_ENABLE (1 << 31)
+#define EDP_SU_TRACK_ENABLE (1 << 30) /* up to adl-p */
+#define TGL_EDP_PSR2_BLOCK_COUNT_NUM_2 (0 << 28)
+#define TGL_EDP_PSR2_BLOCK_COUNT_NUM_3 (1 << 28)
+#define EDP_Y_COORDINATE_ENABLE REG_BIT(25) /* display 10, 11 and 12 */
+#define EDP_PSR2_SU_SDP_SCANLINE REG_BIT(25) /* display 13+ */
+#define EDP_MAX_SU_DISABLE_TIME(t) ((t) << 20)
+#define EDP_MAX_SU_DISABLE_TIME_MASK (0x1f << 20)
+#define EDP_PSR2_IO_BUFFER_WAKE_MAX_LINES 8
+#define EDP_PSR2_IO_BUFFER_WAKE(lines) ((EDP_PSR2_IO_BUFFER_WAKE_MAX_LINES - (lines)) << 13)
+#define EDP_PSR2_IO_BUFFER_WAKE_MASK (3 << 13)
+#define TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES 5
+#define TGL_EDP_PSR2_IO_BUFFER_WAKE_SHIFT 13
+#define TGL_EDP_PSR2_IO_BUFFER_WAKE(lines) (((lines) - TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES) << TGL_EDP_PSR2_IO_BUFFER_WAKE_SHIFT)
+#define TGL_EDP_PSR2_IO_BUFFER_WAKE_MASK (7 << 13)
+#define EDP_PSR2_FAST_WAKE_MAX_LINES 8
+#define EDP_PSR2_FAST_WAKE(lines) ((EDP_PSR2_FAST_WAKE_MAX_LINES - (lines)) << 11)
+#define EDP_PSR2_FAST_WAKE_MASK (3 << 11)
+#define TGL_EDP_PSR2_FAST_WAKE_MIN_LINES 5
+#define TGL_EDP_PSR2_FAST_WAKE_MIN_SHIFT 10
+#define TGL_EDP_PSR2_FAST_WAKE(lines) (((lines) - TGL_EDP_PSR2_FAST_WAKE_MIN_LINES) << TGL_EDP_PSR2_FAST_WAKE_MIN_SHIFT)
+#define TGL_EDP_PSR2_FAST_WAKE_MASK (7 << 10)
+#define EDP_PSR2_TP2_TIME_500us (0 << 8)
+#define EDP_PSR2_TP2_TIME_100us (1 << 8)
+#define EDP_PSR2_TP2_TIME_2500us (2 << 8)
+#define EDP_PSR2_TP2_TIME_50us (3 << 8)
+#define EDP_PSR2_TP2_TIME_MASK (3 << 8)
+#define EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4
+#define EDP_PSR2_FRAME_BEFORE_SU_MASK (0xf << 4)
+#define EDP_PSR2_FRAME_BEFORE_SU(a) ((a) << 4)
+#define EDP_PSR2_IDLE_FRAME_MASK 0xf
+#define EDP_PSR2_IDLE_FRAME_SHIFT 0
+
+#define _PSR_EVENT_TRANS_A 0x60848
+#define _PSR_EVENT_TRANS_B 0x61848
+#define _PSR_EVENT_TRANS_C 0x62848
+#define _PSR_EVENT_TRANS_D 0x63848
+#define _PSR_EVENT_TRANS_EDP 0x6f848
+#define PSR_EVENT(tran) _MMIO_TRANS2(tran, _PSR_EVENT_TRANS_A)
+#define PSR_EVENT_PSR2_WD_TIMER_EXPIRE (1 << 17)
+#define PSR_EVENT_PSR2_DISABLED (1 << 16)
+#define PSR_EVENT_SU_DIRTY_FIFO_UNDERRUN (1 << 15)
+#define PSR_EVENT_SU_CRC_FIFO_UNDERRUN (1 << 14)
+#define PSR_EVENT_GRAPHICS_RESET (1 << 12)
+#define PSR_EVENT_PCH_INTERRUPT (1 << 11)
+#define PSR_EVENT_MEMORY_UP (1 << 10)
+#define PSR_EVENT_FRONT_BUFFER_MODIFY (1 << 9)
+#define PSR_EVENT_WD_TIMER_EXPIRE (1 << 8)
+#define PSR_EVENT_PIPE_REGISTERS_UPDATE (1 << 6)
+#define PSR_EVENT_REGISTER_UPDATE (1 << 5) /* Reserved in ICL+ */
+#define PSR_EVENT_HDCP_ENABLE (1 << 4)
+#define PSR_EVENT_KVMR_SESSION_ENABLE (1 << 3)
+#define PSR_EVENT_VBI_ENABLE (1 << 2)
+#define PSR_EVENT_LPSP_MODE_EXIT (1 << 1)
+#define PSR_EVENT_PSR_DISABLE (1 << 0)
+
+#define _PSR2_STATUS_A 0x60940
+#define _PSR2_STATUS_EDP 0x6f940
+#define EDP_PSR2_STATUS(tran) _MMIO_TRANS2(tran, _PSR2_STATUS_A)
+#define EDP_PSR2_STATUS_STATE_MASK REG_GENMASK(31, 28)
+#define EDP_PSR2_STATUS_STATE_DEEP_SLEEP REG_FIELD_PREP(EDP_PSR2_STATUS_STATE_MASK, 0x8)
+
+#define _PSR2_SU_STATUS_A 0x60914
+#define _PSR2_SU_STATUS_EDP 0x6f914
+#define _PSR2_SU_STATUS(tran, index) _MMIO_TRANS2(tran, _PSR2_SU_STATUS_A + (index) * 4)
+#define PSR2_SU_STATUS(tran, frame) (_PSR2_SU_STATUS(tran, (frame) / 3))
+#define PSR2_SU_STATUS_SHIFT(frame) (((frame) % 3) * 10)
+#define PSR2_SU_STATUS_MASK(frame) (0x3ff << PSR2_SU_STATUS_SHIFT(frame))
+#define PSR2_SU_STATUS_FRAMES 8
+
+#define _PSR2_MAN_TRK_CTL_A 0x60910
+#define _PSR2_MAN_TRK_CTL_EDP 0x6f910
+#define PSR2_MAN_TRK_CTL(tran) _MMIO_TRANS2(tran, _PSR2_MAN_TRK_CTL_A)
+#define PSR2_MAN_TRK_CTL_ENABLE REG_BIT(31)
+#define PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR_MASK REG_GENMASK(30, 21)
+#define PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR(val) REG_FIELD_PREP(PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR_MASK, val)
+#define PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR_MASK REG_GENMASK(20, 11)
+#define PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR(val) REG_FIELD_PREP(PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR_MASK, val)
+#define PSR2_MAN_TRK_CTL_SF_SINGLE_FULL_FRAME REG_BIT(3)
+#define PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME REG_BIT(2)
+#define PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE REG_BIT(1)
+#define ADLP_PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR_MASK REG_GENMASK(28, 16)
+#define ADLP_PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR(val) REG_FIELD_PREP(ADLP_PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR_MASK, val)
+#define ADLP_PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR_MASK REG_GENMASK(12, 0)
+#define ADLP_PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR(val) REG_FIELD_PREP(ADLP_PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR_MASK, val)
+#define ADLP_PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE REG_BIT(31)
+#define ADLP_PSR2_MAN_TRK_CTL_SF_SINGLE_FULL_FRAME REG_BIT(14)
+#define ADLP_PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME REG_BIT(13)
+
+#define _SEL_FETCH_PLANE_BASE_1_A 0x70890
+#define _SEL_FETCH_PLANE_BASE_2_A 0x708B0
+#define _SEL_FETCH_PLANE_BASE_3_A 0x708D0
+#define _SEL_FETCH_PLANE_BASE_4_A 0x708F0
+#define _SEL_FETCH_PLANE_BASE_5_A 0x70920
+#define _SEL_FETCH_PLANE_BASE_6_A 0x70940
+#define _SEL_FETCH_PLANE_BASE_7_A 0x70960
+#define _SEL_FETCH_PLANE_BASE_CUR_A 0x70880
+#define _SEL_FETCH_PLANE_BASE_1_B 0x71890
+
+#define _SEL_FETCH_PLANE_BASE_A(plane) _PICK(plane, \
+ _SEL_FETCH_PLANE_BASE_1_A, \
+ _SEL_FETCH_PLANE_BASE_2_A, \
+ _SEL_FETCH_PLANE_BASE_3_A, \
+ _SEL_FETCH_PLANE_BASE_4_A, \
+ _SEL_FETCH_PLANE_BASE_5_A, \
+ _SEL_FETCH_PLANE_BASE_6_A, \
+ _SEL_FETCH_PLANE_BASE_7_A, \
+ _SEL_FETCH_PLANE_BASE_CUR_A)
+#define _SEL_FETCH_PLANE_BASE_1(pipe) _PIPE(pipe, _SEL_FETCH_PLANE_BASE_1_A, _SEL_FETCH_PLANE_BASE_1_B)
+#define _SEL_FETCH_PLANE_BASE(pipe, plane) (_SEL_FETCH_PLANE_BASE_1(pipe) - \
+ _SEL_FETCH_PLANE_BASE_1_A + \
+ _SEL_FETCH_PLANE_BASE_A(plane))
+
+#define _SEL_FETCH_PLANE_CTL_1_A 0x70890
+#define PLANE_SEL_FETCH_CTL(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \
+ _SEL_FETCH_PLANE_CTL_1_A - \
+ _SEL_FETCH_PLANE_BASE_1_A)
+#define PLANE_SEL_FETCH_CTL_ENABLE REG_BIT(31)
+
+#define _SEL_FETCH_PLANE_POS_1_A 0x70894
+#define PLANE_SEL_FETCH_POS(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \
+ _SEL_FETCH_PLANE_POS_1_A - \
+ _SEL_FETCH_PLANE_BASE_1_A)
+
+#define _SEL_FETCH_PLANE_SIZE_1_A 0x70898
+#define PLANE_SEL_FETCH_SIZE(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \
+ _SEL_FETCH_PLANE_SIZE_1_A - \
+ _SEL_FETCH_PLANE_BASE_1_A)
+
+#define _SEL_FETCH_PLANE_OFFSET_1_A 0x7089C
+#define PLANE_SEL_FETCH_OFFSET(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \
+ _SEL_FETCH_PLANE_OFFSET_1_A - \
+ _SEL_FETCH_PLANE_BASE_1_A)
+
+#endif /* __INTEL_PSR_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_qp_tables.c b/drivers/gpu/drm/i915/display/intel_qp_tables.c
index 6f8e4ec5c0fb..6e86c0971d24 100644
--- a/drivers/gpu/drm/i915/display/intel_qp_tables.c
+++ b/drivers/gpu/drm/i915/display/intel_qp_tables.c
@@ -17,6 +17,15 @@
/* from BPP 6 to 36 in steps of 0.5 */
#define RC_RANGE_QP444_12BPC_MAX_NUM_BPP 61
+/* from BPP 6 to 24 in steps of 0.5 */
+#define RC_RANGE_QP420_8BPC_MAX_NUM_BPP 17
+
+/* from BPP 6 to 30 in steps of 0.5 */
+#define RC_RANGE_QP420_10BPC_MAX_NUM_BPP 23
+
+/* from BPP 6 to 36 in steps of 0.5 */
+#define RC_RANGE_QP420_12BPC_MAX_NUM_BPP 29
+
/*
* These qp tables are as per the C model
* and it has the rows pointing to bpps which increment
@@ -283,26 +292,182 @@ static const u8 rc_range_maxqp444_12bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP444_12BPC
11, 11, 10, 10, 10, 10, 10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 6, 6, 6, 6, 5, 5, 4 }
};
-#define PARAM_TABLE(_minmax, _bpc, _row, _col) do { \
- if (bpc == (_bpc)) \
- return rc_range_##_minmax##qp444_##_bpc##bpc[_row][_col]; \
+static const u8 rc_range_minqp420_8bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP420_8BPC_MAX_NUM_BPP] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 3, 3, 3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ { 3, 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 3, 3, 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ { 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0 },
+ { 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0 },
+ { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1, 1 },
+ { 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 1, 1 },
+ { 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, 2, 2, 1 },
+ { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 3, 3, 2, 1 },
+ { 9, 8, 8, 7, 7, 7, 7, 7, 7, 6, 5, 5, 4, 3, 3, 3, 2 },
+ { 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 6, 6, 5, 5, 4, 4, 3 }
+};
+
+static const u8 rc_range_maxqp420_8bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP420_8BPC_MAX_NUM_BPP] = {
+ { 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 4, 4, 4, 4, 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 5, 5, 5, 5, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 6, 6, 6, 6, 6, 5, 4, 3, 2, 2, 2, 1, 1, 1, 1, 0, 0 },
+ { 7, 7, 7, 7, 7, 5, 4, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0 },
+ { 7, 7, 7, 7, 7, 6, 5, 4, 3, 3, 3, 2, 2, 2, 1, 1, 0 },
+ { 7, 7, 7, 7, 7, 6, 5, 4, 3, 3, 3, 3, 2, 2, 2, 1, 1 },
+ { 8, 8, 8, 8, 8, 7, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1 },
+ { 9, 9, 9, 8, 8, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1 },
+ { 10, 10, 9, 9, 9, 8, 7, 6, 5, 5, 5, 4, 4, 3, 3, 2, 2 },
+ { 10, 10, 10, 9, 9, 8, 8, 7, 6, 6, 5, 5, 4, 4, 3, 2, 2 },
+ { 11, 11, 10, 10, 9, 9, 8, 7, 7, 6, 6, 5, 5, 4, 3, 3, 2 },
+ { 11, 11, 11, 10, 9, 9, 9, 8, 7, 7, 6, 5, 5, 4, 4, 3, 2 },
+ { 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 6, 6, 5, 4, 4, 4, 3 },
+ { 14, 13, 13, 12, 11, 11, 10, 9, 9, 8, 7, 7, 6, 6, 5, 5, 4 }
+};
+
+static const u8 rc_range_minqp420_10bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP420_10BPC_MAX_NUM_BPP] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 4, 4, 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 7, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0 },
+ { 7, 7, 7, 7, 7, 6, 5, 5, 5, 5, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 0, 0 },
+ { 7, 7, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 3, 2, 2, 2, 2, 1, 1, 1, 0 },
+ { 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 4, 4, 4, 3, 2, 2, 2, 1, 1, 1, 0 },
+ { 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 2, 1, 1 },
+ { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1 },
+ { 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1 },
+ { 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 4, 4, 3, 3, 2, 1 },
+ { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 7, 7, 6, 5, 4, 4, 3, 3, 2, 1 },
+ { 13, 12, 12, 11, 11, 11, 11, 11, 11, 10, 9, 9, 8, 7, 7, 6, 5, 5, 4, 3, 3,
+ 2, 2 },
+ { 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 10, 9, 8, 8, 7, 6, 6, 5,
+ 5, 4, 4 }
+};
+
+static const u8 rc_range_maxqp420_10bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP420_10BPC_MAX_NUM_BPP] = {
+ { 8, 8, 7, 6, 4, 4, 3, 3, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 8, 8, 8, 7, 6, 5, 4, 4, 3, 3, 3, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 9, 9, 9, 8, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0 },
+ { 10, 10, 10, 9, 9, 8, 7, 6, 5, 4, 4, 3, 3, 3, 3, 2, 1, 1, 1, 1, 1, 0,
+ 0 },
+ { 11, 11, 11, 10, 10, 8, 7, 6, 5, 4, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1,
+ 0 },
+ { 11, 11, 11, 10, 10, 9, 8, 7, 6, 6, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 1,
+ 1 },
+ { 11, 11, 11, 11, 11, 10, 9, 8, 7, 7, 7, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2,
+ 1 },
+ { 12, 12, 12, 12, 12, 11, 10, 9, 8, 8, 8, 7, 6, 5, 5, 4, 3, 3, 3, 2, 2,
+ 2, 1 },
+ { 13, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 5, 4, 4, 3, 3, 3,
+ 2, 2 },
+ { 14, 14, 13, 13, 13, 12, 11, 10, 9, 9, 9, 8, 8, 7, 7, 6, 5, 4, 4, 3, 3,
+ 2, 2 },
+ { 14, 14, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 6, 5, 5, 4, 4,
+ 3, 3, 2 },
+ { 15, 15, 14, 14, 13, 13, 12, 11, 11, 10, 10, 9, 9, 8, 7, 7, 6, 5, 5, 4,
+ 4, 3, 2 },
+ { 15, 15, 15, 14, 13, 13, 13, 12, 11, 11, 10, 9, 9, 8, 8, 7, 6, 5, 5, 4,
+ 4, 3, 2 },
+ { 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 6, 6, 5, 4,
+ 4, 3, 3 },
+ { 18, 17, 17, 16, 15, 15, 14, 13, 13, 12, 11, 11, 11, 10, 9, 9, 8, 7, 7,
+ 6, 6, 5, 5 }
+};
+
+static const u8 rc_range_minqp420_12bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP420_12BPC_MAX_NUM_BPP] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ { 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ { 9, 8, 8, 7, 7, 6, 5, 5, 4, 4, 4, 4, 3, 3, 3, 2, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ { 10, 9, 9, 8, 8, 8, 7, 7, 6, 6, 6, 5, 5, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0 },
+ { 11, 10, 10, 10, 10, 9, 9, 8, 7, 6, 6, 6, 6, 5, 5, 4, 3, 3, 3, 2, 2, 1,
+ 0, 0, 0, 0, 0, 0, 0 },
+ { 11, 11, 11, 11, 11, 10, 10, 9, 9, 9, 9, 8, 7, 6, 5, 5, 4, 4, 3, 3, 3, 2,
+ 1, 1, 0, 0, 0, 0, 0 },
+ { 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 9, 8, 8, 7, 6, 5, 5, 5, 5, 4, 3, 3,
+ 2, 1, 1, 1, 1, 1, 0 },
+ { 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 8, 8, 8, 7, 6, 6, 5, 4, 4,
+ 3, 2, 2, 1, 1, 1, 1, 1 },
+ { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 5,
+ 5, 4, 4, 2, 2, 1, 1, 1, 1 },
+ { 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6,
+ 5, 4, 4, 3, 2, 2, 1, 1, 1 },
+ { 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7,
+ 6, 5, 4, 3, 3, 2, 2, 1, 1 },
+ { 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 11, 10, 10, 9, 8, 8,
+ 7, 7, 6, 5, 4, 3, 3, 2, 2, 1 },
+ { 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 11, 11, 10, 9, 8, 8,
+ 7, 7, 6, 5, 4, 4, 3, 2, 2, 1 },
+ { 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 13, 13, 12, 11, 11, 10, 9, 9, 8,
+ 8, 7, 6, 6, 5, 4, 4, 3, 3, 2 },
+ { 21, 20, 20, 19, 18, 18, 17, 16, 16, 15, 14, 14, 14, 13, 12, 12, 11, 10,
+ 10, 10, 9, 8, 8, 7, 6, 6, 5, 5, 4 }
+};
+
+static const u8 rc_range_maxqp420_12bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP420_12BPC_MAX_NUM_BPP] = {
+ { 11, 10, 9, 8, 6, 6, 5, 5, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0 },
+ { 12, 11, 11, 10, 9, 8, 7, 7, 6, 6, 5, 5, 4, 3, 3, 2, 1, 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 0, 0, 0 },
+ { 13, 12, 12, 11, 11, 10, 9, 8, 7, 6, 6, 6, 5, 5, 4, 3, 3, 2, 1, 1, 1, 1,
+ 1, 0, 0, 0, 0, 0, 0 },
+ { 14, 13, 13, 12, 12, 11, 10, 9, 8, 7, 7, 6, 6, 5, 5, 4, 3, 3, 2, 2, 2, 1,
+ 1, 1, 0, 0, 0, 0, 0 },
+ { 15, 14, 14, 13, 13, 11, 10, 9, 8, 7, 7, 7, 7, 6, 6, 5, 4, 4, 4, 3, 3, 2,
+ 1, 1, 1, 0, 0, 0, 0 },
+ { 15, 15, 15, 14, 14, 13, 12, 11, 10, 10, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4,
+ 4, 3, 2, 2, 1, 1, 0, 0, 0 },
+ { 15, 15, 15, 15, 15, 14, 13, 12, 11, 11, 11, 10, 9, 8, 7, 6, 6, 6, 6, 5,
+ 4, 4, 3, 2, 2, 2, 1, 1, 0 },
+ { 16, 16, 16, 16, 16, 15, 14, 13, 12, 12, 12, 11, 10, 9, 9, 8, 7, 7, 6, 5,
+ 5, 4, 3, 3, 2, 2, 2, 1, 1 },
+ { 17, 17, 17, 16, 16, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 9, 8, 8, 7,
+ 6, 6, 5, 5, 3, 3, 2, 2, 1, 1 },
+ { 18, 18, 17, 17, 17, 16, 15, 14, 13, 13, 13, 12, 12, 11, 11, 10, 9, 8, 8,
+ 7, 6, 5, 5, 4, 3, 3, 2, 2, 1 },
+ { 18, 18, 18, 17, 17, 16, 16, 15, 14, 14, 13, 13, 12, 12, 11, 10, 9, 9, 8,
+ 8, 7, 6, 5, 4, 4, 3, 3, 2, 2 },
+ { 19, 19, 18, 18, 17, 17, 16, 15, 15, 14, 14, 13, 13, 12, 11, 11, 10, 9,
+ 9, 8, 8, 7, 6, 5, 4, 4, 3, 3, 2 },
+ { 19, 19, 19, 18, 17, 17, 17, 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 9,
+ 9, 8, 8, 7, 6, 5, 5, 4, 3, 3, 2 },
+ { 21, 20, 20, 19, 18, 18, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10,
+ 9, 9, 8, 7, 7, 6, 5, 5, 4, 4, 3 },
+ { 22, 21, 21, 20, 19, 19, 18, 17, 17, 16, 15, 15, 15, 14, 13, 13, 12, 11,
+ 11, 11, 10, 9, 9, 8, 7, 7, 6, 6, 5 }
+};
+
+#define PARAM_TABLE(_minmax, _bpc, _row, _col, _is_420) do { \
+ if (bpc == (_bpc)) { \
+ if (_is_420) \
+ return rc_range_##_minmax##qp420_##_bpc##bpc[_row][_col]; \
+ else \
+ return rc_range_##_minmax##qp444_##_bpc##bpc[_row][_col]; \
+ } \
} while (0)
-u8 intel_lookup_range_min_qp(int bpc, int buf_i, int bpp_i)
+u8 intel_lookup_range_min_qp(int bpc, int buf_i, int bpp_i, bool is_420)
{
- PARAM_TABLE(min, 8, buf_i, bpp_i);
- PARAM_TABLE(min, 10, buf_i, bpp_i);
- PARAM_TABLE(min, 12, buf_i, bpp_i);
+ PARAM_TABLE(min, 8, buf_i, bpp_i, is_420);
+ PARAM_TABLE(min, 10, buf_i, bpp_i, is_420);
+ PARAM_TABLE(min, 12, buf_i, bpp_i, is_420);
MISSING_CASE(bpc);
return 0;
}
-u8 intel_lookup_range_max_qp(int bpc, int buf_i, int bpp_i)
+u8 intel_lookup_range_max_qp(int bpc, int buf_i, int bpp_i, bool is_420)
{
- PARAM_TABLE(max, 8, buf_i, bpp_i);
- PARAM_TABLE(max, 10, buf_i, bpp_i);
- PARAM_TABLE(max, 12, buf_i, bpp_i);
+ PARAM_TABLE(max, 8, buf_i, bpp_i, is_420);
+ PARAM_TABLE(max, 10, buf_i, bpp_i, is_420);
+ PARAM_TABLE(max, 12, buf_i, bpp_i, is_420);
MISSING_CASE(bpc);
return 0;
diff --git a/drivers/gpu/drm/i915/display/intel_qp_tables.h b/drivers/gpu/drm/i915/display/intel_qp_tables.h
index 9fb3c36bd7c6..a9ff9ca29938 100644
--- a/drivers/gpu/drm/i915/display/intel_qp_tables.h
+++ b/drivers/gpu/drm/i915/display/intel_qp_tables.h
@@ -8,7 +8,7 @@
#include <linux/types.h>
-u8 intel_lookup_range_min_qp(int bpc, int buf_i, int bpp_i);
-u8 intel_lookup_range_max_qp(int bpc, int buf_i, int bpp_i);
+u8 intel_lookup_range_min_qp(int bpc, int buf_i, int bpp_i, bool is_420);
+u8 intel_lookup_range_max_qp(int bpc, int buf_i, int bpp_i, bool is_420);
#endif
diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c
index a16e56a60c30..25034bbf1445 100644
--- a/drivers/gpu/drm/i915/display/intel_sprite.c
+++ b/drivers/gpu/drm/i915/display/intel_sprite.c
@@ -32,85 +32,20 @@
#include <linux/string_helpers.h>
-#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_color_mgmt.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_rect.h>
#include "i915_drv.h"
#include "i915_reg.h"
-#include "i915_vgpu.h"
#include "i9xx_plane.h"
#include "intel_atomic_plane.h"
-#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_fb.h"
-#include "intel_frontbuffer.h"
#include "intel_sprite.h"
-#include "intel_vrr.h"
-
-int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state)
-{
- struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev);
- const struct drm_framebuffer *fb = plane_state->hw.fb;
- struct drm_rect *src = &plane_state->uapi.src;
- u32 src_x, src_y, src_w, src_h, hsub, vsub;
- bool rotated = drm_rotation_90_or_270(plane_state->hw.rotation);
-
- /*
- * FIXME hsub/vsub vs. block size is a mess. Pre-tgl CCS
- * abuses hsub/vsub so we can't use them here. But as they
- * are limited to 32bpp RGB formats we don't actually need
- * to check anything.
- */
- if (fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
- fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS)
- return 0;
-
- /*
- * Hardware doesn't handle subpixel coordinates.
- * Adjust to (macro)pixel boundary, but be careful not to
- * increase the source viewport size, because that could
- * push the downscaling factor out of bounds.
- */
- src_x = src->x1 >> 16;
- src_w = drm_rect_width(src) >> 16;
- src_y = src->y1 >> 16;
- src_h = drm_rect_height(src) >> 16;
-
- drm_rect_init(src, src_x << 16, src_y << 16,
- src_w << 16, src_h << 16);
-
- if (fb->format->format == DRM_FORMAT_RGB565 && rotated) {
- hsub = 2;
- vsub = 2;
- } else {
- hsub = fb->format->hsub;
- vsub = fb->format->vsub;
- }
-
- if (rotated)
- hsub = vsub = max(hsub, vsub);
-
- if (src_x % hsub || src_w % hsub) {
- drm_dbg_kms(&i915->drm, "src x/w (%u, %u) must be a multiple of %u (rotated: %s)\n",
- src_x, src_w, hsub, str_yes_no(rotated));
- return -EINVAL;
- }
-
- if (src_y % vsub || src_h % vsub) {
- drm_dbg_kms(&i915->drm, "src y/h (%u, %u) must be a multiple of %u (rotated: %s)\n",
- src_y, src_h, vsub, str_yes_no(rotated));
- return -EINVAL;
- }
-
- return 0;
-}
static void i9xx_plane_linear_gamma(u16 gamma[8])
{
@@ -1449,124 +1384,6 @@ vlv_sprite_check(struct intel_crtc_state *crtc_state,
return 0;
}
-static bool has_dst_key_in_primary_plane(struct drm_i915_private *dev_priv)
-{
- return DISPLAY_VER(dev_priv) >= 9;
-}
-
-static void intel_plane_set_ckey(struct intel_plane_state *plane_state,
- const struct drm_intel_sprite_colorkey *set)
-{
- struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
- struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
- struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
-
- *key = *set;
-
- /*
- * We want src key enabled on the
- * sprite and not on the primary.
- */
- if (plane->id == PLANE_PRIMARY &&
- set->flags & I915_SET_COLORKEY_SOURCE)
- key->flags = 0;
-
- /*
- * On SKL+ we want dst key enabled on
- * the primary and not on the sprite.
- */
- if (DISPLAY_VER(dev_priv) >= 9 && plane->id != PLANE_PRIMARY &&
- set->flags & I915_SET_COLORKEY_DESTINATION)
- key->flags = 0;
-}
-
-int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- struct drm_i915_private *dev_priv = to_i915(dev);
- struct drm_intel_sprite_colorkey *set = data;
- struct drm_plane *plane;
- struct drm_plane_state *plane_state;
- struct drm_atomic_state *state;
- struct drm_modeset_acquire_ctx ctx;
- int ret = 0;
-
- /* ignore the pointless "none" flag */
- set->flags &= ~I915_SET_COLORKEY_NONE;
-
- if (set->flags & ~(I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))
- return -EINVAL;
-
- /* Make sure we don't try to enable both src & dest simultaneously */
- if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))
- return -EINVAL;
-
- if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) &&
- set->flags & I915_SET_COLORKEY_DESTINATION)
- return -EINVAL;
-
- plane = drm_plane_find(dev, file_priv, set->plane_id);
- if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY)
- return -ENOENT;
-
- /*
- * SKL+ only plane 2 can do destination keying against plane 1.
- * Also multiple planes can't do destination keying on the same
- * pipe simultaneously.
- */
- if (DISPLAY_VER(dev_priv) >= 9 &&
- to_intel_plane(plane)->id >= PLANE_SPRITE1 &&
- set->flags & I915_SET_COLORKEY_DESTINATION)
- return -EINVAL;
-
- drm_modeset_acquire_init(&ctx, 0);
-
- state = drm_atomic_state_alloc(plane->dev);
- if (!state) {
- ret = -ENOMEM;
- goto out;
- }
- state->acquire_ctx = &ctx;
-
- while (1) {
- plane_state = drm_atomic_get_plane_state(state, plane);
- ret = PTR_ERR_OR_ZERO(plane_state);
- if (!ret)
- intel_plane_set_ckey(to_intel_plane_state(plane_state), set);
-
- /*
- * On some platforms we have to configure
- * the dst colorkey on the primary plane.
- */
- if (!ret && has_dst_key_in_primary_plane(dev_priv)) {
- struct intel_crtc *crtc =
- intel_crtc_for_pipe(dev_priv,
- to_intel_plane(plane)->pipe);
-
- plane_state = drm_atomic_get_plane_state(state,
- crtc->base.primary);
- ret = PTR_ERR_OR_ZERO(plane_state);
- if (!ret)
- intel_plane_set_ckey(to_intel_plane_state(plane_state), set);
- }
-
- if (!ret)
- ret = drm_atomic_commit(state);
-
- if (ret != -EDEADLK)
- break;
-
- drm_atomic_state_clear(state);
- drm_modeset_backoff(&ctx);
- }
-
- drm_atomic_state_put(state);
-out:
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- return ret;
-}
-
static const u32 g4x_sprite_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_YUYV,
diff --git a/drivers/gpu/drm/i915/display/intel_sprite_uapi.c b/drivers/gpu/drm/i915/display/intel_sprite_uapi.c
new file mode 100644
index 000000000000..70a391083751
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_sprite_uapi.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#include "i915_drv.h"
+#include "intel_crtc.h"
+#include "intel_display_types.h"
+#include "intel_sprite_uapi.h"
+
+static bool has_dst_key_in_primary_plane(struct drm_i915_private *dev_priv)
+{
+ return DISPLAY_VER(dev_priv) >= 9;
+}
+
+static void intel_plane_set_ckey(struct intel_plane_state *plane_state,
+ const struct drm_intel_sprite_colorkey *set)
+{
+ struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
+
+ *key = *set;
+
+ /*
+ * We want src key enabled on the
+ * sprite and not on the primary.
+ */
+ if (plane->id == PLANE_PRIMARY &&
+ set->flags & I915_SET_COLORKEY_SOURCE)
+ key->flags = 0;
+
+ /*
+ * On SKL+ we want dst key enabled on
+ * the primary and not on the sprite.
+ */
+ if (DISPLAY_VER(dev_priv) >= 9 && plane->id != PLANE_PRIMARY &&
+ set->flags & I915_SET_COLORKEY_DESTINATION)
+ key->flags = 0;
+}
+
+int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ struct drm_intel_sprite_colorkey *set = data;
+ struct drm_plane *plane;
+ struct drm_plane_state *plane_state;
+ struct drm_atomic_state *state;
+ struct drm_modeset_acquire_ctx ctx;
+ int ret = 0;
+
+ /* ignore the pointless "none" flag */
+ set->flags &= ~I915_SET_COLORKEY_NONE;
+
+ if (set->flags & ~(I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))
+ return -EINVAL;
+
+ /* Make sure we don't try to enable both src & dest simultaneously */
+ if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))
+ return -EINVAL;
+
+ if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) &&
+ set->flags & I915_SET_COLORKEY_DESTINATION)
+ return -EINVAL;
+
+ plane = drm_plane_find(dev, file_priv, set->plane_id);
+ if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY)
+ return -ENOENT;
+
+ /*
+ * SKL+ only plane 2 can do destination keying against plane 1.
+ * Also multiple planes can't do destination keying on the same
+ * pipe simultaneously.
+ */
+ if (DISPLAY_VER(dev_priv) >= 9 &&
+ to_intel_plane(plane)->id >= PLANE_SPRITE1 &&
+ set->flags & I915_SET_COLORKEY_DESTINATION)
+ return -EINVAL;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_atomic_state_alloc(plane->dev);
+ if (!state) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ state->acquire_ctx = &ctx;
+
+ while (1) {
+ plane_state = drm_atomic_get_plane_state(state, plane);
+ ret = PTR_ERR_OR_ZERO(plane_state);
+ if (!ret)
+ intel_plane_set_ckey(to_intel_plane_state(plane_state), set);
+
+ /*
+ * On some platforms we have to configure
+ * the dst colorkey on the primary plane.
+ */
+ if (!ret && has_dst_key_in_primary_plane(dev_priv)) {
+ struct intel_crtc *crtc =
+ intel_crtc_for_pipe(dev_priv,
+ to_intel_plane(plane)->pipe);
+
+ plane_state = drm_atomic_get_plane_state(state,
+ crtc->base.primary);
+ ret = PTR_ERR_OR_ZERO(plane_state);
+ if (!ret)
+ intel_plane_set_ckey(to_intel_plane_state(plane_state), set);
+ }
+
+ if (!ret)
+ ret = drm_atomic_commit(state);
+
+ if (ret != -EDEADLK)
+ break;
+
+ drm_atomic_state_clear(state);
+ drm_modeset_backoff(&ctx);
+ }
+
+ drm_atomic_state_put(state);
+out:
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+ return ret;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_sprite_uapi.h b/drivers/gpu/drm/i915/display/intel_sprite_uapi.h
new file mode 100644
index 000000000000..3eb50025acaf
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_sprite_uapi.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_SPRITE_UAPI_H__
+#define __INTEL_SPRITE_UAPI_H__
+
+struct drm_device;
+struct drm_file;
+
+int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+#endif /* __INTEL_SPRITE_UAPI_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c
index f45328712bff..3b60995e9dfb 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.c
+++ b/drivers/gpu/drm/i915/display/intel_tc.c
@@ -5,6 +5,7 @@
#include "i915_drv.h"
#include "i915_reg.h"
+#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display.h"
#include "intel_display_power_map.h"
@@ -14,6 +15,52 @@
#include "intel_mg_phy_regs.h"
#include "intel_tc.h"
+enum tc_port_mode {
+ TC_PORT_DISCONNECTED,
+ TC_PORT_TBT_ALT,
+ TC_PORT_DP_ALT,
+ TC_PORT_LEGACY,
+};
+
+struct intel_tc_port;
+
+struct intel_tc_phy_ops {
+ enum intel_display_power_domain (*cold_off_domain)(struct intel_tc_port *tc);
+ u32 (*hpd_live_status)(struct intel_tc_port *tc);
+ bool (*is_ready)(struct intel_tc_port *tc);
+ bool (*is_owned)(struct intel_tc_port *tc);
+ void (*get_hw_state)(struct intel_tc_port *tc);
+ bool (*connect)(struct intel_tc_port *tc, int required_lanes);
+ void (*disconnect)(struct intel_tc_port *tc);
+ void (*init)(struct intel_tc_port *tc);
+};
+
+struct intel_tc_port {
+ struct intel_digital_port *dig_port;
+
+ const struct intel_tc_phy_ops *phy_ops;
+
+ struct mutex lock; /* protects the TypeC port mode */
+ intel_wakeref_t lock_wakeref;
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)
+ enum intel_display_power_domain lock_power_domain;
+#endif
+ struct delayed_work disconnect_phy_work;
+ int link_refcount;
+ bool legacy_port:1;
+ char port_name[8];
+ enum tc_port_mode mode;
+ enum tc_port_mode init_mode;
+ enum phy_fia phy_fia;
+ u8 phy_fia_idx;
+};
+
+static enum intel_display_power_domain
+tc_phy_cold_off_domain(struct intel_tc_port *);
+static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc);
+static bool tc_phy_is_ready(struct intel_tc_port *tc);
+static enum tc_port_mode tc_phy_get_current_mode(struct intel_tc_port *tc);
+
static const char *tc_port_mode_name(enum tc_port_mode mode)
{
static const char * const names[] = {
@@ -29,13 +76,24 @@ static const char *tc_port_mode_name(enum tc_port_mode mode)
return names[mode];
}
+static struct intel_tc_port *to_tc_port(struct intel_digital_port *dig_port)
+{
+ return dig_port->tc;
+}
+
+static struct drm_i915_private *tc_to_i915(struct intel_tc_port *tc)
+{
+ return to_i915(tc->dig_port->base.base.dev);
+}
+
static bool intel_tc_port_in_mode(struct intel_digital_port *dig_port,
enum tc_port_mode mode)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
enum phy phy = intel_port_to_phy(i915, dig_port->base.port);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
- return intel_phy_is_tc(i915, phy) && dig_port->tc_mode == mode;
+ return intel_phy_is_tc(i915, phy) && tc->mode == mode;
}
bool intel_tc_port_in_tbt_alt_mode(struct intel_digital_port *dig_port)
@@ -53,109 +111,178 @@ bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port)
return intel_tc_port_in_mode(dig_port, TC_PORT_LEGACY);
}
+/*
+ * The display power domains used for TC ports depending on the
+ * platform and TC mode (legacy, DP-alt, TBT):
+ *
+ * POWER_DOMAIN_DISPLAY_CORE:
+ * --------------------------
+ * ADLP/all modes:
+ * - TCSS/IOM access for PHY ready state.
+ * ADLP+/all modes:
+ * - DE/north-,south-HPD ISR access for HPD live state.
+ *
+ * POWER_DOMAIN_PORT_DDI_LANES_<port>:
+ * -----------------------------------
+ * ICL+/all modes:
+ * - DE/DDI_BUF access for port enabled state.
+ * ADLP/all modes:
+ * - DE/DDI_BUF access for PHY owned state.
+ *
+ * POWER_DOMAIN_AUX_USBC<TC port index>:
+ * -------------------------------------
+ * ICL/legacy mode:
+ * - TCSS/IOM,FIA access for PHY ready, owned and HPD live state
+ * - TCSS/PHY: block TC-cold power state for using the PHY AUX and
+ * main lanes.
+ * ADLP/legacy, DP-alt modes:
+ * - TCSS/PHY: block TC-cold power state for using the PHY AUX and
+ * main lanes.
+ *
+ * POWER_DOMAIN_TC_COLD_OFF:
+ * -------------------------
+ * TGL/legacy, DP-alt modes:
+ * - TCSS/IOM,FIA access for PHY ready, owned and HPD live state
+ * - TCSS/PHY: block TC-cold power state for using the PHY AUX and
+ * main lanes.
+ *
+ * ICL, TGL, ADLP/TBT mode:
+ * - TCSS/IOM,FIA access for HPD live state
+ * - TCSS/TBT: block TC-cold power state for using the (TBT DP-IN)
+ * AUX and main lanes.
+ */
bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
- return (DISPLAY_VER(i915) == 11 && dig_port->tc_legacy_port) ||
- IS_ALDERLAKE_P(i915);
+ return tc_phy_cold_off_domain(tc) ==
+ intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch);
}
-static enum intel_display_power_domain
-tc_cold_get_power_domain(struct intel_digital_port *dig_port, enum tc_port_mode mode)
+static intel_wakeref_t
+__tc_cold_block(struct intel_tc_port *tc, enum intel_display_power_domain *domain)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
- if (mode == TC_PORT_TBT_ALT || !intel_tc_cold_requires_aux_pw(dig_port))
- return POWER_DOMAIN_TC_COLD_OFF;
+ *domain = tc_phy_cold_off_domain(tc);
- return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch);
+ return intel_display_power_get(i915, *domain);
}
static intel_wakeref_t
-tc_cold_block_in_mode(struct intel_digital_port *dig_port, enum tc_port_mode mode,
- enum intel_display_power_domain *domain)
+tc_cold_block(struct intel_tc_port *tc)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-
- *domain = tc_cold_get_power_domain(dig_port, mode);
+ enum intel_display_power_domain domain;
+ intel_wakeref_t wakeref;
- return intel_display_power_get(i915, *domain);
+ wakeref = __tc_cold_block(tc, &domain);
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)
+ tc->lock_power_domain = domain;
+#endif
+ return wakeref;
}
-static intel_wakeref_t
-tc_cold_block(struct intel_digital_port *dig_port, enum intel_display_power_domain *domain)
+static void
+__tc_cold_unblock(struct intel_tc_port *tc, enum intel_display_power_domain domain,
+ intel_wakeref_t wakeref)
{
- return tc_cold_block_in_mode(dig_port, dig_port->tc_mode, domain);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+
+ intel_display_power_put(i915, domain, wakeref);
}
static void
-tc_cold_unblock(struct intel_digital_port *dig_port, enum intel_display_power_domain domain,
- intel_wakeref_t wakeref)
+tc_cold_unblock(struct intel_tc_port *tc, intel_wakeref_t wakeref)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ enum intel_display_power_domain domain = tc_phy_cold_off_domain(tc);
- /*
- * wakeref == -1, means some error happened saving save_depot_stack but
- * power should still be put down and 0 is a invalid save_depot_stack
- * id so can be used to skip it for non TC legacy ports.
- */
- if (wakeref == 0)
- return;
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)
+ drm_WARN_ON(&tc_to_i915(tc)->drm, tc->lock_power_domain != domain);
+#endif
+ __tc_cold_unblock(tc, domain, wakeref);
+}
- intel_display_power_put(i915, domain, wakeref);
+static void
+assert_display_core_power_enabled(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+
+ drm_WARN_ON(&i915->drm,
+ !intel_display_power_is_enabled(i915, POWER_DOMAIN_DISPLAY_CORE));
}
static void
-assert_tc_cold_blocked(struct intel_digital_port *dig_port)
+assert_tc_cold_blocked(struct intel_tc_port *tc)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
bool enabled;
enabled = intel_display_power_is_enabled(i915,
- tc_cold_get_power_domain(dig_port,
- dig_port->tc_mode));
+ tc_phy_cold_off_domain(tc));
drm_WARN_ON(&i915->drm, !enabled);
}
+static enum intel_display_power_domain
+tc_port_power_domain(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum tc_port tc_port = intel_port_to_tc(i915, tc->dig_port->base.port);
+
+ return POWER_DOMAIN_PORT_DDI_LANES_TC1 + tc_port - TC_PORT_1;
+}
+
+static void
+assert_tc_port_power_enabled(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+
+ drm_WARN_ON(&i915->drm,
+ !intel_display_power_is_enabled(i915, tc_port_power_domain(tc)));
+}
+
u32 intel_tc_port_get_lane_mask(struct intel_digital_port *dig_port)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
u32 lane_mask;
- lane_mask = intel_de_read(i915, PORT_TX_DFLEXDPSP(dig_port->tc_phy_fia));
+ lane_mask = intel_de_read(i915, PORT_TX_DFLEXDPSP(tc->phy_fia));
drm_WARN_ON(&i915->drm, lane_mask == 0xffffffff);
- assert_tc_cold_blocked(dig_port);
+ assert_tc_cold_blocked(tc);
- lane_mask &= DP_LANE_ASSIGNMENT_MASK(dig_port->tc_phy_fia_idx);
- return lane_mask >> DP_LANE_ASSIGNMENT_SHIFT(dig_port->tc_phy_fia_idx);
+ lane_mask &= DP_LANE_ASSIGNMENT_MASK(tc->phy_fia_idx);
+ return lane_mask >> DP_LANE_ASSIGNMENT_SHIFT(tc->phy_fia_idx);
}
u32 intel_tc_port_get_pin_assignment_mask(struct intel_digital_port *dig_port)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
u32 pin_mask;
- pin_mask = intel_de_read(i915, PORT_TX_DFLEXPA1(dig_port->tc_phy_fia));
+ pin_mask = intel_de_read(i915, PORT_TX_DFLEXPA1(tc->phy_fia));
drm_WARN_ON(&i915->drm, pin_mask == 0xffffffff);
- assert_tc_cold_blocked(dig_port);
+ assert_tc_cold_blocked(tc);
- return (pin_mask & DP_PIN_ASSIGNMENT_MASK(dig_port->tc_phy_fia_idx)) >>
- DP_PIN_ASSIGNMENT_SHIFT(dig_port->tc_phy_fia_idx);
+ return (pin_mask & DP_PIN_ASSIGNMENT_MASK(tc->phy_fia_idx)) >>
+ DP_PIN_ASSIGNMENT_SHIFT(tc->phy_fia_idx);
}
int intel_tc_port_fia_max_lane_count(struct intel_digital_port *dig_port)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+ enum phy phy = intel_port_to_phy(i915, dig_port->base.port);
intel_wakeref_t wakeref;
u32 lane_mask;
- if (dig_port->tc_mode != TC_PORT_DP_ALT)
+ if (!intel_phy_is_tc(i915, phy) || tc->mode != TC_PORT_DP_ALT)
return 4;
- assert_tc_cold_blocked(dig_port);
+ assert_tc_cold_blocked(tc);
lane_mask = 0;
with_intel_display_power(i915, POWER_DOMAIN_DISPLAY_CORE, wakeref)
@@ -182,45 +309,51 @@ void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port,
int required_lanes)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL;
u32 val;
drm_WARN_ON(&i915->drm,
- lane_reversal && dig_port->tc_mode != TC_PORT_LEGACY);
+ lane_reversal && tc->mode != TC_PORT_LEGACY);
- assert_tc_cold_blocked(dig_port);
+ assert_tc_cold_blocked(tc);
- val = intel_de_read(i915, PORT_TX_DFLEXDPMLE1(dig_port->tc_phy_fia));
- val &= ~DFLEXDPMLE1_DPMLETC_MASK(dig_port->tc_phy_fia_idx);
+ val = intel_de_read(i915, PORT_TX_DFLEXDPMLE1(tc->phy_fia));
+ val &= ~DFLEXDPMLE1_DPMLETC_MASK(tc->phy_fia_idx);
switch (required_lanes) {
case 1:
val |= lane_reversal ?
- DFLEXDPMLE1_DPMLETC_ML3(dig_port->tc_phy_fia_idx) :
- DFLEXDPMLE1_DPMLETC_ML0(dig_port->tc_phy_fia_idx);
+ DFLEXDPMLE1_DPMLETC_ML3(tc->phy_fia_idx) :
+ DFLEXDPMLE1_DPMLETC_ML0(tc->phy_fia_idx);
break;
case 2:
val |= lane_reversal ?
- DFLEXDPMLE1_DPMLETC_ML3_2(dig_port->tc_phy_fia_idx) :
- DFLEXDPMLE1_DPMLETC_ML1_0(dig_port->tc_phy_fia_idx);
+ DFLEXDPMLE1_DPMLETC_ML3_2(tc->phy_fia_idx) :
+ DFLEXDPMLE1_DPMLETC_ML1_0(tc->phy_fia_idx);
break;
case 4:
- val |= DFLEXDPMLE1_DPMLETC_ML3_0(dig_port->tc_phy_fia_idx);
+ val |= DFLEXDPMLE1_DPMLETC_ML3_0(tc->phy_fia_idx);
break;
default:
MISSING_CASE(required_lanes);
}
- intel_de_write(i915, PORT_TX_DFLEXDPMLE1(dig_port->tc_phy_fia), val);
+ intel_de_write(i915, PORT_TX_DFLEXDPMLE1(tc->phy_fia), val);
}
-static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port,
+static void tc_port_fixup_legacy_flag(struct intel_tc_port *tc,
u32 live_status_mask)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
u32 valid_hpd_mask;
- if (dig_port->tc_legacy_port)
+ drm_WARN_ON(&i915->drm, tc->mode != TC_PORT_DISCONNECTED);
+
+ if (hweight32(live_status_mask) != 1)
+ return;
+
+ if (tc->legacy_port)
valid_hpd_mask = BIT(TC_PORT_LEGACY);
else
valid_hpd_mask = BIT(TC_PORT_DP_ALT) |
@@ -232,80 +365,79 @@ static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port,
/* If live status mismatches the VBT flag, trust the live status. */
drm_dbg_kms(&i915->drm,
"Port %s: live status %08x mismatches the legacy port flag %08x, fixing flag\n",
- dig_port->tc_port_name, live_status_mask, valid_hpd_mask);
+ tc->port_name, live_status_mask, valid_hpd_mask);
- dig_port->tc_legacy_port = !dig_port->tc_legacy_port;
+ tc->legacy_port = !tc->legacy_port;
}
-static u32 icl_tc_port_live_status_mask(struct intel_digital_port *dig_port)
+static void tc_phy_load_fia_params(struct intel_tc_port *tc, bool modular_fia)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin];
- u32 mask = 0;
- u32 val;
-
- val = intel_de_read(i915, PORT_TX_DFLEXDPSP(dig_port->tc_phy_fia));
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum port port = tc->dig_port->base.port;
+ enum tc_port tc_port = intel_port_to_tc(i915, port);
- if (val == 0xffffffff) {
- drm_dbg_kms(&i915->drm,
- "Port %s: PHY in TCCOLD, nothing connected\n",
- dig_port->tc_port_name);
- return mask;
+ /*
+ * Each Modular FIA instance houses 2 TC ports. In SOC that has more
+ * than two TC ports, there are multiple instances of Modular FIA.
+ */
+ if (modular_fia) {
+ tc->phy_fia = tc_port / 2;
+ tc->phy_fia_idx = tc_port % 2;
+ } else {
+ tc->phy_fia = FIA1;
+ tc->phy_fia_idx = tc_port;
}
+}
- if (val & TC_LIVE_STATE_TBT(dig_port->tc_phy_fia_idx))
- mask |= BIT(TC_PORT_TBT_ALT);
- if (val & TC_LIVE_STATE_TC(dig_port->tc_phy_fia_idx))
- mask |= BIT(TC_PORT_DP_ALT);
-
- if (intel_de_read(i915, SDEISR) & isr_bit)
- mask |= BIT(TC_PORT_LEGACY);
+/*
+ * ICL TC PHY handlers
+ * -------------------
+ */
+static enum intel_display_power_domain
+icl_tc_phy_cold_off_domain(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
- /* The sink can be connected only in a single mode. */
- if (!drm_WARN_ON_ONCE(&i915->drm, hweight32(mask) > 1))
- tc_port_fixup_legacy_flag(dig_port, mask);
+ if (tc->legacy_port)
+ return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch);
- return mask;
+ return POWER_DOMAIN_TC_COLD_OFF;
}
-static u32 adl_tc_port_live_status_mask(struct intel_digital_port *dig_port)
+static u32 icl_tc_phy_hpd_live_status(struct intel_tc_port *tc)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin];
- u32 val, mask = 0;
+ intel_wakeref_t wakeref;
+ u32 fia_isr;
+ u32 pch_isr;
+ u32 mask = 0;
- /*
- * On ADL-P HW/FW will wake from TCCOLD to complete the read access of
- * registers in IOM. Note that this doesn't apply to PHY and FIA
- * registers.
- */
- val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port));
- if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_ALT)
- mask |= BIT(TC_PORT_DP_ALT);
- if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_TBT)
+ with_intel_display_power(i915, tc_phy_cold_off_domain(tc), wakeref) {
+ fia_isr = intel_de_read(i915, PORT_TX_DFLEXDPSP(tc->phy_fia));
+ pch_isr = intel_de_read(i915, SDEISR);
+ }
+
+ if (fia_isr == 0xffffffff) {
+ drm_dbg_kms(&i915->drm,
+ "Port %s: PHY in TCCOLD, nothing connected\n",
+ tc->port_name);
+ return mask;
+ }
+
+ if (fia_isr & TC_LIVE_STATE_TBT(tc->phy_fia_idx))
mask |= BIT(TC_PORT_TBT_ALT);
+ if (fia_isr & TC_LIVE_STATE_TC(tc->phy_fia_idx))
+ mask |= BIT(TC_PORT_DP_ALT);
- if (intel_de_read(i915, SDEISR) & isr_bit)
+ if (pch_isr & isr_bit)
mask |= BIT(TC_PORT_LEGACY);
- /* The sink can be connected only in a single mode. */
- if (!drm_WARN_ON(&i915->drm, hweight32(mask) > 1))
- tc_port_fixup_legacy_flag(dig_port, mask);
-
return mask;
}
-static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-
- if (IS_ALDERLAKE_P(i915))
- return adl_tc_port_live_status_mask(dig_port);
-
- return icl_tc_port_live_status_mask(dig_port);
-}
-
/*
* Return the PHY status complete flag indicating that display can acquire the
* PHY ownership. The IOM firmware sets this flag when a DP-alt or legacy sink
@@ -314,136 +446,80 @@ static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port)
* owned by the TBT subsystem and so switching the ownership to display is not
* required.
*/
-static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port)
+static bool icl_tc_phy_is_ready(struct intel_tc_port *tc)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
u32 val;
- val = intel_de_read(i915, PORT_TX_DFLEXDPPMS(dig_port->tc_phy_fia));
- if (val == 0xffffffff) {
- drm_dbg_kms(&i915->drm,
- "Port %s: PHY in TCCOLD, assuming not complete\n",
- dig_port->tc_port_name);
- return false;
- }
-
- return val & DP_PHY_MODE_STATUS_COMPLETED(dig_port->tc_phy_fia_idx);
-}
-
-/*
- * Return the PHY status complete flag indicating that display can acquire the
- * PHY ownership. The IOM firmware sets this flag when it's ready to switch
- * the ownership to display, regardless of what sink is connected (TBT-alt,
- * DP-alt, legacy or nothing). For TBT-alt sinks the PHY is owned by the TBT
- * subsystem and so switching the ownership to display is not required.
- */
-static bool adl_tc_phy_status_complete(struct intel_digital_port *dig_port)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port);
- u32 val;
+ assert_tc_cold_blocked(tc);
- val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port));
+ val = intel_de_read(i915, PORT_TX_DFLEXDPPMS(tc->phy_fia));
if (val == 0xffffffff) {
drm_dbg_kms(&i915->drm,
- "Port %s: PHY in TCCOLD, assuming not complete\n",
- dig_port->tc_port_name);
+ "Port %s: PHY in TCCOLD, assuming not ready\n",
+ tc->port_name);
return false;
}
- return val & TCSS_DDI_STATUS_READY;
-}
-
-static bool tc_phy_status_complete(struct intel_digital_port *dig_port)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-
- if (IS_ALDERLAKE_P(i915))
- return adl_tc_phy_status_complete(dig_port);
-
- return icl_tc_phy_status_complete(dig_port);
+ return val & DP_PHY_MODE_STATUS_COMPLETED(tc->phy_fia_idx);
}
-static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port,
+static bool icl_tc_phy_take_ownership(struct intel_tc_port *tc,
bool take)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
u32 val;
- val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia));
+ assert_tc_cold_blocked(tc);
+
+ val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia));
if (val == 0xffffffff) {
drm_dbg_kms(&i915->drm,
"Port %s: PHY in TCCOLD, can't %s ownership\n",
- dig_port->tc_port_name, take ? "take" : "release");
+ tc->port_name, take ? "take" : "release");
return false;
}
- val &= ~DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx);
+ val &= ~DP_PHY_MODE_STATUS_NOT_SAFE(tc->phy_fia_idx);
if (take)
- val |= DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx);
+ val |= DP_PHY_MODE_STATUS_NOT_SAFE(tc->phy_fia_idx);
- intel_de_write(i915, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia), val);
+ intel_de_write(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia), val);
return true;
}
-static bool adl_tc_phy_take_ownership(struct intel_digital_port *dig_port,
- bool take)
+static bool icl_tc_phy_is_owned(struct intel_tc_port *tc)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- enum port port = dig_port->base.port;
-
- intel_de_rmw(i915, DDI_BUF_CTL(port), DDI_BUF_CTL_TC_PHY_OWNERSHIP,
- take ? DDI_BUF_CTL_TC_PHY_OWNERSHIP : 0);
-
- return true;
-}
-
-static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-
- if (IS_ALDERLAKE_P(i915))
- return adl_tc_phy_take_ownership(dig_port, take);
-
- return icl_tc_phy_take_ownership(dig_port, take);
-}
-
-static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
u32 val;
- val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia));
+ assert_tc_cold_blocked(tc);
+
+ val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia));
if (val == 0xffffffff) {
drm_dbg_kms(&i915->drm,
- "Port %s: PHY in TCCOLD, assume safe mode\n",
- dig_port->tc_port_name);
- return true;
+ "Port %s: PHY in TCCOLD, assume not owned\n",
+ tc->port_name);
+ return false;
}
- return val & DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx);
+ return val & DP_PHY_MODE_STATUS_NOT_SAFE(tc->phy_fia_idx);
}
-static bool adl_tc_phy_is_owned(struct intel_digital_port *dig_port)
+static void icl_tc_phy_get_hw_state(struct intel_tc_port *tc)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- enum port port = dig_port->base.port;
- u32 val;
-
- val = intel_de_read(i915, DDI_BUF_CTL(port));
- return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP;
-}
+ enum intel_display_power_domain domain;
+ intel_wakeref_t tc_cold_wref;
-static bool tc_phy_is_owned(struct intel_digital_port *dig_port)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ tc_cold_wref = __tc_cold_block(tc, &domain);
- if (IS_ALDERLAKE_P(i915))
- return adl_tc_phy_is_owned(dig_port);
+ tc->mode = tc_phy_get_current_mode(tc);
+ if (tc->mode != TC_PORT_DISCONNECTED)
+ tc->lock_wakeref = tc_cold_block(tc);
- return icl_tc_phy_is_owned(dig_port);
+ __tc_cold_unblock(tc, domain, tc_cold_wref);
}
/*
@@ -457,151 +533,590 @@ static bool tc_phy_is_owned(struct intel_digital_port *dig_port)
* connect and disconnect to cleanly transfer ownership with the controller and
* set the type-C power state.
*/
-static void icl_tc_phy_connect(struct intel_digital_port *dig_port,
- int required_lanes)
+static bool tc_phy_verify_legacy_or_dp_alt_mode(struct intel_tc_port *tc,
+ int required_lanes)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- u32 live_status_mask;
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
int max_lanes;
- if (!tc_phy_status_complete(dig_port)) {
- drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n",
- dig_port->tc_port_name);
- goto out_set_tbt_alt_mode;
- }
-
- live_status_mask = tc_port_live_status_mask(dig_port);
- if (!(live_status_mask & (BIT(TC_PORT_DP_ALT) | BIT(TC_PORT_LEGACY))) &&
- !dig_port->tc_legacy_port) {
- drm_dbg_kms(&i915->drm, "Port %s: PHY ownership not required (live status %02x)\n",
- dig_port->tc_port_name, live_status_mask);
- goto out_set_tbt_alt_mode;
- }
-
- if (!tc_phy_take_ownership(dig_port, true) &&
- !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port))
- goto out_set_tbt_alt_mode;
-
max_lanes = intel_tc_port_fia_max_lane_count(dig_port);
- if (dig_port->tc_legacy_port) {
+ if (tc->mode == TC_PORT_LEGACY) {
drm_WARN_ON(&i915->drm, max_lanes != 4);
- dig_port->tc_mode = TC_PORT_LEGACY;
-
- return;
+ return true;
}
+ drm_WARN_ON(&i915->drm, tc->mode != TC_PORT_DP_ALT);
+
/*
* Now we have to re-check the live state, in case the port recently
* became disconnected. Not necessary for legacy mode.
*/
- if (!(tc_port_live_status_mask(dig_port) & BIT(TC_PORT_DP_ALT))) {
+ if (!(tc_phy_hpd_live_status(tc) & BIT(TC_PORT_DP_ALT))) {
drm_dbg_kms(&i915->drm, "Port %s: PHY sudden disconnect\n",
- dig_port->tc_port_name);
- goto out_release_phy;
+ tc->port_name);
+ return false;
}
if (max_lanes < required_lanes) {
drm_dbg_kms(&i915->drm,
"Port %s: PHY max lanes %d < required lanes %d\n",
- dig_port->tc_port_name,
+ tc->port_name,
max_lanes, required_lanes);
- goto out_release_phy;
+ return false;
+ }
+
+ return true;
+}
+
+static bool icl_tc_phy_connect(struct intel_tc_port *tc,
+ int required_lanes)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+
+ tc->lock_wakeref = tc_cold_block(tc);
+
+ if (tc->mode == TC_PORT_TBT_ALT)
+ return true;
+
+ if ((!tc_phy_is_ready(tc) ||
+ !icl_tc_phy_take_ownership(tc, true)) &&
+ !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) {
+ drm_dbg_kms(&i915->drm, "Port %s: can't take PHY ownership (ready %s)\n",
+ tc->port_name,
+ str_yes_no(tc_phy_is_ready(tc)));
+ goto out_unblock_tc_cold;
}
- dig_port->tc_mode = TC_PORT_DP_ALT;
- return;
+ if (!tc_phy_verify_legacy_or_dp_alt_mode(tc, required_lanes))
+ goto out_release_phy;
+
+ return true;
out_release_phy:
- tc_phy_take_ownership(dig_port, false);
-out_set_tbt_alt_mode:
- dig_port->tc_mode = TC_PORT_TBT_ALT;
+ icl_tc_phy_take_ownership(tc, false);
+out_unblock_tc_cold:
+ tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref));
+
+ return false;
}
/*
* See the comment at the connect function. This implements the Disconnect
* Flow.
*/
-static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port)
+static void icl_tc_phy_disconnect(struct intel_tc_port *tc)
{
- switch (dig_port->tc_mode) {
+ switch (tc->mode) {
case TC_PORT_LEGACY:
case TC_PORT_DP_ALT:
- tc_phy_take_ownership(dig_port, false);
+ icl_tc_phy_take_ownership(tc, false);
fallthrough;
case TC_PORT_TBT_ALT:
- dig_port->tc_mode = TC_PORT_DISCONNECTED;
- fallthrough;
- case TC_PORT_DISCONNECTED:
+ tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref));
break;
default:
- MISSING_CASE(dig_port->tc_mode);
+ MISSING_CASE(tc->mode);
}
}
-static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port)
+static void icl_tc_phy_init(struct intel_tc_port *tc)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ tc_phy_load_fia_params(tc, false);
+}
+
+static const struct intel_tc_phy_ops icl_tc_phy_ops = {
+ .cold_off_domain = icl_tc_phy_cold_off_domain,
+ .hpd_live_status = icl_tc_phy_hpd_live_status,
+ .is_ready = icl_tc_phy_is_ready,
+ .is_owned = icl_tc_phy_is_owned,
+ .get_hw_state = icl_tc_phy_get_hw_state,
+ .connect = icl_tc_phy_connect,
+ .disconnect = icl_tc_phy_disconnect,
+ .init = icl_tc_phy_init,
+};
+
+/*
+ * TGL TC PHY handlers
+ * -------------------
+ */
+static enum intel_display_power_domain
+tgl_tc_phy_cold_off_domain(struct intel_tc_port *tc)
+{
+ return POWER_DOMAIN_TC_COLD_OFF;
+}
+
+static void tgl_tc_phy_init(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ intel_wakeref_t wakeref;
+ u32 val;
+
+ with_intel_display_power(i915, tc_phy_cold_off_domain(tc), wakeref)
+ val = intel_de_read(i915, PORT_TX_DFLEXDPSP(FIA1));
+
+ drm_WARN_ON(&i915->drm, val == 0xffffffff);
+
+ tc_phy_load_fia_params(tc, val & MODULAR_FIA_MASK);
+}
+
+static const struct intel_tc_phy_ops tgl_tc_phy_ops = {
+ .cold_off_domain = tgl_tc_phy_cold_off_domain,
+ .hpd_live_status = icl_tc_phy_hpd_live_status,
+ .is_ready = icl_tc_phy_is_ready,
+ .is_owned = icl_tc_phy_is_owned,
+ .get_hw_state = icl_tc_phy_get_hw_state,
+ .connect = icl_tc_phy_connect,
+ .disconnect = icl_tc_phy_disconnect,
+ .init = tgl_tc_phy_init,
+};
+
+/*
+ * ADLP TC PHY handlers
+ * --------------------
+ */
+static enum intel_display_power_domain
+adlp_tc_phy_cold_off_domain(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
+
+ if (tc->mode != TC_PORT_TBT_ALT)
+ return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch);
+
+ return POWER_DOMAIN_TC_COLD_OFF;
+}
+
+static u32 adlp_tc_phy_hpd_live_status(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
+ enum hpd_pin hpd_pin = dig_port->base.hpd_pin;
+ u32 cpu_isr_bits = i915->display.hotplug.hpd[hpd_pin];
+ u32 pch_isr_bit = i915->display.hotplug.pch_hpd[hpd_pin];
+ intel_wakeref_t wakeref;
+ u32 cpu_isr;
+ u32 pch_isr;
+ u32 mask = 0;
- if (!tc_phy_status_complete(dig_port)) {
- drm_dbg_kms(&i915->drm, "Port %s: PHY status not complete\n",
- dig_port->tc_port_name);
- return dig_port->tc_mode == TC_PORT_TBT_ALT;
+ with_intel_display_power(i915, POWER_DOMAIN_DISPLAY_CORE, wakeref) {
+ cpu_isr = intel_de_read(i915, GEN11_DE_HPD_ISR);
+ pch_isr = intel_de_read(i915, SDEISR);
}
- /* On ADL-P the PHY complete flag is set in TBT mode as well. */
- if (IS_ALDERLAKE_P(i915) && dig_port->tc_mode == TC_PORT_TBT_ALT)
- return true;
+ if (cpu_isr & (cpu_isr_bits & GEN11_DE_TC_HOTPLUG_MASK))
+ mask |= BIT(TC_PORT_DP_ALT);
+ if (cpu_isr & (cpu_isr_bits & GEN11_DE_TBT_HOTPLUG_MASK))
+ mask |= BIT(TC_PORT_TBT_ALT);
+
+ if (pch_isr & pch_isr_bit)
+ mask |= BIT(TC_PORT_LEGACY);
+
+ return mask;
+}
+
+/*
+ * Return the PHY status complete flag indicating that display can acquire the
+ * PHY ownership. The IOM firmware sets this flag when it's ready to switch
+ * the ownership to display, regardless of what sink is connected (TBT-alt,
+ * DP-alt, legacy or nothing). For TBT-alt sinks the PHY is owned by the TBT
+ * subsystem and so switching the ownership to display is not required.
+ */
+static bool adlp_tc_phy_is_ready(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum tc_port tc_port = intel_port_to_tc(i915, tc->dig_port->base.port);
+ u32 val;
- if (!tc_phy_is_owned(dig_port)) {
- drm_dbg_kms(&i915->drm, "Port %s: PHY not owned\n",
- dig_port->tc_port_name);
+ assert_display_core_power_enabled(tc);
+ val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port));
+ if (val == 0xffffffff) {
+ drm_dbg_kms(&i915->drm,
+ "Port %s: PHY in TCCOLD, assuming not ready\n",
+ tc->port_name);
return false;
}
- return dig_port->tc_mode == TC_PORT_DP_ALT ||
- dig_port->tc_mode == TC_PORT_LEGACY;
+ return val & TCSS_DDI_STATUS_READY;
+}
+
+static bool adlp_tc_phy_take_ownership(struct intel_tc_port *tc,
+ bool take)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum port port = tc->dig_port->base.port;
+
+ assert_tc_port_power_enabled(tc);
+
+ intel_de_rmw(i915, DDI_BUF_CTL(port), DDI_BUF_CTL_TC_PHY_OWNERSHIP,
+ take ? DDI_BUF_CTL_TC_PHY_OWNERSHIP : 0);
+
+ return true;
+}
+
+static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum port port = tc->dig_port->base.port;
+ u32 val;
+
+ assert_tc_port_power_enabled(tc);
+
+ val = intel_de_read(i915, DDI_BUF_CTL(port));
+ return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP;
+}
+
+static void adlp_tc_phy_get_hw_state(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum intel_display_power_domain port_power_domain =
+ tc_port_power_domain(tc);
+ intel_wakeref_t port_wakeref;
+
+ port_wakeref = intel_display_power_get(i915, port_power_domain);
+
+ tc->mode = tc_phy_get_current_mode(tc);
+ if (tc->mode != TC_PORT_DISCONNECTED)
+ tc->lock_wakeref = tc_cold_block(tc);
+
+ intel_display_power_put(i915, port_power_domain, port_wakeref);
+}
+
+static bool adlp_tc_phy_connect(struct intel_tc_port *tc, int required_lanes)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum intel_display_power_domain port_power_domain =
+ tc_port_power_domain(tc);
+ intel_wakeref_t port_wakeref;
+
+ if (tc->mode == TC_PORT_TBT_ALT) {
+ tc->lock_wakeref = tc_cold_block(tc);
+ return true;
+ }
+
+ port_wakeref = intel_display_power_get(i915, port_power_domain);
+
+ if (!adlp_tc_phy_take_ownership(tc, true) &&
+ !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) {
+ drm_dbg_kms(&i915->drm, "Port %s: can't take PHY ownership\n",
+ tc->port_name);
+ goto out_put_port_power;
+ }
+
+ if (!tc_phy_is_ready(tc) &&
+ !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) {
+ drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n",
+ tc->port_name);
+ goto out_release_phy;
+ }
+
+ tc->lock_wakeref = tc_cold_block(tc);
+
+ if (!tc_phy_verify_legacy_or_dp_alt_mode(tc, required_lanes))
+ goto out_unblock_tc_cold;
+
+ intel_display_power_put(i915, port_power_domain, port_wakeref);
+
+ return true;
+
+out_unblock_tc_cold:
+ tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref));
+out_release_phy:
+ adlp_tc_phy_take_ownership(tc, false);
+out_put_port_power:
+ intel_display_power_put(i915, port_power_domain, port_wakeref);
+
+ return false;
+}
+
+static void adlp_tc_phy_disconnect(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum intel_display_power_domain port_power_domain =
+ tc_port_power_domain(tc);
+ intel_wakeref_t port_wakeref;
+
+ port_wakeref = intel_display_power_get(i915, port_power_domain);
+
+ tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref));
+
+ switch (tc->mode) {
+ case TC_PORT_LEGACY:
+ case TC_PORT_DP_ALT:
+ adlp_tc_phy_take_ownership(tc, false);
+ fallthrough;
+ case TC_PORT_TBT_ALT:
+ break;
+ default:
+ MISSING_CASE(tc->mode);
+ }
+
+ intel_display_power_put(i915, port_power_domain, port_wakeref);
+}
+
+static void adlp_tc_phy_init(struct intel_tc_port *tc)
+{
+ tc_phy_load_fia_params(tc, true);
+}
+
+static const struct intel_tc_phy_ops adlp_tc_phy_ops = {
+ .cold_off_domain = adlp_tc_phy_cold_off_domain,
+ .hpd_live_status = adlp_tc_phy_hpd_live_status,
+ .is_ready = adlp_tc_phy_is_ready,
+ .is_owned = adlp_tc_phy_is_owned,
+ .get_hw_state = adlp_tc_phy_get_hw_state,
+ .connect = adlp_tc_phy_connect,
+ .disconnect = adlp_tc_phy_disconnect,
+ .init = adlp_tc_phy_init,
+};
+
+/*
+ * Generic TC PHY handlers
+ * -----------------------
+ */
+static enum intel_display_power_domain
+tc_phy_cold_off_domain(struct intel_tc_port *tc)
+{
+ return tc->phy_ops->cold_off_domain(tc);
+}
+
+static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ u32 mask;
+
+ mask = tc->phy_ops->hpd_live_status(tc);
+
+ /* The sink can be connected only in a single mode. */
+ drm_WARN_ON_ONCE(&i915->drm, hweight32(mask) > 1);
+
+ return mask;
+}
+
+static bool tc_phy_is_ready(struct intel_tc_port *tc)
+{
+ return tc->phy_ops->is_ready(tc);
+}
+
+static bool tc_phy_is_owned(struct intel_tc_port *tc)
+{
+ return tc->phy_ops->is_owned(tc);
+}
+
+static void tc_phy_get_hw_state(struct intel_tc_port *tc)
+{
+ tc->phy_ops->get_hw_state(tc);
+}
+
+static bool tc_phy_is_ready_and_owned(struct intel_tc_port *tc,
+ bool phy_is_ready, bool phy_is_owned)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+
+ drm_WARN_ON(&i915->drm, phy_is_owned && !phy_is_ready);
+
+ return phy_is_ready && phy_is_owned;
+}
+
+static bool tc_phy_is_connected(struct intel_tc_port *tc,
+ enum icl_port_dpll_id port_pll_type)
+{
+ struct intel_encoder *encoder = &tc->dig_port->base;
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ bool phy_is_ready = tc_phy_is_ready(tc);
+ bool phy_is_owned = tc_phy_is_owned(tc);
+ bool is_connected;
+
+ if (tc_phy_is_ready_and_owned(tc, phy_is_ready, phy_is_owned))
+ is_connected = port_pll_type == ICL_PORT_DPLL_MG_PHY;
+ else
+ is_connected = port_pll_type == ICL_PORT_DPLL_DEFAULT;
+
+ drm_dbg_kms(&i915->drm,
+ "Port %s: PHY connected: %s (ready: %s, owned: %s, pll_type: %s)\n",
+ tc->port_name,
+ str_yes_no(is_connected),
+ str_yes_no(phy_is_ready),
+ str_yes_no(phy_is_owned),
+ port_pll_type == ICL_PORT_DPLL_DEFAULT ? "tbt" : "non-tbt");
+
+ return is_connected;
+}
+
+static void tc_phy_wait_for_ready(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+
+ if (wait_for(tc_phy_is_ready(tc), 100))
+ drm_err(&i915->drm, "Port %s: timeout waiting for PHY ready\n",
+ tc->port_name);
}
static enum tc_port_mode
-intel_tc_port_get_current_mode(struct intel_digital_port *dig_port)
+hpd_mask_to_tc_mode(u32 live_status_mask)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- u32 live_status_mask = tc_port_live_status_mask(dig_port);
- enum tc_port_mode mode;
+ if (live_status_mask)
+ return fls(live_status_mask) - 1;
- if (!tc_phy_is_owned(dig_port) ||
- drm_WARN_ON(&i915->drm, !tc_phy_status_complete(dig_port)))
+ return TC_PORT_DISCONNECTED;
+}
+
+static enum tc_port_mode
+tc_phy_hpd_live_mode(struct intel_tc_port *tc)
+{
+ u32 live_status_mask = tc_phy_hpd_live_status(tc);
+
+ return hpd_mask_to_tc_mode(live_status_mask);
+}
+
+static enum tc_port_mode
+get_tc_mode_in_phy_owned_state(struct intel_tc_port *tc,
+ enum tc_port_mode live_mode)
+{
+ switch (live_mode) {
+ case TC_PORT_LEGACY:
+ case TC_PORT_DP_ALT:
+ return live_mode;
+ default:
+ MISSING_CASE(live_mode);
+ fallthrough;
+ case TC_PORT_TBT_ALT:
+ case TC_PORT_DISCONNECTED:
+ if (tc->legacy_port)
+ return TC_PORT_LEGACY;
+ else
+ return TC_PORT_DP_ALT;
+ }
+}
+
+static enum tc_port_mode
+get_tc_mode_in_phy_not_owned_state(struct intel_tc_port *tc,
+ enum tc_port_mode live_mode)
+{
+ switch (live_mode) {
+ case TC_PORT_LEGACY:
+ return TC_PORT_DISCONNECTED;
+ case TC_PORT_DP_ALT:
+ case TC_PORT_TBT_ALT:
return TC_PORT_TBT_ALT;
+ default:
+ MISSING_CASE(live_mode);
+ fallthrough;
+ case TC_PORT_DISCONNECTED:
+ if (tc->legacy_port)
+ return TC_PORT_DISCONNECTED;
+ else
+ return TC_PORT_TBT_ALT;
+ }
+}
- mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT;
- if (live_status_mask) {
- enum tc_port_mode live_mode = fls(live_status_mask) - 1;
+static enum tc_port_mode
+tc_phy_get_current_mode(struct intel_tc_port *tc)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ enum tc_port_mode live_mode = tc_phy_hpd_live_mode(tc);
+ bool phy_is_ready;
+ bool phy_is_owned;
+ enum tc_port_mode mode;
- if (!drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT))
- mode = live_mode;
+ /*
+ * For legacy ports the IOM firmware initializes the PHY during boot-up
+ * and system resume whether or not a sink is connected. Wait here for
+ * the initialization to get ready.
+ */
+ if (tc->legacy_port)
+ tc_phy_wait_for_ready(tc);
+
+ phy_is_ready = tc_phy_is_ready(tc);
+ phy_is_owned = tc_phy_is_owned(tc);
+
+ if (!tc_phy_is_ready_and_owned(tc, phy_is_ready, phy_is_owned)) {
+ mode = get_tc_mode_in_phy_not_owned_state(tc, live_mode);
+ } else {
+ drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT);
+ mode = get_tc_mode_in_phy_owned_state(tc, live_mode);
}
+ drm_dbg_kms(&i915->drm,
+ "Port %s: PHY mode: %s (ready: %s, owned: %s, HPD: %s)\n",
+ tc->port_name,
+ tc_port_mode_name(mode),
+ str_yes_no(phy_is_ready),
+ str_yes_no(phy_is_owned),
+ tc_port_mode_name(live_mode));
+
return mode;
}
+static enum tc_port_mode default_tc_mode(struct intel_tc_port *tc)
+{
+ if (tc->legacy_port)
+ return TC_PORT_LEGACY;
+
+ return TC_PORT_TBT_ALT;
+}
+
static enum tc_port_mode
-intel_tc_port_get_target_mode(struct intel_digital_port *dig_port)
+hpd_mask_to_target_mode(struct intel_tc_port *tc, u32 live_status_mask)
{
- u32 live_status_mask = tc_port_live_status_mask(dig_port);
+ enum tc_port_mode mode = hpd_mask_to_tc_mode(live_status_mask);
- if (live_status_mask)
- return fls(live_status_mask) - 1;
+ if (mode != TC_PORT_DISCONNECTED)
+ return mode;
- return TC_PORT_TBT_ALT;
+ return default_tc_mode(tc);
+}
+
+static enum tc_port_mode
+tc_phy_get_target_mode(struct intel_tc_port *tc)
+{
+ u32 live_status_mask = tc_phy_hpd_live_status(tc);
+
+ return hpd_mask_to_target_mode(tc, live_status_mask);
+}
+
+static void tc_phy_connect(struct intel_tc_port *tc, int required_lanes)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ u32 live_status_mask = tc_phy_hpd_live_status(tc);
+ bool connected;
+
+ tc_port_fixup_legacy_flag(tc, live_status_mask);
+
+ tc->mode = hpd_mask_to_target_mode(tc, live_status_mask);
+
+ connected = tc->phy_ops->connect(tc, required_lanes);
+ if (!connected && tc->mode != default_tc_mode(tc)) {
+ tc->mode = default_tc_mode(tc);
+ connected = tc->phy_ops->connect(tc, required_lanes);
+ }
+
+ drm_WARN_ON(&i915->drm, !connected);
+}
+
+static void tc_phy_disconnect(struct intel_tc_port *tc)
+{
+ if (tc->mode != TC_PORT_DISCONNECTED) {
+ tc->phy_ops->disconnect(tc);
+ tc->mode = TC_PORT_DISCONNECTED;
+ }
+}
+
+static void tc_phy_init(struct intel_tc_port *tc)
+{
+ mutex_lock(&tc->lock);
+ tc->phy_ops->init(tc);
+ mutex_unlock(&tc->lock);
}
-static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port,
+static void intel_tc_port_reset_mode(struct intel_tc_port *tc,
int required_lanes, bool force_disconnect)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- enum tc_port_mode old_tc_mode = dig_port->tc_mode;
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
+ enum tc_port_mode old_tc_mode = tc->mode;
intel_display_power_flush_work(i915);
if (!intel_tc_cold_requires_aux_pw(dig_port)) {
@@ -613,58 +1128,48 @@ static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port,
drm_WARN_ON(&i915->drm, aux_powered);
}
- icl_tc_phy_disconnect(dig_port);
+ tc_phy_disconnect(tc);
if (!force_disconnect)
- icl_tc_phy_connect(dig_port, required_lanes);
+ tc_phy_connect(tc, required_lanes);
drm_dbg_kms(&i915->drm, "Port %s: TC port mode reset (%s -> %s)\n",
- dig_port->tc_port_name,
+ tc->port_name,
tc_port_mode_name(old_tc_mode),
- tc_port_mode_name(dig_port->tc_mode));
+ tc_port_mode_name(tc->mode));
}
-static bool intel_tc_port_needs_reset(struct intel_digital_port *dig_port)
+static bool intel_tc_port_needs_reset(struct intel_tc_port *tc)
{
- return intel_tc_port_get_target_mode(dig_port) != dig_port->tc_mode;
+ return tc_phy_get_target_mode(tc) != tc->mode;
}
-static void intel_tc_port_update_mode(struct intel_digital_port *dig_port,
+static void intel_tc_port_update_mode(struct intel_tc_port *tc,
int required_lanes, bool force_disconnect)
{
- enum intel_display_power_domain domain;
- intel_wakeref_t wref;
- bool needs_reset = force_disconnect;
-
- if (!needs_reset) {
- /* Get power domain required to check the hotplug live status. */
- wref = tc_cold_block(dig_port, &domain);
- needs_reset = intel_tc_port_needs_reset(dig_port);
- tc_cold_unblock(dig_port, domain, wref);
- }
-
- if (!needs_reset)
- return;
-
- /* Get power domain required for resetting the mode. */
- wref = tc_cold_block_in_mode(dig_port, TC_PORT_DISCONNECTED, &domain);
-
- intel_tc_port_reset_mode(dig_port, required_lanes, force_disconnect);
+ if (force_disconnect ||
+ intel_tc_port_needs_reset(tc))
+ intel_tc_port_reset_mode(tc, required_lanes, force_disconnect);
+}
- /* Get power domain matching the new mode after reset. */
- tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain,
- fetch_and_zero(&dig_port->tc_lock_wakeref));
- if (dig_port->tc_mode != TC_PORT_DISCONNECTED)
- dig_port->tc_lock_wakeref = tc_cold_block(dig_port,
- &dig_port->tc_lock_power_domain);
+static void __intel_tc_port_get_link(struct intel_tc_port *tc)
+{
+ tc->link_refcount++;
+}
- tc_cold_unblock(dig_port, domain, wref);
+static void __intel_tc_port_put_link(struct intel_tc_port *tc)
+{
+ tc->link_refcount--;
}
-static void
-intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port,
- int refcount)
+static bool tc_port_is_enabled(struct intel_tc_port *tc)
{
- dig_port->tc_link_refcount = refcount;
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
+
+ assert_tc_port_power_enabled(tc);
+
+ return intel_de_read(i915, DDI_BUF_CTL(dig_port->base.port)) &
+ DDI_BUF_CTL_ENABLE;
}
/**
@@ -677,85 +1182,119 @@ intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port,
void intel_tc_port_init_mode(struct intel_digital_port *dig_port)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- intel_wakeref_t tc_cold_wref;
- enum intel_display_power_domain domain;
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+ bool update_mode = false;
- mutex_lock(&dig_port->tc_lock);
+ mutex_lock(&tc->lock);
- drm_WARN_ON(&i915->drm, dig_port->tc_mode != TC_PORT_DISCONNECTED);
- drm_WARN_ON(&i915->drm, dig_port->tc_lock_wakeref);
- drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount);
+ drm_WARN_ON(&i915->drm, tc->mode != TC_PORT_DISCONNECTED);
+ drm_WARN_ON(&i915->drm, tc->lock_wakeref);
+ drm_WARN_ON(&i915->drm, tc->link_refcount);
- tc_cold_wref = tc_cold_block(dig_port, &domain);
+ tc_phy_get_hw_state(tc);
+ /*
+ * Save the initial mode for the state check in
+ * intel_tc_port_sanitize_mode().
+ */
+ tc->init_mode = tc->mode;
+
+ /*
+ * The PHY needs to be connected for AUX to work during HW readout and
+ * MST topology resume, but the PHY mode can only be changed if the
+ * port is disabled.
+ *
+ * An exception is the case where BIOS leaves the PHY incorrectly
+ * disconnected on an enabled legacy port. Work around that by
+ * connecting the PHY even though the port is enabled. This doesn't
+ * cause a problem as the PHY ownership state is ignored by the
+ * IOM/TCSS firmware (only display can own the PHY in that case).
+ */
+ if (!tc_port_is_enabled(tc)) {
+ update_mode = true;
+ } else if (tc->mode == TC_PORT_DISCONNECTED) {
+ drm_WARN_ON(&i915->drm, !tc->legacy_port);
+ drm_err(&i915->drm,
+ "Port %s: PHY disconnected on enabled port, connecting it\n",
+ tc->port_name);
+ update_mode = true;
+ }
+
+ if (update_mode)
+ intel_tc_port_update_mode(tc, 1, false);
+
+ /* Prevent changing tc->mode until intel_tc_port_sanitize_mode() is called. */
+ __intel_tc_port_get_link(tc);
- dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port);
- /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */
- intel_tc_port_link_init_refcount(dig_port, 1);
- dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain);
+ mutex_unlock(&tc->lock);
+}
+
+static bool tc_port_has_active_links(struct intel_tc_port *tc,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *i915 = tc_to_i915(tc);
+ struct intel_digital_port *dig_port = tc->dig_port;
+ enum icl_port_dpll_id pll_type = ICL_PORT_DPLL_DEFAULT;
+ int active_links = 0;
- tc_cold_unblock(dig_port, domain, tc_cold_wref);
+ if (dig_port->dp.is_mst) {
+ /* TODO: get the PLL type for MST, once HW readout is done for it. */
+ active_links = intel_dp_mst_encoder_active_links(dig_port);
+ } else if (crtc_state && crtc_state->hw.active) {
+ pll_type = intel_ddi_port_pll_type(&dig_port->base, crtc_state);
+ active_links = 1;
+ }
- drm_dbg_kms(&i915->drm, "Port %s: init mode (%s)\n",
- dig_port->tc_port_name,
- tc_port_mode_name(dig_port->tc_mode));
+ if (active_links && !tc_phy_is_connected(tc, pll_type))
+ drm_err(&i915->drm,
+ "Port %s: PHY disconnected with %d active link(s)\n",
+ tc->port_name, active_links);
- mutex_unlock(&dig_port->tc_lock);
+ return active_links;
}
/**
* intel_tc_port_sanitize_mode: Sanitize the given port's TypeC mode
* @dig_port: digital port
+ * @crtc_state: atomic state of CRTC connected to @dig_port
*
* Sanitize @dig_port's TypeC mode wrt. the encoder's state right after driver
* loading and system resume:
* If the encoder is enabled keep the TypeC mode/PHY connected state locked until
* the encoder is disabled.
* If the encoder is disabled make sure the PHY is disconnected.
+ * @crtc_state is valid if @dig_port is enabled, NULL otherwise.
*/
-void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port)
+void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port,
+ const struct intel_crtc_state *crtc_state)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- struct intel_encoder *encoder = &dig_port->base;
- int active_links = 0;
-
- mutex_lock(&dig_port->tc_lock);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
- if (dig_port->dp.is_mst)
- active_links = intel_dp_mst_encoder_active_links(dig_port);
- else if (encoder->base.crtc)
- active_links = to_intel_crtc(encoder->base.crtc)->active;
-
- drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1);
- intel_tc_port_link_init_refcount(dig_port, active_links);
+ mutex_lock(&tc->lock);
- if (active_links) {
- if (!icl_tc_phy_is_connected(dig_port))
- drm_dbg_kms(&i915->drm,
- "Port %s: PHY disconnected with %d active link(s)\n",
- dig_port->tc_port_name, active_links);
- } else {
+ drm_WARN_ON(&i915->drm, tc->link_refcount != 1);
+ if (!tc_port_has_active_links(tc, crtc_state)) {
/*
* TBT-alt is the default mode in any case the PHY ownership is not
* held (regardless of the sink's connected live state), so
* we'll just switch to disconnected mode from it here without
* a note.
*/
- if (dig_port->tc_mode != TC_PORT_TBT_ALT)
+ if (tc->init_mode != TC_PORT_TBT_ALT &&
+ tc->init_mode != TC_PORT_DISCONNECTED)
drm_dbg_kms(&i915->drm,
"Port %s: PHY left in %s mode on disabled port, disconnecting it\n",
- dig_port->tc_port_name,
- tc_port_mode_name(dig_port->tc_mode));
- icl_tc_phy_disconnect(dig_port);
-
- tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain,
- fetch_and_zero(&dig_port->tc_lock_wakeref));
+ tc->port_name,
+ tc_port_mode_name(tc->init_mode));
+ tc_phy_disconnect(tc);
+ __intel_tc_port_put_link(tc);
}
drm_dbg_kms(&i915->drm, "Port %s: sanitize mode (%s)\n",
- dig_port->tc_port_name,
- tc_port_mode_name(dig_port->tc_mode));
+ tc->port_name,
+ tc_port_mode_name(tc->mode));
- mutex_unlock(&dig_port->tc_lock);
+ mutex_unlock(&tc->lock);
}
/*
@@ -768,63 +1307,73 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port)
* connected ports are usable, and avoids exposing to the users objects they
* can't really use.
*/
-bool intel_tc_port_connected(struct intel_encoder *encoder)
+bool intel_tc_port_connected_locked(struct intel_encoder *encoder)
{
struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
- bool is_connected;
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+ u32 mask = ~0;
- intel_tc_port_lock(dig_port);
+ drm_WARN_ON(&i915->drm, !intel_tc_port_ref_held(dig_port));
- is_connected = tc_port_live_status_mask(dig_port) &
- BIT(dig_port->tc_mode);
+ if (tc->mode != TC_PORT_DISCONNECTED)
+ mask = BIT(tc->mode);
- intel_tc_port_unlock(dig_port);
+ return tc_phy_hpd_live_status(tc) & mask;
+}
+
+bool intel_tc_port_connected(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+ bool is_connected;
+
+ mutex_lock(&tc->lock);
+ is_connected = intel_tc_port_connected_locked(encoder);
+ mutex_unlock(&tc->lock);
return is_connected;
}
-static void __intel_tc_port_lock(struct intel_digital_port *dig_port,
+static void __intel_tc_port_lock(struct intel_tc_port *tc,
int required_lanes)
{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct drm_i915_private *i915 = tc_to_i915(tc);
- mutex_lock(&dig_port->tc_lock);
+ mutex_lock(&tc->lock);
- cancel_delayed_work(&dig_port->tc_disconnect_phy_work);
+ cancel_delayed_work(&tc->disconnect_phy_work);
- if (!dig_port->tc_link_refcount)
- intel_tc_port_update_mode(dig_port, required_lanes,
+ if (!tc->link_refcount)
+ intel_tc_port_update_mode(tc, required_lanes,
false);
- drm_WARN_ON(&i915->drm, dig_port->tc_mode == TC_PORT_DISCONNECTED);
- drm_WARN_ON(&i915->drm, dig_port->tc_mode != TC_PORT_TBT_ALT &&
- !tc_phy_is_owned(dig_port));
+ drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_DISCONNECTED);
+ drm_WARN_ON(&i915->drm, tc->mode != TC_PORT_TBT_ALT &&
+ !tc_phy_is_owned(tc));
}
void intel_tc_port_lock(struct intel_digital_port *dig_port)
{
- __intel_tc_port_lock(dig_port, 1);
+ __intel_tc_port_lock(to_tc_port(dig_port), 1);
}
-/**
- * intel_tc_port_disconnect_phy_work: disconnect TypeC PHY from display port
- * @dig_port: digital port
- *
+/*
* Disconnect the given digital port from its TypeC PHY (handing back the
* control of the PHY to the TypeC subsystem). This will happen in a delayed
* manner after each aux transactions and modeset disables.
*/
static void intel_tc_port_disconnect_phy_work(struct work_struct *work)
{
- struct intel_digital_port *dig_port =
- container_of(work, struct intel_digital_port, tc_disconnect_phy_work.work);
+ struct intel_tc_port *tc =
+ container_of(work, struct intel_tc_port, disconnect_phy_work.work);
- mutex_lock(&dig_port->tc_lock);
+ mutex_lock(&tc->lock);
- if (!dig_port->tc_link_refcount)
- intel_tc_port_update_mode(dig_port, 1, true);
+ if (!tc->link_refcount)
+ intel_tc_port_update_mode(tc, 1, true);
- mutex_unlock(&dig_port->tc_lock);
+ mutex_unlock(&tc->lock);
}
/**
@@ -835,105 +1384,91 @@ static void intel_tc_port_disconnect_phy_work(struct work_struct *work)
*/
void intel_tc_port_flush_work(struct intel_digital_port *dig_port)
{
- flush_delayed_work(&dig_port->tc_disconnect_phy_work);
+ flush_delayed_work(&to_tc_port(dig_port)->disconnect_phy_work);
}
void intel_tc_port_unlock(struct intel_digital_port *dig_port)
{
- if (!dig_port->tc_link_refcount && dig_port->tc_mode != TC_PORT_DISCONNECTED)
- queue_delayed_work(system_unbound_wq, &dig_port->tc_disconnect_phy_work,
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+
+ if (!tc->link_refcount && tc->mode != TC_PORT_DISCONNECTED)
+ queue_delayed_work(system_unbound_wq, &tc->disconnect_phy_work,
msecs_to_jiffies(1000));
- mutex_unlock(&dig_port->tc_lock);
+ mutex_unlock(&tc->lock);
}
bool intel_tc_port_ref_held(struct intel_digital_port *dig_port)
{
- return mutex_is_locked(&dig_port->tc_lock) ||
- dig_port->tc_link_refcount;
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+
+ return mutex_is_locked(&tc->lock) ||
+ tc->link_refcount;
}
void intel_tc_port_get_link(struct intel_digital_port *dig_port,
int required_lanes)
{
- __intel_tc_port_lock(dig_port, required_lanes);
- dig_port->tc_link_refcount++;
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+
+ __intel_tc_port_lock(tc, required_lanes);
+ __intel_tc_port_get_link(tc);
intel_tc_port_unlock(dig_port);
}
void intel_tc_port_put_link(struct intel_digital_port *dig_port)
{
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+
intel_tc_port_lock(dig_port);
- --dig_port->tc_link_refcount;
+ __intel_tc_port_put_link(tc);
intel_tc_port_unlock(dig_port);
-
- /*
- * Disconnecting the PHY after the PHY's PLL gets disabled may
- * hang the system on ADL-P, so disconnect the PHY here synchronously.
- * TODO: remove this once the root cause of the ordering requirement
- * is found/fixed.
- */
- intel_tc_port_flush_work(dig_port);
}
-static bool
-tc_has_modular_fia(struct drm_i915_private *i915, struct intel_digital_port *dig_port)
+int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy)
{
- enum intel_display_power_domain domain;
- intel_wakeref_t wakeref;
- u32 val;
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_tc_port *tc;
+ enum port port = dig_port->base.port;
+ enum tc_port tc_port = intel_port_to_tc(i915, port);
- if (!INTEL_INFO(i915)->display.has_modular_fia)
- return false;
+ if (drm_WARN_ON(&i915->drm, tc_port == TC_PORT_NONE))
+ return -EINVAL;
- mutex_lock(&dig_port->tc_lock);
- wakeref = tc_cold_block(dig_port, &domain);
- val = intel_de_read(i915, PORT_TX_DFLEXDPSP(FIA1));
- tc_cold_unblock(dig_port, domain, wakeref);
- mutex_unlock(&dig_port->tc_lock);
+ tc = kzalloc(sizeof(*tc), GFP_KERNEL);
+ if (!tc)
+ return -ENOMEM;
- drm_WARN_ON(&i915->drm, val == 0xffffffff);
+ dig_port->tc = tc;
+ tc->dig_port = dig_port;
- return val & MODULAR_FIA_MASK;
-}
+ if (DISPLAY_VER(i915) >= 13)
+ tc->phy_ops = &adlp_tc_phy_ops;
+ else if (DISPLAY_VER(i915) >= 12)
+ tc->phy_ops = &tgl_tc_phy_ops;
+ else
+ tc->phy_ops = &icl_tc_phy_ops;
-static void
-tc_port_load_fia_params(struct drm_i915_private *i915, struct intel_digital_port *dig_port)
-{
- enum port port = dig_port->base.port;
- enum tc_port tc_port = intel_port_to_tc(i915, port);
+ snprintf(tc->port_name, sizeof(tc->port_name),
+ "%c/TC#%d", port_name(port), tc_port + 1);
- /*
- * Each Modular FIA instance houses 2 TC ports. In SOC that has more
- * than two TC ports, there are multiple instances of Modular FIA.
- */
- if (tc_has_modular_fia(i915, dig_port)) {
- dig_port->tc_phy_fia = tc_port / 2;
- dig_port->tc_phy_fia_idx = tc_port % 2;
- } else {
- dig_port->tc_phy_fia = FIA1;
- dig_port->tc_phy_fia_idx = tc_port;
- }
-}
+ mutex_init(&tc->lock);
+ INIT_DELAYED_WORK(&tc->disconnect_phy_work, intel_tc_port_disconnect_phy_work);
+ tc->legacy_port = is_legacy;
+ tc->mode = TC_PORT_DISCONNECTED;
+ tc->link_refcount = 0;
-void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- enum port port = dig_port->base.port;
- enum tc_port tc_port = intel_port_to_tc(i915, port);
+ tc_phy_init(tc);
- if (drm_WARN_ON(&i915->drm, tc_port == TC_PORT_NONE))
- return;
+ intel_tc_port_init_mode(dig_port);
- snprintf(dig_port->tc_port_name, sizeof(dig_port->tc_port_name),
- "%c/TC#%d", port_name(port), tc_port + 1);
+ return 0;
+}
- mutex_init(&dig_port->tc_lock);
- INIT_DELAYED_WORK(&dig_port->tc_disconnect_phy_work, intel_tc_port_disconnect_phy_work);
- dig_port->tc_legacy_port = is_legacy;
- dig_port->tc_mode = TC_PORT_DISCONNECTED;
- dig_port->tc_link_refcount = 0;
- tc_port_load_fia_params(i915, dig_port);
+void intel_tc_port_cleanup(struct intel_digital_port *dig_port)
+{
+ intel_tc_port_flush_work(dig_port);
- intel_tc_port_init_mode(dig_port);
+ kfree(dig_port->tc);
+ dig_port->tc = NULL;
}
diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h
index d54082e2d5e8..dd0810f9ea95 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.h
+++ b/drivers/gpu/drm/i915/display/intel_tc.h
@@ -6,9 +6,9 @@
#ifndef __INTEL_TC_H__
#define __INTEL_TC_H__
-#include <linux/mutex.h>
#include <linux/types.h>
+struct intel_crtc_state;
struct intel_digital_port;
struct intel_encoder;
@@ -17,6 +17,7 @@ bool intel_tc_port_in_dp_alt_mode(struct intel_digital_port *dig_port);
bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port);
bool intel_tc_port_connected(struct intel_encoder *encoder);
+bool intel_tc_port_connected_locked(struct intel_encoder *encoder);
u32 intel_tc_port_get_lane_mask(struct intel_digital_port *dig_port);
u32 intel_tc_port_get_pin_assignment_mask(struct intel_digital_port *dig_port);
@@ -25,7 +26,8 @@ void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port,
int required_lanes);
void intel_tc_port_init_mode(struct intel_digital_port *dig_port);
-void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port);
+void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port,
+ const struct intel_crtc_state *crtc_state);
void intel_tc_port_lock(struct intel_digital_port *dig_port);
void intel_tc_port_unlock(struct intel_digital_port *dig_port);
void intel_tc_port_flush_work(struct intel_digital_port *dig_port);
@@ -34,7 +36,8 @@ void intel_tc_port_get_link(struct intel_digital_port *dig_port,
void intel_tc_port_put_link(struct intel_digital_port *dig_port);
bool intel_tc_port_ref_held(struct intel_digital_port *dig_port);
-void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy);
+int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy);
+void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port);
diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c
index 3b5ff84dc615..557ec5b62afa 100644
--- a/drivers/gpu/drm/i915/display/intel_tv.c
+++ b/drivers/gpu/drm/i915/display/intel_tv.c
@@ -35,8 +35,8 @@
#include <drm/drm_edid.h>
#include "i915_drv.h"
-#include "i915_reg.h"
#include "i915_irq.h"
+#include "i915_reg.h"
#include "intel_connector.h"
#include "intel_crtc.h"
#include "intel_de.h"
@@ -44,6 +44,7 @@
#include "intel_dpll.h"
#include "intel_hotplug.h"
#include "intel_tv.h"
+#include "intel_tv_regs.h"
enum tv_margin {
TV_MARGIN_LEFT, TV_MARGIN_TOP,
diff --git a/drivers/gpu/drm/i915/display/intel_tv_regs.h b/drivers/gpu/drm/i915/display/intel_tv_regs.h
new file mode 100644
index 000000000000..ab25aeb3c423
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_tv_regs.h
@@ -0,0 +1,490 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __INTEL_TV_REGS_H__
+#define __INTEL_TV_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+/* TV port control */
+#define TV_CTL _MMIO(0x68000)
+/* Enables the TV encoder */
+# define TV_ENC_ENABLE (1 << 31)
+/* Sources the TV encoder input from pipe B instead of A. */
+# define TV_ENC_PIPE_SEL_SHIFT 30
+# define TV_ENC_PIPE_SEL_MASK (1 << 30)
+# define TV_ENC_PIPE_SEL(pipe) ((pipe) << 30)
+/* Outputs composite video (DAC A only) */
+# define TV_ENC_OUTPUT_COMPOSITE (0 << 28)
+/* Outputs SVideo video (DAC B/C) */
+# define TV_ENC_OUTPUT_SVIDEO (1 << 28)
+/* Outputs Component video (DAC A/B/C) */
+# define TV_ENC_OUTPUT_COMPONENT (2 << 28)
+/* Outputs Composite and SVideo (DAC A/B/C) */
+# define TV_ENC_OUTPUT_SVIDEO_COMPOSITE (3 << 28)
+# define TV_TRILEVEL_SYNC (1 << 21)
+/* Enables slow sync generation (945GM only) */
+# define TV_SLOW_SYNC (1 << 20)
+/* Selects 4x oversampling for 480i and 576p */
+# define TV_OVERSAMPLE_4X (0 << 18)
+/* Selects 2x oversampling for 720p and 1080i */
+# define TV_OVERSAMPLE_2X (1 << 18)
+/* Selects no oversampling for 1080p */
+# define TV_OVERSAMPLE_NONE (2 << 18)
+/* Selects 8x oversampling */
+# define TV_OVERSAMPLE_8X (3 << 18)
+# define TV_OVERSAMPLE_MASK (3 << 18)
+/* Selects progressive mode rather than interlaced */
+# define TV_PROGRESSIVE (1 << 17)
+/* Sets the colorburst to PAL mode. Required for non-M PAL modes. */
+# define TV_PAL_BURST (1 << 16)
+/* Field for setting delay of Y compared to C */
+# define TV_YC_SKEW_MASK (7 << 12)
+/* Enables a fix for 480p/576p standard definition modes on the 915GM only */
+# define TV_ENC_SDP_FIX (1 << 11)
+/*
+ * Enables a fix for the 915GM only.
+ *
+ * Not sure what it does.
+ */
+# define TV_ENC_C0_FIX (1 << 10)
+/* Bits that must be preserved by software */
+# define TV_CTL_SAVE ((1 << 11) | (3 << 9) | (7 << 6) | 0xf)
+# define TV_FUSE_STATE_MASK (3 << 4)
+/* Read-only state that reports all features enabled */
+# define TV_FUSE_STATE_ENABLED (0 << 4)
+/* Read-only state that reports that Macrovision is disabled in hardware*/
+# define TV_FUSE_STATE_NO_MACROVISION (1 << 4)
+/* Read-only state that reports that TV-out is disabled in hardware. */
+# define TV_FUSE_STATE_DISABLED (2 << 4)
+/* Normal operation */
+# define TV_TEST_MODE_NORMAL (0 << 0)
+/* Encoder test pattern 1 - combo pattern */
+# define TV_TEST_MODE_PATTERN_1 (1 << 0)
+/* Encoder test pattern 2 - full screen vertical 75% color bars */
+# define TV_TEST_MODE_PATTERN_2 (2 << 0)
+/* Encoder test pattern 3 - full screen horizontal 75% color bars */
+# define TV_TEST_MODE_PATTERN_3 (3 << 0)
+/* Encoder test pattern 4 - random noise */
+# define TV_TEST_MODE_PATTERN_4 (4 << 0)
+/* Encoder test pattern 5 - linear color ramps */
+# define TV_TEST_MODE_PATTERN_5 (5 << 0)
+/*
+ * This test mode forces the DACs to 50% of full output.
+ *
+ * This is used for load detection in combination with TVDAC_SENSE_MASK
+ */
+# define TV_TEST_MODE_MONITOR_DETECT (7 << 0)
+# define TV_TEST_MODE_MASK (7 << 0)
+
+#define TV_DAC _MMIO(0x68004)
+# define TV_DAC_SAVE 0x00ffff00
+/*
+ * Reports that DAC state change logic has reported change (RO).
+ *
+ * This gets cleared when TV_DAC_STATE_EN is cleared
+*/
+# define TVDAC_STATE_CHG (1 << 31)
+# define TVDAC_SENSE_MASK (7 << 28)
+/* Reports that DAC A voltage is above the detect threshold */
+# define TVDAC_A_SENSE (1 << 30)
+/* Reports that DAC B voltage is above the detect threshold */
+# define TVDAC_B_SENSE (1 << 29)
+/* Reports that DAC C voltage is above the detect threshold */
+# define TVDAC_C_SENSE (1 << 28)
+/*
+ * Enables DAC state detection logic, for load-based TV detection.
+ *
+ * The PLL of the chosen pipe (in TV_CTL) must be running, and the encoder set
+ * to off, for load detection to work.
+ */
+# define TVDAC_STATE_CHG_EN (1 << 27)
+/* Sets the DAC A sense value to high */
+# define TVDAC_A_SENSE_CTL (1 << 26)
+/* Sets the DAC B sense value to high */
+# define TVDAC_B_SENSE_CTL (1 << 25)
+/* Sets the DAC C sense value to high */
+# define TVDAC_C_SENSE_CTL (1 << 24)
+/* Overrides the ENC_ENABLE and DAC voltage levels */
+# define DAC_CTL_OVERRIDE (1 << 7)
+/* Sets the slew rate. Must be preserved in software */
+# define ENC_TVDAC_SLEW_FAST (1 << 6)
+# define DAC_A_1_3_V (0 << 4)
+# define DAC_A_1_1_V (1 << 4)
+# define DAC_A_0_7_V (2 << 4)
+# define DAC_A_MASK (3 << 4)
+# define DAC_B_1_3_V (0 << 2)
+# define DAC_B_1_1_V (1 << 2)
+# define DAC_B_0_7_V (2 << 2)
+# define DAC_B_MASK (3 << 2)
+# define DAC_C_1_3_V (0 << 0)
+# define DAC_C_1_1_V (1 << 0)
+# define DAC_C_0_7_V (2 << 0)
+# define DAC_C_MASK (3 << 0)
+
+/*
+ * CSC coefficients are stored in a floating point format with 9 bits of
+ * mantissa and 2 or 3 bits of exponent. The exponent is represented as 2**-n,
+ * where 2-bit exponents are unsigned n, and 3-bit exponents are signed n with
+ * -1 (0x3) being the only legal negative value.
+ */
+#define TV_CSC_Y _MMIO(0x68010)
+# define TV_RY_MASK 0x07ff0000
+# define TV_RY_SHIFT 16
+# define TV_GY_MASK 0x00000fff
+# define TV_GY_SHIFT 0
+
+#define TV_CSC_Y2 _MMIO(0x68014)
+# define TV_BY_MASK 0x07ff0000
+# define TV_BY_SHIFT 16
+/*
+ * Y attenuation for component video.
+ *
+ * Stored in 1.9 fixed point.
+ */
+# define TV_AY_MASK 0x000003ff
+# define TV_AY_SHIFT 0
+
+#define TV_CSC_U _MMIO(0x68018)
+# define TV_RU_MASK 0x07ff0000
+# define TV_RU_SHIFT 16
+# define TV_GU_MASK 0x000007ff
+# define TV_GU_SHIFT 0
+
+#define TV_CSC_U2 _MMIO(0x6801c)
+# define TV_BU_MASK 0x07ff0000
+# define TV_BU_SHIFT 16
+/*
+ * U attenuation for component video.
+ *
+ * Stored in 1.9 fixed point.
+ */
+# define TV_AU_MASK 0x000003ff
+# define TV_AU_SHIFT 0
+
+#define TV_CSC_V _MMIO(0x68020)
+# define TV_RV_MASK 0x0fff0000
+# define TV_RV_SHIFT 16
+# define TV_GV_MASK 0x000007ff
+# define TV_GV_SHIFT 0
+
+#define TV_CSC_V2 _MMIO(0x68024)
+# define TV_BV_MASK 0x07ff0000
+# define TV_BV_SHIFT 16
+/*
+ * V attenuation for component video.
+ *
+ * Stored in 1.9 fixed point.
+ */
+# define TV_AV_MASK 0x000007ff
+# define TV_AV_SHIFT 0
+
+#define TV_CLR_KNOBS _MMIO(0x68028)
+/* 2s-complement brightness adjustment */
+# define TV_BRIGHTNESS_MASK 0xff000000
+# define TV_BRIGHTNESS_SHIFT 24
+/* Contrast adjustment, as a 2.6 unsigned floating point number */
+# define TV_CONTRAST_MASK 0x00ff0000
+# define TV_CONTRAST_SHIFT 16
+/* Saturation adjustment, as a 2.6 unsigned floating point number */
+# define TV_SATURATION_MASK 0x0000ff00
+# define TV_SATURATION_SHIFT 8
+/* Hue adjustment, as an integer phase angle in degrees */
+# define TV_HUE_MASK 0x000000ff
+# define TV_HUE_SHIFT 0
+
+#define TV_CLR_LEVEL _MMIO(0x6802c)
+/* Controls the DAC level for black */
+# define TV_BLACK_LEVEL_MASK 0x01ff0000
+# define TV_BLACK_LEVEL_SHIFT 16
+/* Controls the DAC level for blanking */
+# define TV_BLANK_LEVEL_MASK 0x000001ff
+# define TV_BLANK_LEVEL_SHIFT 0
+
+#define TV_H_CTL_1 _MMIO(0x68030)
+/* Number of pixels in the hsync. */
+# define TV_HSYNC_END_MASK 0x1fff0000
+# define TV_HSYNC_END_SHIFT 16
+/* Total number of pixels minus one in the line (display and blanking). */
+# define TV_HTOTAL_MASK 0x00001fff
+# define TV_HTOTAL_SHIFT 0
+
+#define TV_H_CTL_2 _MMIO(0x68034)
+/* Enables the colorburst (needed for non-component color) */
+# define TV_BURST_ENA (1 << 31)
+/* Offset of the colorburst from the start of hsync, in pixels minus one. */
+# define TV_HBURST_START_SHIFT 16
+# define TV_HBURST_START_MASK 0x1fff0000
+/* Length of the colorburst */
+# define TV_HBURST_LEN_SHIFT 0
+# define TV_HBURST_LEN_MASK 0x0001fff
+
+#define TV_H_CTL_3 _MMIO(0x68038)
+/* End of hblank, measured in pixels minus one from start of hsync */
+# define TV_HBLANK_END_SHIFT 16
+# define TV_HBLANK_END_MASK 0x1fff0000
+/* Start of hblank, measured in pixels minus one from start of hsync */
+# define TV_HBLANK_START_SHIFT 0
+# define TV_HBLANK_START_MASK 0x0001fff
+
+#define TV_V_CTL_1 _MMIO(0x6803c)
+/* XXX */
+# define TV_NBR_END_SHIFT 16
+# define TV_NBR_END_MASK 0x07ff0000
+/* XXX */
+# define TV_VI_END_F1_SHIFT 8
+# define TV_VI_END_F1_MASK 0x00003f00
+/* XXX */
+# define TV_VI_END_F2_SHIFT 0
+# define TV_VI_END_F2_MASK 0x0000003f
+
+#define TV_V_CTL_2 _MMIO(0x68040)
+/* Length of vsync, in half lines */
+# define TV_VSYNC_LEN_MASK 0x07ff0000
+# define TV_VSYNC_LEN_SHIFT 16
+/* Offset of the start of vsync in field 1, measured in one less than the
+ * number of half lines.
+ */
+# define TV_VSYNC_START_F1_MASK 0x00007f00
+# define TV_VSYNC_START_F1_SHIFT 8
+/*
+ * Offset of the start of vsync in field 2, measured in one less than the
+ * number of half lines.
+ */
+# define TV_VSYNC_START_F2_MASK 0x0000007f
+# define TV_VSYNC_START_F2_SHIFT 0
+
+#define TV_V_CTL_3 _MMIO(0x68044)
+/* Enables generation of the equalization signal */
+# define TV_EQUAL_ENA (1 << 31)
+/* Length of vsync, in half lines */
+# define TV_VEQ_LEN_MASK 0x007f0000
+# define TV_VEQ_LEN_SHIFT 16
+/* Offset of the start of equalization in field 1, measured in one less than
+ * the number of half lines.
+ */
+# define TV_VEQ_START_F1_MASK 0x0007f00
+# define TV_VEQ_START_F1_SHIFT 8
+/*
+ * Offset of the start of equalization in field 2, measured in one less than
+ * the number of half lines.
+ */
+# define TV_VEQ_START_F2_MASK 0x000007f
+# define TV_VEQ_START_F2_SHIFT 0
+
+#define TV_V_CTL_4 _MMIO(0x68048)
+/*
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F1_MASK 0x003f0000
+# define TV_VBURST_START_F1_SHIFT 16
+/*
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F1_MASK 0x000000ff
+# define TV_VBURST_END_F1_SHIFT 0
+
+#define TV_V_CTL_5 _MMIO(0x6804c)
+/*
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F2_MASK 0x003f0000
+# define TV_VBURST_START_F2_SHIFT 16
+/*
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F2_MASK 0x000000ff
+# define TV_VBURST_END_F2_SHIFT 0
+
+#define TV_V_CTL_6 _MMIO(0x68050)
+/*
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F3_MASK 0x003f0000
+# define TV_VBURST_START_F3_SHIFT 16
+/*
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F3_MASK 0x000000ff
+# define TV_VBURST_END_F3_SHIFT 0
+
+#define TV_V_CTL_7 _MMIO(0x68054)
+/*
+ * Offset to start of vertical colorburst, measured in one less than the
+ * number of lines from vertical start.
+ */
+# define TV_VBURST_START_F4_MASK 0x003f0000
+# define TV_VBURST_START_F4_SHIFT 16
+/*
+ * Offset to the end of vertical colorburst, measured in one less than the
+ * number of lines from the start of NBR.
+ */
+# define TV_VBURST_END_F4_MASK 0x000000ff
+# define TV_VBURST_END_F4_SHIFT 0
+
+#define TV_SC_CTL_1 _MMIO(0x68060)
+/* Turns on the first subcarrier phase generation DDA */
+# define TV_SC_DDA1_EN (1 << 31)
+/* Turns on the first subcarrier phase generation DDA */
+# define TV_SC_DDA2_EN (1 << 30)
+/* Turns on the first subcarrier phase generation DDA */
+# define TV_SC_DDA3_EN (1 << 29)
+/* Sets the subcarrier DDA to reset frequency every other field */
+# define TV_SC_RESET_EVERY_2 (0 << 24)
+/* Sets the subcarrier DDA to reset frequency every fourth field */
+# define TV_SC_RESET_EVERY_4 (1 << 24)
+/* Sets the subcarrier DDA to reset frequency every eighth field */
+# define TV_SC_RESET_EVERY_8 (2 << 24)
+/* Sets the subcarrier DDA to never reset the frequency */
+# define TV_SC_RESET_NEVER (3 << 24)
+/* Sets the peak amplitude of the colorburst.*/
+# define TV_BURST_LEVEL_MASK 0x00ff0000
+# define TV_BURST_LEVEL_SHIFT 16
+/* Sets the increment of the first subcarrier phase generation DDA */
+# define TV_SCDDA1_INC_MASK 0x00000fff
+# define TV_SCDDA1_INC_SHIFT 0
+
+#define TV_SC_CTL_2 _MMIO(0x68064)
+/* Sets the rollover for the second subcarrier phase generation DDA */
+# define TV_SCDDA2_SIZE_MASK 0x7fff0000
+# define TV_SCDDA2_SIZE_SHIFT 16
+/* Sets the increent of the second subcarrier phase generation DDA */
+# define TV_SCDDA2_INC_MASK 0x00007fff
+# define TV_SCDDA2_INC_SHIFT 0
+
+#define TV_SC_CTL_3 _MMIO(0x68068)
+/* Sets the rollover for the third subcarrier phase generation DDA */
+# define TV_SCDDA3_SIZE_MASK 0x7fff0000
+# define TV_SCDDA3_SIZE_SHIFT 16
+/* Sets the increent of the third subcarrier phase generation DDA */
+# define TV_SCDDA3_INC_MASK 0x00007fff
+# define TV_SCDDA3_INC_SHIFT 0
+
+#define TV_WIN_POS _MMIO(0x68070)
+/* X coordinate of the display from the start of horizontal active */
+# define TV_XPOS_MASK 0x1fff0000
+# define TV_XPOS_SHIFT 16
+/* Y coordinate of the display from the start of vertical active (NBR) */
+# define TV_YPOS_MASK 0x00000fff
+# define TV_YPOS_SHIFT 0
+
+#define TV_WIN_SIZE _MMIO(0x68074)
+/* Horizontal size of the display window, measured in pixels*/
+# define TV_XSIZE_MASK 0x1fff0000
+# define TV_XSIZE_SHIFT 16
+/*
+ * Vertical size of the display window, measured in pixels.
+ *
+ * Must be even for interlaced modes.
+ */
+# define TV_YSIZE_MASK 0x00000fff
+# define TV_YSIZE_SHIFT 0
+
+#define TV_FILTER_CTL_1 _MMIO(0x68080)
+/*
+ * Enables automatic scaling calculation.
+ *
+ * If set, the rest of the registers are ignored, and the calculated values can
+ * be read back from the register.
+ */
+# define TV_AUTO_SCALE (1 << 31)
+/*
+ * Disables the vertical filter.
+ *
+ * This is required on modes more than 1024 pixels wide */
+# define TV_V_FILTER_BYPASS (1 << 29)
+/* Enables adaptive vertical filtering */
+# define TV_VADAPT (1 << 28)
+# define TV_VADAPT_MODE_MASK (3 << 26)
+/* Selects the least adaptive vertical filtering mode */
+# define TV_VADAPT_MODE_LEAST (0 << 26)
+/* Selects the moderately adaptive vertical filtering mode */
+# define TV_VADAPT_MODE_MODERATE (1 << 26)
+/* Selects the most adaptive vertical filtering mode */
+# define TV_VADAPT_MODE_MOST (3 << 26)
+/*
+ * Sets the horizontal scaling factor.
+ *
+ * This should be the fractional part of the horizontal scaling factor divided
+ * by the oversampling rate. TV_HSCALE should be less than 1, and set to:
+ *
+ * (src width - 1) / ((oversample * dest width) - 1)
+ */
+# define TV_HSCALE_FRAC_MASK 0x00003fff
+# define TV_HSCALE_FRAC_SHIFT 0
+
+#define TV_FILTER_CTL_2 _MMIO(0x68084)
+/*
+ * Sets the integer part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * TV_VSCALE should be (src height - 1) / ((interlace * dest height) - 1)
+ */
+# define TV_VSCALE_INT_MASK 0x00038000
+# define TV_VSCALE_INT_SHIFT 15
+/*
+ * Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * \sa TV_VSCALE_INT_MASK
+ */
+# define TV_VSCALE_FRAC_MASK 0x00007fff
+# define TV_VSCALE_FRAC_SHIFT 0
+
+#define TV_FILTER_CTL_3 _MMIO(0x68088)
+/*
+ * Sets the integer part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * TV_VSCALE should be (src height - 1) / (1/4 * (dest height - 1))
+ *
+ * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes.
+ */
+# define TV_VSCALE_IP_INT_MASK 0x00038000
+# define TV_VSCALE_IP_INT_SHIFT 15
+/*
+ * Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
+ *
+ * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes.
+ *
+ * \sa TV_VSCALE_IP_INT_MASK
+ */
+# define TV_VSCALE_IP_FRAC_MASK 0x00007fff
+# define TV_VSCALE_IP_FRAC_SHIFT 0
+
+#define TV_CC_CONTROL _MMIO(0x68090)
+# define TV_CC_ENABLE (1 << 31)
+/*
+ * Specifies which field to send the CC data in.
+ *
+ * CC data is usually sent in field 0.
+ */
+# define TV_CC_FID_MASK (1 << 27)
+# define TV_CC_FID_SHIFT 27
+/* Sets the horizontal position of the CC data. Usually 135. */
+# define TV_CC_HOFF_MASK 0x03ff0000
+# define TV_CC_HOFF_SHIFT 16
+/* Sets the vertical position of the CC data. Usually 21 */
+# define TV_CC_LINE_MASK 0x0000003f
+# define TV_CC_LINE_SHIFT 0
+
+#define TV_CC_DATA _MMIO(0x68094)
+# define TV_CC_RDY (1 << 31)
+/* Second word of CC data to be transmitted. */
+# define TV_CC_DATA_2_MASK 0x007f0000
+# define TV_CC_DATA_2_SHIFT 16
+/* First word of CC data to be transmitted. */
+# define TV_CC_DATA_1_MASK 0x0000007f
+# define TV_CC_DATA_1_SHIFT 0
+
+#define TV_H_LUMA(i) _MMIO(0x68100 + (i) * 4) /* 60 registers */
+#define TV_H_CHROMA(i) _MMIO(0x68200 + (i) * 4) /* 60 registers */
+#define TV_V_LUMA(i) _MMIO(0x68300 + (i) * 4) /* 43 registers */
+#define TV_V_CHROMA(i) _MMIO(0x68400 + (i) * 4) /* 43 registers */
+
+#endif /* __INTEL_TV_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c
index 571f5dda1e66..f8bf9810527d 100644
--- a/drivers/gpu/drm/i915/display/intel_vblank.c
+++ b/drivers/gpu/drm/i915/display/intel_vblank.c
@@ -8,6 +8,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_vblank.h"
+#include "intel_vrr.h"
/*
* This timing diagram depicts the video signal in and
@@ -439,3 +440,94 @@ void intel_wait_for_pipe_scanline_moving(struct intel_crtc *crtc)
{
wait_for_pipe_scanline_moving(crtc, true);
}
+
+static int intel_crtc_scanline_offset(const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+ const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
+
+ /*
+ * The scanline counter increments at the leading edge of hsync.
+ *
+ * On most platforms it starts counting from vtotal-1 on the
+ * first active line. That means the scanline counter value is
+ * always one less than what we would expect. Ie. just after
+ * start of vblank, which also occurs at start of hsync (on the
+ * last active line), the scanline counter will read vblank_start-1.
+ *
+ * On gen2 the scanline counter starts counting from 1 instead
+ * of vtotal-1, so we have to subtract one (or rather add vtotal-1
+ * to keep the value positive), instead of adding one.
+ *
+ * On HSW+ the behaviour of the scanline counter depends on the output
+ * type. For DP ports it behaves like most other platforms, but on HDMI
+ * there's an extra 1 line difference. So we need to add two instead of
+ * one to the value.
+ *
+ * On VLV/CHV DSI the scanline counter would appear to increment
+ * approx. 1/3 of a scanline before start of vblank. Unfortunately
+ * that means we can't tell whether we're in vblank or not while
+ * we're on that particular line. We must still set scanline_offset
+ * to 1 so that the vblank timestamps come out correct when we query
+ * the scanline counter from within the vblank interrupt handler.
+ * However if queried just before the start of vblank we'll get an
+ * answer that's slightly in the future.
+ */
+ if (DISPLAY_VER(i915) == 2) {
+ int vtotal;
+
+ vtotal = adjusted_mode->crtc_vtotal;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+ vtotal /= 2;
+
+ return vtotal - 1;
+ } else if (HAS_DDI(i915) && intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) {
+ return 2;
+ } else {
+ return 1;
+ }
+}
+
+void intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ struct drm_display_mode adjusted_mode;
+ int vmax_vblank_start = 0;
+ unsigned long irqflags;
+
+ drm_mode_init(&adjusted_mode, &crtc_state->hw.adjusted_mode);
+
+ if (crtc_state->vrr.enable) {
+ adjusted_mode.crtc_vtotal = crtc_state->vrr.vmax;
+ adjusted_mode.crtc_vblank_end = crtc_state->vrr.vmax;
+ adjusted_mode.crtc_vblank_start = intel_vrr_vmin_vblank_start(crtc_state);
+ vmax_vblank_start = intel_vrr_vmax_vblank_start(crtc_state);
+ }
+
+ /*
+ * Belts and suspenders locking to guarantee everyone sees 100%
+ * consistent state during fastset seamless refresh rate changes.
+ *
+ * vblank_time_lock takes care of all drm_vblank.c stuff, and
+ * uncore.lock takes care of __intel_get_crtc_scanline() which
+ * may get called elsewhere as well.
+ *
+ * TODO maybe just protect everything (including
+ * __intel_get_crtc_scanline()) with vblank_time_lock?
+ * Need to audit everything to make sure it's safe.
+ */
+ spin_lock_irqsave(&i915->drm.vblank_time_lock, irqflags);
+ spin_lock(&i915->uncore.lock);
+
+ drm_calc_timestamping_constants(&crtc->base, &adjusted_mode);
+
+ crtc->vmax_vblank_start = vmax_vblank_start;
+
+ crtc->mode_flags = crtc_state->mode_flags;
+
+ crtc->scanline_offset = intel_crtc_scanline_offset(crtc_state);
+
+ spin_unlock(&i915->uncore.lock);
+ spin_unlock_irqrestore(&i915->drm.vblank_time_lock, irqflags);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_vblank.h b/drivers/gpu/drm/i915/display/intel_vblank.h
index c9fea2c2a990..0884db7e76ae 100644
--- a/drivers/gpu/drm/i915/display/intel_vblank.h
+++ b/drivers/gpu/drm/i915/display/intel_vblank.h
@@ -11,6 +11,7 @@
struct drm_crtc;
struct intel_crtc;
+struct intel_crtc_state;
u32 i915_get_vblank_counter(struct drm_crtc *crtc);
u32 g4x_get_vblank_counter(struct drm_crtc *crtc);
@@ -19,5 +20,6 @@ bool intel_crtc_get_vblank_timestamp(struct drm_crtc *crtc, int *max_error,
int intel_get_crtc_scanline(struct intel_crtc *crtc);
void intel_wait_for_pipe_scanline_stopped(struct intel_crtc *crtc);
void intel_wait_for_pipe_scanline_moving(struct intel_crtc *crtc);
+void intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state);
#endif /* __INTEL_VBLANK_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c
index 09b32ffdc552..8e787c13d26d 100644
--- a/drivers/gpu/drm/i915/display/intel_vdsc.c
+++ b/drivers/gpu/drm/i915/display/intel_vdsc.c
@@ -423,9 +423,9 @@ calculate_rc_params(struct rc_parameters *rc,
for (buf_i = 0; buf_i < DSC_NUM_BUF_RANGES; buf_i++) {
/* Read range_minqp and range_max_qp from qp tables */
rc->rc_range_params[buf_i].range_min_qp =
- intel_lookup_range_min_qp(bpc, buf_i, bpp_i);
+ intel_lookup_range_min_qp(bpc, buf_i, bpp_i, vdsc_cfg->native_420);
rc->rc_range_params[buf_i].range_max_qp =
- intel_lookup_range_max_qp(bpc, buf_i, bpp_i);
+ intel_lookup_range_max_qp(bpc, buf_i, bpp_i, vdsc_cfg->native_420);
/* Calculate range_bgp_offset */
if (bpp <= 6) {
@@ -448,6 +448,29 @@ calculate_rc_params(struct rc_parameters *rc,
}
}
+static int intel_dsc_slice_dimensions_valid(struct intel_crtc_state *pipe_config,
+ struct drm_dsc_config *vdsc_cfg)
+{
+ if (pipe_config->output_format == INTEL_OUTPUT_FORMAT_RGB ||
+ pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR444) {
+ if (vdsc_cfg->slice_height > 4095)
+ return -EINVAL;
+ if (vdsc_cfg->slice_height * vdsc_cfg->slice_width < 15000)
+ return -EINVAL;
+ } else if (pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) {
+ if (vdsc_cfg->slice_width % 2)
+ return -EINVAL;
+ if (vdsc_cfg->slice_height % 2)
+ return -EINVAL;
+ if (vdsc_cfg->slice_height > 4094)
+ return -EINVAL;
+ if (vdsc_cfg->slice_height * vdsc_cfg->slice_width < 30000)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int intel_dsc_compute_params(struct intel_crtc_state *pipe_config)
{
struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
@@ -456,19 +479,64 @@ int intel_dsc_compute_params(struct intel_crtc_state *pipe_config)
u16 compressed_bpp = pipe_config->dsc.compressed_bpp;
const struct rc_parameters *rc_params;
struct rc_parameters *rc = NULL;
+ int err;
u8 i = 0;
vdsc_cfg->pic_width = pipe_config->hw.adjusted_mode.crtc_hdisplay;
vdsc_cfg->slice_width = DIV_ROUND_UP(vdsc_cfg->pic_width,
pipe_config->dsc.slice_count);
- /* Gen 11 does not support YCbCr */
+ err = intel_dsc_slice_dimensions_valid(pipe_config, vdsc_cfg);
+
+ if (err) {
+ drm_dbg_kms(&dev_priv->drm, "Slice dimension requirements not met\n");
+ return err;
+ }
+
+ /*
+ * According to DSC 1.2 specs if colorspace is YCbCr then convert_rgb is 0
+ * else 1
+ */
+ vdsc_cfg->convert_rgb = pipe_config->output_format != INTEL_OUTPUT_FORMAT_YCBCR420 &&
+ pipe_config->output_format != INTEL_OUTPUT_FORMAT_YCBCR444;
+
+ if (DISPLAY_VER(dev_priv) >= 14 &&
+ pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
+ vdsc_cfg->native_420 = true;
+ /* We do not support YcBCr422 as of now */
+ vdsc_cfg->native_422 = false;
vdsc_cfg->simple_422 = false;
/* Gen 11 does not support VBR */
vdsc_cfg->vbr_enable = false;
/* Gen 11 only supports integral values of bpp */
vdsc_cfg->bits_per_pixel = compressed_bpp << 4;
+
+ /*
+ * According to DSC 1.2 specs in Section 4.1 if native_420 is set:
+ * -We need to double the current bpp.
+ * -second_line_bpg_offset is 12 in general and equal to 2*(slice_height-1) if slice
+ * height < 8.
+ * -second_line_offset_adj is 512 as shown by emperical values to yeild best chroma
+ * preservation in second line.
+ * -nsl_bpg_offset is calculated as second_line_offset/slice_height -1 then rounded
+ * up to 16 fractional bits, we left shift second line offset by 11 to preserve 11
+ * fractional bits.
+ */
+ if (vdsc_cfg->native_420) {
+ vdsc_cfg->bits_per_pixel <<= 1;
+
+ if (vdsc_cfg->slice_height >= 8)
+ vdsc_cfg->second_line_bpg_offset = 12;
+ else
+ vdsc_cfg->second_line_bpg_offset =
+ 2 * (vdsc_cfg->slice_height - 1);
+
+ vdsc_cfg->second_line_offset_adj = 512;
+ vdsc_cfg->nsl_bpg_offset = DIV_ROUND_UP(vdsc_cfg->second_line_bpg_offset << 11,
+ vdsc_cfg->slice_height - 1);
+ }
+
vdsc_cfg->bits_per_component = pipe_config->pipe_bpp / 3;
for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++) {
@@ -595,8 +663,13 @@ static void intel_dsc_pps_configure(const struct intel_crtc_state *crtc_state)
DSC_VER_MIN_SHIFT |
vdsc_cfg->bits_per_component << DSC_BPC_SHIFT |
vdsc_cfg->line_buf_depth << DSC_LINE_BUF_DEPTH_SHIFT;
- if (vdsc_cfg->dsc_version_minor == 2)
+ if (vdsc_cfg->dsc_version_minor == 2) {
pps_val |= DSC_ALT_ICH_SEL;
+ if (vdsc_cfg->native_420)
+ pps_val |= DSC_NATIVE_420_ENABLE;
+ if (vdsc_cfg->native_422)
+ pps_val |= DSC_NATIVE_422_ENABLE;
+ }
if (vdsc_cfg->block_pred_enable)
pps_val |= DSC_BLOCK_PREDICTION;
if (vdsc_cfg->convert_rgb)
@@ -907,6 +980,33 @@ static void intel_dsc_pps_configure(const struct intel_crtc_state *crtc_state)
pps_val);
}
+ if (DISPLAY_VER(dev_priv) >= 14) {
+ /* Populate PICTURE_PARAMETER_SET_17 registers */
+ pps_val = 0;
+ pps_val |= DSC_SL_BPG_OFFSET(vdsc_cfg->second_line_bpg_offset);
+ drm_dbg_kms(&dev_priv->drm, "PPS17 = 0x%08x\n", pps_val);
+ intel_de_write(dev_priv,
+ MTL_DSC0_PICTURE_PARAMETER_SET_17(pipe),
+ pps_val);
+ if (crtc_state->dsc.dsc_split)
+ intel_de_write(dev_priv,
+ MTL_DSC1_PICTURE_PARAMETER_SET_17(pipe),
+ pps_val);
+
+ /* Populate PICTURE_PARAMETER_SET_18 registers */
+ pps_val = 0;
+ pps_val |= DSC_NSL_BPG_OFFSET(vdsc_cfg->nsl_bpg_offset) |
+ DSC_SL_OFFSET_ADJ(vdsc_cfg->second_line_offset_adj);
+ drm_dbg_kms(&dev_priv->drm, "PPS18 = 0x%08x\n", pps_val);
+ intel_de_write(dev_priv,
+ MTL_DSC0_PICTURE_PARAMETER_SET_18(pipe),
+ pps_val);
+ if (crtc_state->dsc.dsc_split)
+ intel_de_write(dev_priv,
+ MTL_DSC1_PICTURE_PARAMETER_SET_18(pipe),
+ pps_val);
+ }
+
/* Populate the RC_BUF_THRESH registers */
memset(rc_buf_thresh_dword, 0, sizeof(rc_buf_thresh_dword));
for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++) {
@@ -1181,7 +1281,7 @@ void intel_dsc_get_config(struct intel_crtc_state *crtc_state)
enum pipe pipe = crtc->pipe;
enum intel_display_power_domain power_domain;
intel_wakeref_t wakeref;
- u32 dss_ctl1, dss_ctl2, val;
+ u32 dss_ctl1, dss_ctl2, pps0 = 0, pps1 = 0;
if (!intel_dsc_source_support(crtc_state))
return;
@@ -1204,13 +1304,21 @@ void intel_dsc_get_config(struct intel_crtc_state *crtc_state)
/* FIXME: add more state readout as needed */
- /* PPS1 */
- if (!is_pipe_dsc(crtc, cpu_transcoder))
- val = intel_de_read(dev_priv, DSCA_PICTURE_PARAMETER_SET_1);
- else
- val = intel_de_read(dev_priv,
- ICL_DSC0_PICTURE_PARAMETER_SET_1(pipe));
- vdsc_cfg->bits_per_pixel = val;
+ /* PPS0 & PPS1 */
+ if (!is_pipe_dsc(crtc, cpu_transcoder)) {
+ pps1 = intel_de_read(dev_priv, DSCA_PICTURE_PARAMETER_SET_1);
+ } else {
+ pps0 = intel_de_read(dev_priv,
+ ICL_DSC0_PICTURE_PARAMETER_SET_0(pipe));
+ pps1 = intel_de_read(dev_priv,
+ ICL_DSC0_PICTURE_PARAMETER_SET_1(pipe));
+ }
+
+ vdsc_cfg->bits_per_pixel = pps1;
+
+ if (pps0 & DSC_NATIVE_420_ENABLE)
+ vdsc_cfg->bits_per_pixel >>= 1;
+
crtc_state->dsc.compressed_bpp = vdsc_cfg->bits_per_pixel >> 4;
out:
intel_display_power_put(dev_priv, power_domain, wakeref);
diff --git a/drivers/gpu/drm/i915/display/intel_vdsc_regs.h b/drivers/gpu/drm/i915/display/intel_vdsc_regs.h
index 4fd883463752..b71f00b5c761 100644
--- a/drivers/gpu/drm/i915/display/intel_vdsc_regs.h
+++ b/drivers/gpu/drm/i915/display/intel_vdsc_regs.h
@@ -46,6 +46,32 @@
_ICL_PIPE_DSS_CTL2_PB, \
_ICL_PIPE_DSS_CTL2_PC)
+/* MTL Display Stream Compression registers */
+#define _MTL_DSC0_PICTURE_PARAMETER_SET_17_PB 0x782B4
+#define _MTL_DSC1_PICTURE_PARAMETER_SET_17_PB 0x783B4
+#define _MTL_DSC0_PICTURE_PARAMETER_SET_17_PC 0x784B4
+#define _MTL_DSC1_PICTURE_PARAMETER_SET_17_PC 0x785B4
+#define MTL_DSC0_PICTURE_PARAMETER_SET_17(pipe) _MMIO_PIPE((pipe) - PIPE_B, \
+ _MTL_DSC0_PICTURE_PARAMETER_SET_17_PB, \
+ _MTL_DSC0_PICTURE_PARAMETER_SET_17_PC)
+#define MTL_DSC1_PICTURE_PARAMETER_SET_17(pipe) _MMIO_PIPE((pipe) - PIPE_B, \
+ _MTL_DSC1_PICTURE_PARAMETER_SET_17_PB, \
+ _MTL_DSC1_PICTURE_PARAMETER_SET_17_PC)
+#define DSC_SL_BPG_OFFSET(offset) ((offset) << 27)
+
+#define _MTL_DSC0_PICTURE_PARAMETER_SET_18_PB 0x782B8
+#define _MTL_DSC1_PICTURE_PARAMETER_SET_18_PB 0x783B8
+#define _MTL_DSC0_PICTURE_PARAMETER_SET_18_PC 0x784B8
+#define _MTL_DSC1_PICTURE_PARAMETER_SET_18_PC 0x785B8
+#define MTL_DSC0_PICTURE_PARAMETER_SET_18(pipe) _MMIO_PIPE((pipe) - PIPE_B, \
+ _MTL_DSC0_PICTURE_PARAMETER_SET_18_PB, \
+ _MTL_DSC0_PICTURE_PARAMETER_SET_18_PC)
+#define MTL_DSC1_PICTURE_PARAMETER_SET_18(pipe) _MMIO_PIPE((pipe) - PIPE_B, \
+ _MTL_DSC1_PICTURE_PARAMETER_SET_18_PB, \
+ _MTL_DSC1_PICTURE_PARAMETER_SET_18_PC)
+#define DSC_NSL_BPG_OFFSET(offset) ((offset) << 16)
+#define DSC_SL_OFFSET_ADJ(offset) ((offset) << 0)
+
/* Icelake Display Stream Compression Registers */
#define DSCA_PICTURE_PARAMETER_SET_0 _MMIO(0x6B200)
#define DSCC_PICTURE_PARAMETER_SET_0 _MMIO(0x6BA00)
@@ -59,6 +85,8 @@
#define ICL_DSC1_PICTURE_PARAMETER_SET_0(pipe) _MMIO_PIPE((pipe) - PIPE_B, \
_ICL_DSC1_PICTURE_PARAMETER_SET_0_PB, \
_ICL_DSC1_PICTURE_PARAMETER_SET_0_PC)
+#define DSC_NATIVE_422_ENABLE BIT(23)
+#define DSC_NATIVE_420_ENABLE BIT(22)
#define DSC_ALT_ICH_SEL (1 << 20)
#define DSC_VBR_ENABLE (1 << 19)
#define DSC_422_ENABLE (1 << 18)
diff --git a/drivers/gpu/drm/i915/display/intel_wm.c b/drivers/gpu/drm/i915/display/intel_wm.c
index bb99179cd5fd..b615449e70b4 100644
--- a/drivers/gpu/drm/i915/display/intel_wm.c
+++ b/drivers/gpu/drm/i915/display/intel_wm.c
@@ -11,7 +11,7 @@
/**
* intel_update_watermarks - update FIFO watermark values based on current modes
- * @dev_priv: i915 device
+ * @i915: i915 device
*
* Calculate watermark values for the various WM regs based on current mode
* and plane configuration.
diff --git a/drivers/gpu/drm/i915/display/skl_scaler.c b/drivers/gpu/drm/i915/display/skl_scaler.c
index 473d53610b92..0e7e014fcc71 100644
--- a/drivers/gpu/drm/i915/display/skl_scaler.c
+++ b/drivers/gpu/drm/i915/display/skl_scaler.c
@@ -111,6 +111,8 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
const struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode;
+ int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
+ int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
int min_src_w, min_src_h, min_dst_w, min_dst_h;
int max_src_w, max_src_h, max_dst_w, max_dst_h;
@@ -207,6 +209,21 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
return -EINVAL;
}
+ /*
+ * The pipe scaler does not use all the bits of PIPESRC, at least
+ * on the earlier platforms. So even when we're scaling a plane
+ * the *pipe* source size must not be too large. For simplicity
+ * we assume the limits match the scaler source size limits. Might
+ * not be 100% accurate on all platforms, but good enough for now.
+ */
+ if (pipe_src_w > max_src_w || pipe_src_h > max_src_h) {
+ drm_dbg_kms(&dev_priv->drm,
+ "scaler_user index %u.%u: pipe src size %ux%u "
+ "is out of scaler range\n",
+ crtc->pipe, scaler_user, pipe_src_w, pipe_src_h);
+ return -EINVAL;
+ }
+
/* mark this plane as a scaler user in crtc_state */
scaler_state->scaler_users |= (1 << scaler_user);
drm_dbg_kms(&dev_priv->drm, "scaler_user index %u.%u: "
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index ce55b8f09301..8ea0598a5a07 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -17,7 +17,6 @@
#include "intel_fb.h"
#include "intel_fbc.h"
#include "intel_psr.h"
-#include "intel_sprite.h"
#include "skl_scaler.h"
#include "skl_universal_plane.h"
#include "skl_watermark.h"
@@ -2476,6 +2475,12 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
goto error;
}
+ if (!dev_priv->params.enable_dpt &&
+ intel_fb_modifier_uses_dpt(dev_priv, fb->modifier)) {
+ drm_dbg_kms(&dev_priv->drm, "DPT disabled, skipping initial FB\n");
+ goto error;
+ }
+
/*
* DRM_MODE_ROTATE_ is counter clockwise to stay compatible with Xrandr
* while i915 HW rotation is clockwise, thats why this swapping.
diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c
index f0af997d2a23..1c7e6468f3e3 100644
--- a/drivers/gpu/drm/i915/display/skl_watermark.c
+++ b/drivers/gpu/drm/i915/display/skl_watermark.c
@@ -12,6 +12,7 @@
#include "intel_atomic.h"
#include "intel_atomic_plane.h"
#include "intel_bw.h"
+#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display.h"
#include "intel_display_power.h"
@@ -20,6 +21,7 @@
#include "intel_pcode.h"
#include "intel_wm.h"
#include "skl_watermark.h"
+#include "skl_watermark_regs.h"
static void skl_sagv_disable(struct drm_i915_private *i915);
@@ -410,6 +412,9 @@ static bool intel_crtc_can_enable_sagv(const struct intel_crtc_state *crtc_state
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ if (!i915->params.enable_sagv)
+ return false;
+
if (DISPLAY_VER(i915) >= 12)
return tgl_crtc_can_enable_sagv(crtc_state);
else
@@ -704,6 +709,28 @@ static void skl_compute_plane_wm(const struct intel_crtc_state *crtc_state,
const struct skl_wm_level *result_prev,
struct skl_wm_level *result /* out */);
+static unsigned int skl_wm_latency(struct drm_i915_private *i915, int level,
+ const struct skl_wm_params *wp)
+{
+ unsigned int latency = i915->display.wm.skl_latency[level];
+
+ if (latency == 0)
+ return 0;
+
+ /*
+ * WaIncreaseLatencyIPCEnabled: kbl,cfl
+ * Display WA #1141: kbl,cfl
+ */
+ if ((IS_KABYLAKE(i915) || IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) &&
+ skl_watermark_ipc_enabled(i915))
+ latency += 4;
+
+ if (skl_needs_memory_bw_wa(i915) && wp && wp->x_tiled)
+ latency += 15;
+
+ return latency;
+}
+
static unsigned int
skl_cursor_allocation(const struct intel_crtc_state *crtc_state,
int num_active)
@@ -723,7 +750,7 @@ skl_cursor_allocation(const struct intel_crtc_state *crtc_state,
drm_WARN_ON(&i915->drm, ret);
for (level = 0; level < i915->display.wm.num_levels; level++) {
- unsigned int latency = i915->display.wm.skl_latency[level];
+ unsigned int latency = skl_wm_latency(i915, level, &wp);
skl_compute_plane_wm(crtc_state, plane, level, latency, &wp, &wm, &wm);
if (wm.min_ddb_alloc == U16_MAX)
@@ -1839,17 +1866,6 @@ static void skl_compute_plane_wm(const struct intel_crtc_state *crtc_state,
return;
}
- /*
- * WaIncreaseLatencyIPCEnabled: kbl,cfl
- * Display WA #1141: kbl,cfl
- */
- if ((IS_KABYLAKE(i915) || IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) &&
- skl_watermark_ipc_enabled(i915))
- latency += 4;
-
- if (skl_needs_memory_bw_wa(i915) && wp->x_tiled)
- latency += 15;
-
method1 = skl_wm_method1(i915, wp->plane_pixel_rate,
wp->cpp, latency, wp->dbuf_block_size);
method2 = skl_wm_method2(wp->plane_pixel_rate,
@@ -1976,7 +1992,7 @@ skl_compute_wm_levels(const struct intel_crtc_state *crtc_state,
for (level = 0; level < i915->display.wm.num_levels; level++) {
struct skl_wm_level *result = &levels[level];
- unsigned int latency = i915->display.wm.skl_latency[level];
+ unsigned int latency = skl_wm_latency(i915, level, wm_params);
skl_compute_plane_wm(crtc_state, plane, level, latency,
wm_params, result_prev, result);
@@ -1996,7 +2012,8 @@ static void tgl_compute_sagv_wm(const struct intel_crtc_state *crtc_state,
unsigned int latency = 0;
if (i915->display.sagv.block_time_us)
- latency = i915->display.sagv.block_time_us + i915->display.wm.skl_latency[0];
+ latency = i915->display.sagv.block_time_us +
+ skl_wm_latency(i915, 0, wm_params);
skl_compute_plane_wm(crtc_state, plane, 0, latency,
wm_params, &levels[0],
@@ -2188,6 +2205,117 @@ static int icl_build_plane_wm(struct intel_crtc_state *crtc_state,
return 0;
}
+static bool
+skl_is_vblank_too_short(const struct intel_crtc_state *crtc_state,
+ int wm0_lines, int latency)
+{
+ const struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
+
+ /* FIXME missing scaler and DSC pre-fill time */
+ return crtc_state->framestart_delay +
+ intel_usecs_to_scanlines(adjusted_mode, latency) +
+ wm0_lines >
+ adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vblank_start;
+}
+
+static int skl_max_wm0_lines(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ enum plane_id plane_id;
+ int wm0_lines = 0;
+
+ for_each_plane_id_on_crtc(crtc, plane_id) {
+ const struct skl_plane_wm *wm = &crtc_state->wm.skl.optimal.planes[plane_id];
+
+ /* FIXME what about !skl_wm_has_lines() platforms? */
+ wm0_lines = max_t(int, wm0_lines, wm->wm[0].lines);
+ }
+
+ return wm0_lines;
+}
+
+static int skl_max_wm_level_for_vblank(struct intel_crtc_state *crtc_state,
+ int wm0_lines)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ int level;
+
+ for (level = i915->display.wm.num_levels - 1; level >= 0; level--) {
+ int latency;
+
+ /* FIXME should we care about the latency w/a's? */
+ latency = skl_wm_latency(i915, level, NULL);
+ if (latency == 0)
+ continue;
+
+ /* FIXME is it correct to use 0 latency for wm0 here? */
+ if (level == 0)
+ latency = 0;
+
+ if (!skl_is_vblank_too_short(crtc_state, wm0_lines, latency))
+ return level;
+ }
+
+ return -EINVAL;
+}
+
+static int skl_wm_check_vblank(struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ int wm0_lines, level;
+
+ if (!crtc_state->hw.active)
+ return 0;
+
+ wm0_lines = skl_max_wm0_lines(crtc_state);
+
+ level = skl_max_wm_level_for_vblank(crtc_state, wm0_lines);
+ if (level < 0)
+ return level;
+
+ /*
+ * PSR needs to toggle LATENCY_REPORTING_REMOVED_PIPE_*
+ * based on whether we're limited by the vblank duration.
+ */
+ crtc_state->wm_level_disabled = level < i915->display.wm.num_levels - 1;
+
+ for (level++; level < i915->display.wm.num_levels; level++) {
+ enum plane_id plane_id;
+
+ for_each_plane_id_on_crtc(crtc, plane_id) {
+ struct skl_plane_wm *wm =
+ &crtc_state->wm.skl.optimal.planes[plane_id];
+
+ /*
+ * FIXME just clear enable or flag the entire
+ * thing as bad via min_ddb_alloc=U16_MAX?
+ */
+ wm->wm[level].enable = false;
+ wm->uv_wm[level].enable = false;
+ }
+ }
+
+ if (DISPLAY_VER(i915) >= 12 &&
+ i915->display.sagv.block_time_us &&
+ skl_is_vblank_too_short(crtc_state, wm0_lines,
+ i915->display.sagv.block_time_us)) {
+ enum plane_id plane_id;
+
+ for_each_plane_id_on_crtc(crtc, plane_id) {
+ struct skl_plane_wm *wm =
+ &crtc_state->wm.skl.optimal.planes[plane_id];
+
+ wm->sagv.wm0.enable = false;
+ wm->sagv.trans_wm.enable = false;
+ }
+ }
+
+ return 0;
+}
+
static int skl_build_pipe_wm(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
@@ -2217,7 +2345,7 @@ static int skl_build_pipe_wm(struct intel_atomic_state *state,
crtc_state->wm.skl.optimal = crtc_state->wm.skl.raw;
- return 0;
+ return skl_wm_check_vblank(crtc_state);
}
static void skl_ddb_entry_write(struct drm_i915_private *i915,
@@ -3570,6 +3698,7 @@ static int intel_sagv_status_show(struct seq_file *m, void *unused)
};
seq_printf(m, "SAGV available: %s\n", str_yes_no(intel_has_sagv(i915)));
+ seq_printf(m, "SAGV modparam: %s\n", str_enabled_disabled(i915->params.enable_sagv));
seq_printf(m, "SAGV status: %s\n", sagv_status[i915->display.sagv.status]);
seq_printf(m, "SAGV block time: %d usec\n", i915->display.sagv.block_time_us);
diff --git a/drivers/gpu/drm/i915/display/skl_watermark_regs.h b/drivers/gpu/drm/i915/display/skl_watermark_regs.h
new file mode 100644
index 000000000000..628c5920ad49
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/skl_watermark_regs.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __SKL_WATERMARK_REGS_H__
+#define __SKL_WATERMARK_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+#define _PIPEA_MBUS_DBOX_CTL 0x7003C
+#define _PIPEB_MBUS_DBOX_CTL 0x7103C
+#define PIPE_MBUS_DBOX_CTL(pipe) _MMIO_PIPE(pipe, _PIPEA_MBUS_DBOX_CTL, \
+ _PIPEB_MBUS_DBOX_CTL)
+#define MBUS_DBOX_B2B_TRANSACTIONS_MAX_MASK REG_GENMASK(24, 20) /* tgl+ */
+#define MBUS_DBOX_B2B_TRANSACTIONS_MAX(x) REG_FIELD_PREP(MBUS_DBOX_B2B_TRANSACTIONS_MAX_MASK, x)
+#define MBUS_DBOX_B2B_TRANSACTIONS_DELAY_MASK REG_GENMASK(19, 17) /* tgl+ */
+#define MBUS_DBOX_B2B_TRANSACTIONS_DELAY(x) REG_FIELD_PREP(MBUS_DBOX_B2B_TRANSACTIONS_DELAY_MASK, x)
+#define MBUS_DBOX_REGULATE_B2B_TRANSACTIONS_EN REG_BIT(16) /* tgl+ */
+#define MBUS_DBOX_BW_CREDIT_MASK REG_GENMASK(15, 14)
+#define MBUS_DBOX_BW_CREDIT(x) REG_FIELD_PREP(MBUS_DBOX_BW_CREDIT_MASK, x)
+#define MBUS_DBOX_BW_4CREDITS_MTL REG_FIELD_PREP(MBUS_DBOX_BW_CREDIT_MASK, 0x2)
+#define MBUS_DBOX_BW_8CREDITS_MTL REG_FIELD_PREP(MBUS_DBOX_BW_CREDIT_MASK, 0x3)
+#define MBUS_DBOX_B_CREDIT_MASK REG_GENMASK(12, 8)
+#define MBUS_DBOX_B_CREDIT(x) REG_FIELD_PREP(MBUS_DBOX_B_CREDIT_MASK, x)
+#define MBUS_DBOX_I_CREDIT_MASK REG_GENMASK(7, 5)
+#define MBUS_DBOX_I_CREDIT(x) REG_FIELD_PREP(MBUS_DBOX_I_CREDIT_MASK, x)
+#define MBUS_DBOX_A_CREDIT_MASK REG_GENMASK(3, 0)
+#define MBUS_DBOX_A_CREDIT(x) REG_FIELD_PREP(MBUS_DBOX_A_CREDIT_MASK, x)
+
+#define MBUS_UBOX_CTL _MMIO(0x4503C)
+#define MBUS_BBOX_CTL_S1 _MMIO(0x45040)
+#define MBUS_BBOX_CTL_S2 _MMIO(0x45044)
+
+#define MBUS_CTL _MMIO(0x4438C)
+#define MBUS_JOIN REG_BIT(31)
+#define MBUS_HASHING_MODE_MASK REG_BIT(30)
+#define MBUS_HASHING_MODE_2x2 REG_FIELD_PREP(MBUS_HASHING_MODE_MASK, 0)
+#define MBUS_HASHING_MODE_1x4 REG_FIELD_PREP(MBUS_HASHING_MODE_MASK, 1)
+#define MBUS_JOIN_PIPE_SELECT_MASK REG_GENMASK(28, 26)
+#define MBUS_JOIN_PIPE_SELECT(pipe) REG_FIELD_PREP(MBUS_JOIN_PIPE_SELECT_MASK, pipe)
+#define MBUS_JOIN_PIPE_SELECT_NONE MBUS_JOIN_PIPE_SELECT(7)
+
+/* Watermark register definitions for SKL */
+#define _CUR_WM_A_0 0x70140
+#define _CUR_WM_B_0 0x71140
+#define _CUR_WM_SAGV_A 0x70158
+#define _CUR_WM_SAGV_B 0x71158
+#define _CUR_WM_SAGV_TRANS_A 0x7015C
+#define _CUR_WM_SAGV_TRANS_B 0x7115C
+#define _CUR_WM_TRANS_A 0x70168
+#define _CUR_WM_TRANS_B 0x71168
+#define _PLANE_WM_1_A_0 0x70240
+#define _PLANE_WM_1_B_0 0x71240
+#define _PLANE_WM_2_A_0 0x70340
+#define _PLANE_WM_2_B_0 0x71340
+#define _PLANE_WM_SAGV_1_A 0x70258
+#define _PLANE_WM_SAGV_1_B 0x71258
+#define _PLANE_WM_SAGV_2_A 0x70358
+#define _PLANE_WM_SAGV_2_B 0x71358
+#define _PLANE_WM_SAGV_TRANS_1_A 0x7025C
+#define _PLANE_WM_SAGV_TRANS_1_B 0x7125C
+#define _PLANE_WM_SAGV_TRANS_2_A 0x7035C
+#define _PLANE_WM_SAGV_TRANS_2_B 0x7135C
+#define _PLANE_WM_TRANS_1_A 0x70268
+#define _PLANE_WM_TRANS_1_B 0x71268
+#define _PLANE_WM_TRANS_2_A 0x70368
+#define _PLANE_WM_TRANS_2_B 0x71368
+#define PLANE_WM_EN (1 << 31)
+#define PLANE_WM_IGNORE_LINES (1 << 30)
+#define PLANE_WM_LINES_MASK REG_GENMASK(26, 14)
+#define PLANE_WM_BLOCKS_MASK REG_GENMASK(11, 0)
+
+#define _CUR_WM_0(pipe) _PIPE(pipe, _CUR_WM_A_0, _CUR_WM_B_0)
+#define CUR_WM(pipe, level) _MMIO(_CUR_WM_0(pipe) + ((4) * (level)))
+#define CUR_WM_SAGV(pipe) _MMIO_PIPE(pipe, _CUR_WM_SAGV_A, _CUR_WM_SAGV_B)
+#define CUR_WM_SAGV_TRANS(pipe) _MMIO_PIPE(pipe, _CUR_WM_SAGV_TRANS_A, _CUR_WM_SAGV_TRANS_B)
+#define CUR_WM_TRANS(pipe) _MMIO_PIPE(pipe, _CUR_WM_TRANS_A, _CUR_WM_TRANS_B)
+#define _PLANE_WM_1(pipe) _PIPE(pipe, _PLANE_WM_1_A_0, _PLANE_WM_1_B_0)
+#define _PLANE_WM_2(pipe) _PIPE(pipe, _PLANE_WM_2_A_0, _PLANE_WM_2_B_0)
+#define _PLANE_WM_BASE(pipe, plane) \
+ _PLANE(plane, _PLANE_WM_1(pipe), _PLANE_WM_2(pipe))
+#define PLANE_WM(pipe, plane, level) \
+ _MMIO(_PLANE_WM_BASE(pipe, plane) + ((4) * (level)))
+#define _PLANE_WM_SAGV_1(pipe) \
+ _PIPE(pipe, _PLANE_WM_SAGV_1_A, _PLANE_WM_SAGV_1_B)
+#define _PLANE_WM_SAGV_2(pipe) \
+ _PIPE(pipe, _PLANE_WM_SAGV_2_A, _PLANE_WM_SAGV_2_B)
+#define PLANE_WM_SAGV(pipe, plane) \
+ _MMIO(_PLANE(plane, _PLANE_WM_SAGV_1(pipe), _PLANE_WM_SAGV_2(pipe)))
+#define _PLANE_WM_SAGV_TRANS_1(pipe) \
+ _PIPE(pipe, _PLANE_WM_SAGV_TRANS_1_A, _PLANE_WM_SAGV_TRANS_1_B)
+#define _PLANE_WM_SAGV_TRANS_2(pipe) \
+ _PIPE(pipe, _PLANE_WM_SAGV_TRANS_2_A, _PLANE_WM_SAGV_TRANS_2_B)
+#define PLANE_WM_SAGV_TRANS(pipe, plane) \
+ _MMIO(_PLANE(plane, _PLANE_WM_SAGV_TRANS_1(pipe), _PLANE_WM_SAGV_TRANS_2(pipe)))
+#define _PLANE_WM_TRANS_1(pipe) \
+ _PIPE(pipe, _PLANE_WM_TRANS_1_A, _PLANE_WM_TRANS_1_B)
+#define _PLANE_WM_TRANS_2(pipe) \
+ _PIPE(pipe, _PLANE_WM_TRANS_2_A, _PLANE_WM_TRANS_2_B)
+#define PLANE_WM_TRANS(pipe, plane) \
+ _MMIO(_PLANE(plane, _PLANE_WM_TRANS_1(pipe), _PLANE_WM_TRANS_2(pipe)))
+
+#define _PLANE_BUF_CFG_1_B 0x7127c
+#define _PLANE_BUF_CFG_2_B 0x7137c
+#define _PLANE_BUF_CFG_1(pipe) \
+ _PIPE(pipe, _PLANE_BUF_CFG_1_A, _PLANE_BUF_CFG_1_B)
+#define _PLANE_BUF_CFG_2(pipe) \
+ _PIPE(pipe, _PLANE_BUF_CFG_2_A, _PLANE_BUF_CFG_2_B)
+#define PLANE_BUF_CFG(pipe, plane) \
+ _MMIO_PLANE(plane, _PLANE_BUF_CFG_1(pipe), _PLANE_BUF_CFG_2(pipe))
+
+#define _PLANE_NV12_BUF_CFG_1_B 0x71278
+#define _PLANE_NV12_BUF_CFG_2_B 0x71378
+#define _PLANE_NV12_BUF_CFG_1(pipe) \
+ _PIPE(pipe, _PLANE_NV12_BUF_CFG_1_A, _PLANE_NV12_BUF_CFG_1_B)
+#define _PLANE_NV12_BUF_CFG_2(pipe) \
+ _PIPE(pipe, _PLANE_NV12_BUF_CFG_2_A, _PLANE_NV12_BUF_CFG_2_B)
+#define PLANE_NV12_BUF_CFG(pipe, plane) \
+ _MMIO_PLANE(plane, _PLANE_NV12_BUF_CFG_1(pipe), _PLANE_NV12_BUF_CFG_2(pipe))
+
+/* SKL new cursor registers */
+#define _CUR_BUF_CFG_A 0x7017c
+#define _CUR_BUF_CFG_B 0x7117c
+#define CUR_BUF_CFG(pipe) _MMIO_PIPE(pipe, _CUR_BUF_CFG_A, _CUR_BUF_CFG_B)
+
+/*
+ * The below are numbered starting from "S1" on gen11/gen12, but starting
+ * with display 13, the bspec switches to a 0-based numbering scheme
+ * (although the addresses stay the same so new S0 = old S1, new S1 = old S2).
+ * We'll just use the 0-based numbering here for all platforms since it's the
+ * way things will be named by the hardware team going forward, plus it's more
+ * consistent with how most of the rest of our registers are named.
+ */
+#define _DBUF_CTL_S0 0x45008
+#define _DBUF_CTL_S1 0x44FE8
+#define _DBUF_CTL_S2 0x44300
+#define _DBUF_CTL_S3 0x44304
+#define DBUF_CTL_S(slice) _MMIO(_PICK(slice, \
+ _DBUF_CTL_S0, \
+ _DBUF_CTL_S1, \
+ _DBUF_CTL_S2, \
+ _DBUF_CTL_S3))
+#define DBUF_POWER_REQUEST REG_BIT(31)
+#define DBUF_POWER_STATE REG_BIT(30)
+#define DBUF_TRACKER_STATE_SERVICE_MASK REG_GENMASK(23, 19)
+#define DBUF_TRACKER_STATE_SERVICE(x) REG_FIELD_PREP(DBUF_TRACKER_STATE_SERVICE_MASK, x)
+#define DBUF_MIN_TRACKER_STATE_SERVICE_MASK REG_GENMASK(18, 16) /* ADL-P+ */
+#define DBUF_MIN_TRACKER_STATE_SERVICE(x) REG_FIELD_PREP(DBUF_MIN_TRACKER_STATE_SERVICE_MASK, x) /* ADL-P+ */
+
+#define MTL_LATENCY_LP0_LP1 _MMIO(0x45780)
+#define MTL_LATENCY_LP2_LP3 _MMIO(0x45784)
+#define MTL_LATENCY_LP4_LP5 _MMIO(0x45788)
+#define MTL_LATENCY_LEVEL_EVEN_MASK REG_GENMASK(12, 0)
+#define MTL_LATENCY_LEVEL_ODD_MASK REG_GENMASK(28, 16)
+
+#define MTL_LATENCY_SAGV _MMIO(0x4578c)
+#define MTL_LATENCY_QCLK_SAGV REG_GENMASK(12, 0)
+
+#endif /* __SKL_WATERMARK_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
index 8d2e6e151ba0..61d008d4e5f1 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
@@ -737,7 +737,6 @@ static void intel_dsi_pre_enable(struct intel_atomic_state *state,
{
struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder);
struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
- struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
enum pipe pipe = crtc->pipe;
enum port port;
@@ -779,21 +778,10 @@ static void intel_dsi_pre_enable(struct intel_atomic_state *state,
if (!IS_GEMINILAKE(dev_priv))
intel_dsi_prepare(encoder, pipe_config);
+ /* Give the panel time to power-on and then deassert its reset */
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_POWER_ON);
-
- /*
- * Give the panel time to power-on and then deassert its reset.
- * Depending on the VBT MIPI sequences version the deassert-seq
- * may contain the necessary delay, intel_dsi_msleep() will skip
- * the delay in that case. If there is no deassert-seq, then an
- * unconditional msleep is used to give the panel time to power-on.
- */
- if (connector->panel.vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET]) {
- intel_dsi_msleep(intel_dsi, intel_dsi->panel_on_delay);
- intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET);
- } else {
- msleep(intel_dsi->panel_on_delay);
- }
+ msleep(intel_dsi->panel_on_delay);
+ intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET);
if (IS_GEMINILAKE(dev_priv)) {
glk_cold_boot = glk_dsi_enable_io(encoder);
@@ -827,7 +815,7 @@ static void intel_dsi_pre_enable(struct intel_atomic_state *state,
msleep(20); /* XXX */
for_each_dsi_port(port, intel_dsi->ports)
dpi_send_cmd(intel_dsi, TURN_ON, false, port);
- intel_dsi_msleep(intel_dsi, 100);
+ msleep(100);
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON);
@@ -949,7 +937,7 @@ static void intel_dsi_post_disable(struct intel_atomic_state *state,
/* Assert reset */
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_ASSERT_RESET);
- intel_dsi_msleep(intel_dsi, intel_dsi->panel_off_delay);
+ msleep(intel_dsi->panel_off_delay);
intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_POWER_OFF);
intel_dsi->panel_power_off_time = ktime_get_boottime();
@@ -1072,7 +1060,7 @@ static void bxt_dsi_get_pipe_config(struct intel_encoder *encoder,
bpp = mipi_dsi_pixel_format_to_bpp(
pixel_format_from_register_bits(fmt));
- pipe_config->pipe_bpp = bdw_get_pipemisc_bpp(crtc);
+ pipe_config->pipe_bpp = bdw_get_pipe_misc_bpp(crtc);
/* Enable Frame time stamo based scanline reporting */
pipe_config->mode_flags |=