summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display/intel_hdmi.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-03-24 16:19:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-03-24 16:19:43 -0700
commitb14ffae378aa1db993e62b01392e70d1e585fb23 (patch)
tree0ac179d24e8a62ec4c2732ed18d90d83da4b82d7 /drivers/gpu/drm/i915/display/intel_hdmi.c
parent52deda9551a01879b3562e7b41748e85c591f14c (diff)
parentc6e90a1c660874736bd09c1fec6312b4b4c2ff7b (diff)
Merge tag 'drm-next-2022-03-24' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie: "Lots of work all over, Intel improving DG2 support, amdkfd CRIU support, msm new hw support, and faster fbdev support. dma-buf: - rename dma-buf-map to iosys-map core: - move buddy allocator to core - add pci/platform init macros - improve EDID parser deep color handling - EDID timing type 7 support - add GPD Win Max quirk - add yes/no helpers to string_helpers - flatten syncobj chains - add nomodeset support to lots of drivers - improve fb-helper clipping support - add default property value interface fbdev: - improve fbdev ops speed ttm: - add a backpointer from ttm bo->ttm resource dp: - move displayport headers - add a dp helper module bridge: - anx7625 atomic support, HDCP support panel: - split out panel-lvds and lvds bindings - find panels in OF subnodes privacy: - add chromeos privacy screen support fb: - hot unplug fw fb on forced removal simpledrm: - request region instead of marking ioresource busy - add panel oreintation property udmabuf: - fix oops with 0 pages amdgpu: - power management code cleanup - Enable freesync video mode by default - RAS code cleanup - Improve VRAM access for debug using SDMA - SR-IOV rework special register access and fixes - profiling power state request ioctl - expose IP discovery via sysfs - Cyan skillfish updates - GC 10.3.7, SDMA 5.2.7, DCN 3.1.6 updates - expose benchmark tests via debugfs - add module param to disable XGMI for testing - GPU reset debugfs register dumping support amdkfd: - CRIU support - SDMA queue fixes radeon: - UVD suspend fix - iMac backlight fix i915: - minimal parallel submission for execlists - DG2-G12 subplatform added - DG2 programming workarounds - DG2 accelerated migration support - flat CCS and CCS engine support for XeHP - initial small BAR support - drop fake LMEM support - ADL-N PCH support - bigjoiner updates - introduce VMA resources and async unbinding - register definitions cleanups - multi-FBC refactoring - DG1 OPROM over SPI support - ADL-N platform enabling - opregion mailbox #5 support - DP MST ESI improvements - drm device based logging - async flip optimisation for DG2 - CPU arch abstraction fixes - improve GuC ADS init to work on aarch64 - tweak TTM LRU priority hint - GuC 69.0.3 support - remove short term execbuf pins nouveau: - higher DP/eDP bitrates - backlight fixes msm: - dpu + dp support for sc8180x - dp support for sm8350 - dpu + dsi support for qcm2290 - 10nm dsi phy tuning support - bridge support for dp encoder - gpu support for additional 7c3 SKUs ingenic: - HDMI support for JZ4780 - aux channel EDID support ast: - AST2600 support - add wide screen support - create DP/DVI connectors omapdrm: - fix implicit dma_buf fencing vc4: - add CSC + full range support - better display firmware handoff panfrost: - add initial dual-core GPU support stm: - new revision support - fb handover support mediatek: - transfer display binding document to yaml format. - add mt8195 display device binding. - allow commands to be sent during video mode. - add wait_for_event for crtc disable by cmdq. tegra: - YUV format support rcar-du: - LVDS support for M3-W+ (R8A77961) exynos: - BGR pixel format for FIMD device" * tag 'drm-next-2022-03-24' of git://anongit.freedesktop.org/drm/drm: (1529 commits) drm/i915/display: Do not re-enable PSR after it was marked as not reliable drm/i915/display: Fix HPD short pulse handling for eDP drm/amdgpu: Use drm_mode_copy() drm/radeon: Use drm_mode_copy() drm/amdgpu: Use ternary operator in `vcn_v1_0_start()` drm/amdgpu: Remove pointless on stack mode copies drm/amd/pm: fix indenting in __smu_cmn_reg_print_error() drm/amdgpu/dc: fix typos in comments drm/amdgpu: fix typos in comments drm/amd/pm: fix typos in comments drm/amdgpu: Add stolen reserved memory for MI25 SRIOV. drm/amdgpu: Merge get_reserved_allocation to get_vbios_allocations. drm/amdkfd: evict svm bo worker handle error drm/amdgpu/vcn: fix vcn ring test failure in igt reload test drm/amdgpu: only allow secure submission on rings which support that drm/amdgpu: fixed the warnings reported by kernel test robot drm/amd/display: 3.2.177 drm/amd/display: [FW Promotion] Release 0.0.108.0 drm/amd/display: Add save/restore PANEL_PWRSEQ_REF_DIV2 drm/amd/display: Wait for hubp read line for Pollock ...
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_hdmi.c')
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.c145
1 files changed, 87 insertions, 58 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 3b5b9e7b05b7..1aa5bdc7b0dc 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -730,7 +730,7 @@ intel_hdmi_compute_avi_infoframe(struct intel_encoder *encoder,
else
frame->colorspace = HDMI_COLORSPACE_RGB;
- drm_hdmi_avi_infoframe_colorspace(frame, conn_state);
+ drm_hdmi_avi_infoframe_colorimetry(frame, conn_state);
/* nonsense combination */
drm_WARN_ON(encoder->base.dev, crtc_state->limited_color_range &&
@@ -1869,7 +1869,7 @@ hdmi_port_clock_valid(struct intel_hdmi *hdmi,
return MODE_OK;
}
-static int intel_hdmi_tmds_clock(int clock, int bpc, bool ycbcr420_output)
+int intel_hdmi_tmds_clock(int clock, int bpc, bool ycbcr420_output)
{
/* YCBCR420 TMDS rate requirement is half the pixel clock */
if (ycbcr420_output)
@@ -1912,7 +1912,7 @@ static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector,
if (ycbcr420_output)
return hdmi->y420_dc_modes & DRM_EDID_YCBCR420_DC_36;
else
- return info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36;
+ return info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36;
case 10:
if (!has_hdmi_sink)
return false;
@@ -1920,7 +1920,7 @@ static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector,
if (ycbcr420_output)
return hdmi->y420_dc_modes & DRM_EDID_YCBCR420_DC_30;
else
- return info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30;
+ return info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30;
case 8:
return true;
default:
@@ -1935,25 +1935,30 @@ intel_hdmi_mode_clock_valid(struct drm_connector *connector, int clock,
{
struct drm_i915_private *i915 = to_i915(connector->dev);
struct intel_hdmi *hdmi = intel_attached_hdmi(to_intel_connector(connector));
- enum drm_mode_status status;
+ enum drm_mode_status status = MODE_OK;
+ int bpc;
+
+ /*
+ * Try all color depths since valid port clock range
+ * can have holes. Any mode that can be used with at
+ * least one color depth is accepted.
+ */
+ for (bpc = 12; bpc >= 8; bpc -= 2) {
+ int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, ycbcr420_output);
- /* check if we can do 8bpc */
- status = hdmi_port_clock_valid(hdmi, intel_hdmi_tmds_clock(clock, 8, ycbcr420_output),
- true, has_hdmi_sink);
+ if (!intel_hdmi_source_bpc_possible(i915, bpc))
+ continue;
+
+ if (!intel_hdmi_sink_bpc_possible(connector, bpc, has_hdmi_sink, ycbcr420_output))
+ continue;
- /* if we can't do 8bpc we may still be able to do 12bpc */
- if (status != MODE_OK &&
- intel_hdmi_source_bpc_possible(i915, 12) &&
- intel_hdmi_sink_bpc_possible(connector, 12, has_hdmi_sink, ycbcr420_output))
- status = hdmi_port_clock_valid(hdmi, intel_hdmi_tmds_clock(clock, 12, ycbcr420_output),
- true, has_hdmi_sink);
+ status = hdmi_port_clock_valid(hdmi, tmds_clock, true, has_hdmi_sink);
+ if (status == MODE_OK)
+ return MODE_OK;
+ }
- /* if we can't do 8,12bpc we may still be able to do 10bpc */
- if (status != MODE_OK &&
- intel_hdmi_source_bpc_possible(i915, 10) &&
- intel_hdmi_sink_bpc_possible(connector, 10, has_hdmi_sink, ycbcr420_output))
- status = hdmi_port_clock_valid(hdmi, intel_hdmi_tmds_clock(clock, 10, ycbcr420_output),
- true, has_hdmi_sink);
+ /* can never happen */
+ drm_WARN_ON(&i915->drm, status == MODE_OK);
return status;
}
@@ -2002,17 +2007,14 @@ intel_hdmi_mode_valid(struct drm_connector *connector,
return intel_mode_valid_max_plane_size(dev_priv, mode, false);
}
-bool intel_hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
- int bpc, bool has_hdmi_sink, bool ycbcr420_output)
+bool intel_hdmi_bpc_possible(const struct intel_crtc_state *crtc_state,
+ int bpc, bool has_hdmi_sink, bool ycbcr420_output)
{
struct drm_atomic_state *state = crtc_state->uapi.state;
struct drm_connector_state *connector_state;
struct drm_connector *connector;
int i;
- if (crtc_state->pipe_bpp < bpc * 3)
- return false;
-
for_each_new_connector_in_state(state, connector, connector_state, i) {
if (connector_state->crtc != crtc_state->uapi.crtc)
continue;
@@ -2024,8 +2026,7 @@ bool intel_hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
return true;
}
-static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
- int bpc)
+static bool hdmi_bpc_possible(const struct intel_crtc_state *crtc_state, int bpc)
{
struct drm_i915_private *dev_priv =
to_i915(crtc_state->uapi.crtc->dev);
@@ -2039,7 +2040,7 @@ static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
* HDMI deep color affects the clocks, so it's only possible
* when not cloning with other encoder types.
*/
- if (crtc_state->output_types != BIT(INTEL_OUTPUT_HDMI))
+ if (bpc > 8 && crtc_state->output_types != BIT(INTEL_OUTPUT_HDMI))
return false;
/* Display Wa_1405510057:icl,ehl */
@@ -2049,35 +2050,50 @@ static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
adjusted_mode->crtc_hblank_start) % 8 == 2)
return false;
- return intel_hdmi_deep_color_possible(crtc_state, bpc,
- crtc_state->has_hdmi_sink,
- intel_hdmi_is_ycbcr420(crtc_state));
+ return intel_hdmi_bpc_possible(crtc_state, bpc, crtc_state->has_hdmi_sink,
+ intel_hdmi_is_ycbcr420(crtc_state));
}
static int intel_hdmi_compute_bpc(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
- int clock)
+ int clock, bool respect_downstream_limits)
{
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
bool ycbcr420_output = intel_hdmi_is_ycbcr420(crtc_state);
int bpc;
- for (bpc = 12; bpc >= 10; bpc -= 2) {
- if (hdmi_deep_color_possible(crtc_state, bpc) &&
- hdmi_port_clock_valid(intel_hdmi,
- intel_hdmi_tmds_clock(clock, bpc, ycbcr420_output),
- true, crtc_state->has_hdmi_sink) == MODE_OK)
+ /*
+ * pipe_bpp could already be below 8bpc due to FDI
+ * bandwidth constraints. HDMI minimum is 8bpc however.
+ */
+ bpc = max(crtc_state->pipe_bpp / 3, 8);
+
+ /*
+ * We will never exceed downstream TMDS clock limits while
+ * attempting deep color. If the user insists on forcing an
+ * out of spec mode they will have to be satisfied with 8bpc.
+ */
+ if (!respect_downstream_limits)
+ bpc = 8;
+
+ for (; bpc >= 8; bpc -= 2) {
+ int tmds_clock = intel_hdmi_tmds_clock(clock, bpc, ycbcr420_output);
+
+ if (hdmi_bpc_possible(crtc_state, bpc) &&
+ hdmi_port_clock_valid(intel_hdmi, tmds_clock,
+ respect_downstream_limits,
+ crtc_state->has_hdmi_sink) == MODE_OK)
return bpc;
}
- return 8;
+ return -EINVAL;
}
static int intel_hdmi_compute_clock(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state)
+ struct intel_crtc_state *crtc_state,
+ bool respect_downstream_limits)
{
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
const struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode;
int bpc, clock = adjusted_mode->crtc_clock;
@@ -2085,31 +2101,25 @@ static int intel_hdmi_compute_clock(struct intel_encoder *encoder,
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)
clock *= 2;
- bpc = intel_hdmi_compute_bpc(encoder, crtc_state, clock);
+ bpc = intel_hdmi_compute_bpc(encoder, crtc_state, clock,
+ respect_downstream_limits);
+ if (bpc < 0)
+ return bpc;
- crtc_state->port_clock = intel_hdmi_tmds_clock(clock, bpc,
- intel_hdmi_is_ycbcr420(crtc_state));
+ crtc_state->port_clock =
+ intel_hdmi_tmds_clock(clock, bpc, intel_hdmi_is_ycbcr420(crtc_state));
/*
* pipe_bpp could already be below 8bpc due to
* FDI bandwidth constraints. We shouldn't bump it
- * back up to 8bpc in that case.
+ * back up to the HDMI minimum 8bpc in that case.
*/
- if (crtc_state->pipe_bpp > bpc * 3)
- crtc_state->pipe_bpp = bpc * 3;
+ crtc_state->pipe_bpp = min(crtc_state->pipe_bpp, bpc * 3);
drm_dbg_kms(&i915->drm,
"picking %d bpc for HDMI output (pipe bpp: %d)\n",
bpc, crtc_state->pipe_bpp);
- if (hdmi_port_clock_valid(intel_hdmi, crtc_state->port_clock,
- false, crtc_state->has_hdmi_sink) != MODE_OK) {
- drm_dbg_kms(&i915->drm,
- "unsupported HDMI clock (%d kHz), rejecting mode\n",
- crtc_state->port_clock);
- return -EINVAL;
- }
-
return 0;
}
@@ -2170,7 +2180,8 @@ intel_hdmi_output_format(struct intel_connector *connector,
static int intel_hdmi_compute_output_format(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
- const struct drm_connector_state *conn_state)
+ const struct drm_connector_state *conn_state,
+ bool respect_downstream_limits)
{
struct intel_connector *connector = to_intel_connector(conn_state->connector);
const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
@@ -2187,7 +2198,7 @@ static int intel_hdmi_compute_output_format(struct intel_encoder *encoder,
crtc_state->output_format = INTEL_OUTPUT_FORMAT_RGB;
}
- ret = intel_hdmi_compute_clock(encoder, crtc_state);
+ ret = intel_hdmi_compute_clock(encoder, crtc_state, respect_downstream_limits);
if (ret) {
if (intel_hdmi_is_ycbcr420(crtc_state) ||
!connector->base.ycbcr_420_allowed ||
@@ -2195,7 +2206,7 @@ static int intel_hdmi_compute_output_format(struct intel_encoder *encoder,
return ret;
crtc_state->output_format = intel_hdmi_output_format(connector, true);
- ret = intel_hdmi_compute_clock(encoder, crtc_state);
+ ret = intel_hdmi_compute_clock(encoder, crtc_state, respect_downstream_limits);
}
return ret;
@@ -2231,9 +2242,19 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder,
pipe_config->has_audio =
intel_hdmi_has_audio(encoder, pipe_config, conn_state);
- ret = intel_hdmi_compute_output_format(encoder, pipe_config, conn_state);
+ /*
+ * Try to respect downstream TMDS clock limits first, if
+ * that fails assume the user might know something we don't.
+ */
+ ret = intel_hdmi_compute_output_format(encoder, pipe_config, conn_state, true);
if (ret)
+ ret = intel_hdmi_compute_output_format(encoder, pipe_config, conn_state, false);
+ if (ret) {
+ drm_dbg_kms(&dev_priv->drm,
+ "unsupported HDMI clock (%d kHz), rejecting mode\n",
+ pipe_config->hw.adjusted_mode.crtc_clock);
return ret;
+ }
if (intel_hdmi_is_ycbcr420(pipe_config)) {
ret = intel_panel_fitting(pipe_config, conn_state);
@@ -2359,6 +2380,14 @@ intel_hdmi_dp_dual_mode_detect(struct drm_connector *connector, bool has_edid)
"DP dual mode adaptor (%s) detected (max TMDS clock: %d kHz)\n",
drm_dp_get_dual_mode_type_name(type),
hdmi->dp_dual_mode.max_tmds_clock);
+
+ /* Older VBTs are often buggy and can't be trusted :( Play it safe. */
+ if ((DISPLAY_VER(dev_priv) >= 8 || IS_HASWELL(dev_priv)) &&
+ !intel_bios_is_port_dp_dual_mode(dev_priv, port)) {
+ drm_dbg_kms(&dev_priv->drm,
+ "Ignoring DP dual mode adaptor max TMDS clock for native HDMI port\n");
+ hdmi->dp_dual_mode.max_tmds_clock = 0;
+ }
}
static bool