diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_dp.c')
-rw-r--r-- | drivers/gpu/drm/i915/display/intel_dp.c | 269 |
1 files changed, 179 insertions, 90 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 80d95cec8f9d..f0bace9d98a1 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 @@ -288,7 +289,7 @@ static int intel_dp_max_common_rate(struct intel_dp *intel_dp) static int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) { - int vbt_max_lanes = intel_bios_dp_max_lane_count(&dig_port->base); + int vbt_max_lanes = intel_bios_dp_max_lane_count(dig_port->base.devdata); int max_lanes = dig_port->max_lanes; if (vbt_max_lanes) @@ -425,7 +426,7 @@ static int vbt_max_link_rate(struct intel_dp *intel_dp) struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; int max_rate; - max_rate = intel_bios_dp_max_link_rate(encoder); + max_rate = intel_bios_dp_max_link_rate(encoder->devdata); if (intel_dp_is_edp(intel_dp)) { struct intel_connector *connector = intel_dp->attached_connector; @@ -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; @@ -1415,6 +1442,28 @@ static int intel_dp_sink_dsc_version_minor(struct intel_dp *intel_dp) DP_DSC_MINOR_SHIFT; } +static int intel_dp_get_slice_height(int vactive) +{ + int slice_height; + + /* + * VDSC 1.2a spec in Section 3.8 Options for Slices implies that 108 + * lines is an optimal slice height, but any size can be used as long as + * vertical active integer multiple and maximum vertical slice count + * requirements are met. + */ + for (slice_height = 108; slice_height <= vactive; slice_height += 2) + if (vactive % slice_height == 0) + return slice_height; + + /* + * Highly unlikely we reach here as most of the resolutions will end up + * finding appropriate slice_height in above loop but returning + * slice_height as 2 here as it should work with all resolutions. + */ + return 2; +} + static int intel_dp_dsc_compute_params(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state) { @@ -1433,17 +1482,7 @@ static int intel_dp_dsc_compute_params(struct intel_encoder *encoder, vdsc_cfg->rc_model_size = DSC_RC_MODEL_SIZE_CONST; vdsc_cfg->pic_height = crtc_state->hw.adjusted_mode.crtc_vdisplay; - /* - * Slice Height of 8 works for all currently available panels. So start - * with that if pic_height is an integral multiple of 8. Eventually add - * logic to try multiple slice heights. - */ - if (vdsc_cfg->pic_height % 8 == 0) - vdsc_cfg->slice_height = 8; - else if (vdsc_cfg->pic_height % 4 == 0) - vdsc_cfg->slice_height = 4; - else - vdsc_cfg->slice_height = 2; + vdsc_cfg->slice_height = intel_dp_get_slice_height(vdsc_cfg->pic_height); ret = intel_dsc_compute_params(crtc_state); if (ret) @@ -1455,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) { @@ -1480,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, @@ -1500,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 @@ -1547,6 +1615,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"); @@ -1585,16 +1662,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) { @@ -1727,7 +1796,7 @@ bool intel_dp_limited_color_range(const struct intel_crtc_state *crtc_state, * Our YCbCr output is always limited range. * crtc_state->limited_color_range only applies to RGB, * and it must never be set for YCbCr or we risk setting - * some conflicting bits in PIPECONF which will mess up + * some conflicting bits in TRANSCONF which will mess up * the colors on the monitor. */ if (crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB) @@ -1991,7 +2060,6 @@ intel_dp_drrs_compute_config(struct intel_connector *connector, } static bool intel_dp_has_audio(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); @@ -2057,7 +2125,7 @@ intel_dp_audio_compute_config(struct intel_encoder *encoder, struct drm_connector *connector = conn_state->connector; pipe_config->sdp_split_enable = - intel_dp_has_audio(encoder, pipe_config, conn_state) && + intel_dp_has_audio(encoder, conn_state) && intel_dp_is_uhbr(pipe_config); drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] SDP split enable: %s\n", @@ -2080,7 +2148,9 @@ intel_dp_compute_config(struct intel_encoder *encoder, if (HAS_PCH_SPLIT(dev_priv) && !HAS_DDI(dev_priv) && encoder->port != PORT_A) pipe_config->has_pch_encoder = true; - pipe_config->has_audio = intel_dp_has_audio(encoder, pipe_config, conn_state); + pipe_config->has_audio = + intel_dp_has_audio(encoder, conn_state) && + intel_audio_compute_config(encoder, pipe_config, conn_state); fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode); if (intel_dp_is_edp(intel_dp) && fixed_mode) { @@ -2279,10 +2349,15 @@ intel_edp_init_source_oui(struct intel_dp *intel_dp, bool careful) void intel_dp_wait_source_oui(struct intel_dp *intel_dp) { + struct intel_connector *connector = intel_dp->attached_connector; struct drm_i915_private *i915 = dp_to_i915(intel_dp); - drm_dbg_kms(&i915->drm, "Performing OUI wait\n"); - wait_remaining_ms_from_jiffies(intel_dp->last_oui_write, 30); + drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] Performing OUI wait (%u ms)\n", + connector->base.base.id, connector->base.name, + connector->panel.vbt.backlight.hdr_dpcd_refresh_timeout); + + wait_remaining_ms_from_jiffies(intel_dp->last_oui_write, + connector->panel.vbt.backlight.hdr_dpcd_refresh_timeout); } /* If the device supports it, try to set the power state appropriately */ @@ -3648,12 +3723,11 @@ static u8 intel_dp_autotest_edid(struct intel_dp *intel_dp) intel_dp->aux.i2c_defer_count); intel_dp->compliance.test_data.edid = INTEL_DP_RESOLUTION_FAILSAFE; } else { - struct edid *block = intel_connector->detect_edid; + /* FIXME: Get rid of drm_edid_raw() */ + const struct edid *block = drm_edid_raw(intel_connector->detect_edid); - /* We have to write the checksum - * of the last block read - */ - block += intel_connector->detect_edid->extensions; + /* We have to write the checksum of the last block read */ + block += block->extensions; if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_EDID_CHECKSUM, block->checksum) <= 0) @@ -4475,29 +4549,34 @@ bool intel_digital_port_connected(struct intel_encoder *encoder) return is_connected; } -static struct edid * +static const struct drm_edid * intel_dp_get_edid(struct intel_dp *intel_dp) { - struct intel_connector *intel_connector = intel_dp->attached_connector; + struct intel_connector *connector = intel_dp->attached_connector; + const struct drm_edid *fixed_edid = connector->panel.fixed_edid; - /* use cached edid if we have one */ - if (intel_connector->edid) { + /* Use panel fixed edid if we have one */ + if (fixed_edid) { /* invalid edid */ - if (IS_ERR(intel_connector->edid)) + if (IS_ERR(fixed_edid)) return NULL; - return drm_edid_duplicate(intel_connector->edid); - } else - return drm_get_edid(&intel_connector->base, - &intel_dp->aux.ddc); + return drm_edid_dup(fixed_edid); + } + + return drm_edid_read_ddc(&connector->base, &intel_dp->aux.ddc); } static void intel_dp_update_dfp(struct intel_dp *intel_dp, - const struct edid *edid) + const struct drm_edid *drm_edid) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct intel_connector *connector = intel_dp->attached_connector; + const struct edid *edid; + + /* FIXME: Get rid of drm_edid_raw() */ + edid = drm_edid_raw(drm_edid); intel_dp->dfp.max_bpc = drm_dp_downstream_max_bpc(intel_dp->dpcd, @@ -4597,21 +4676,27 @@ intel_dp_set_edid(struct intel_dp *intel_dp) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct intel_connector *connector = intel_dp->attached_connector; - struct edid *edid; + const struct drm_edid *drm_edid; + const struct edid *edid; bool vrr_capable; intel_dp_unset_edid(intel_dp); - edid = intel_dp_get_edid(intel_dp); - connector->detect_edid = edid; + drm_edid = intel_dp_get_edid(intel_dp); + connector->detect_edid = drm_edid; + + /* Below we depend on display info having been updated */ + drm_edid_connector_update(&connector->base, drm_edid); vrr_capable = intel_vrr_is_capable(connector); drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] VRR capable: %s\n", connector->base.base.id, connector->base.name, str_yes_no(vrr_capable)); drm_connector_set_vrr_capable_property(&connector->base, vrr_capable); - intel_dp_update_dfp(intel_dp, edid); + intel_dp_update_dfp(intel_dp, drm_edid); intel_dp_update_420(intel_dp); + /* FIXME: Get rid of drm_edid_raw() */ + edid = drm_edid_raw(drm_edid); if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { intel_dp->has_hdmi_sink = drm_detect_hdmi_monitor(edid); intel_dp->has_audio = drm_detect_monitor_audio(edid); @@ -4626,7 +4711,7 @@ intel_dp_unset_edid(struct intel_dp *intel_dp) struct intel_connector *connector = intel_dp->attached_connector; drm_dp_cec_unset_edid(&intel_dp->aux); - kfree(connector->detect_edid); + drm_edid_free(connector->detect_edid); connector->detect_edid = NULL; intel_dp->has_hdmi_sink = false; @@ -4790,12 +4875,10 @@ intel_dp_force(struct drm_connector *connector) static int intel_dp_get_modes(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); - struct edid *edid; - int num_modes = 0; + int num_modes; - edid = intel_connector->detect_edid; - if (edid) - num_modes = intel_connector_update_modes(connector, edid); + /* drm_edid_connector_update() done in ->detect() or ->force() */ + num_modes = drm_edid_connector_add_modes(connector); /* Also add fixed mode, which may or may not be present in EDID */ if (intel_dp_is_edp(intel_attached_dp(intel_connector))) @@ -4804,7 +4887,7 @@ static int intel_dp_get_modes(struct drm_connector *connector) if (num_modes) return num_modes; - if (!edid) { + if (!intel_connector->detect_edid) { struct intel_dp *intel_dp = intel_attached_dp(intel_connector); struct drm_display_mode *mode; @@ -4841,7 +4924,7 @@ intel_dp_connector_register(struct drm_connector *connector) if (!ret) drm_dp_cec_register_connector(&intel_dp->aux, connector); - if (!intel_bios_is_lspcon_present(i915, dig_port->base.port)) + if (!intel_bios_encoder_is_lspcon(dig_port->base.devdata)) return ret; /* @@ -5119,8 +5202,9 @@ intel_dp_hpd_pulse(struct intel_digital_port *dig_port, bool long_hpd) return IRQ_HANDLED; } -/* check the VBT to see whether the eDP is on another port */ -bool intel_dp_is_port_edp(struct drm_i915_private *dev_priv, enum port port) +static bool _intel_dp_is_port_edp(struct drm_i915_private *dev_priv, + const struct intel_bios_encoder_data *devdata, + enum port port) { /* * eDP not supported on g4x. so bail out early just @@ -5132,13 +5216,24 @@ bool intel_dp_is_port_edp(struct drm_i915_private *dev_priv, enum port port) if (DISPLAY_VER(dev_priv) < 9 && port == PORT_A) return true; - return intel_bios_is_port_edp(dev_priv, port); + return devdata && intel_bios_encoder_supports_edp(devdata); +} + +bool intel_dp_is_port_edp(struct drm_i915_private *i915, enum port port) +{ + const struct intel_bios_encoder_data *devdata = + intel_bios_encoder_data_lookup(i915, port); + + return _intel_dp_is_port_edp(i915, devdata, port); } static bool -has_gamut_metadata_dip(struct drm_i915_private *i915, enum port port) +has_gamut_metadata_dip(struct intel_encoder *encoder) { - if (intel_bios_is_lspcon_present(i915, port)) + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum port port = encoder->port; + + if (intel_bios_encoder_is_lspcon(encoder->devdata)) return false; if (DISPLAY_VER(i915) >= 11) @@ -5173,14 +5268,14 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect drm_connector_attach_max_bpc_property(connector, 6, 12); /* Register HDMI colorspace for case of lspcon */ - if (intel_bios_is_lspcon_present(dev_priv, port)) { + if (intel_bios_encoder_is_lspcon(dp_to_dig_port(intel_dp)->base.devdata)) { drm_connector_attach_content_type_property(connector); intel_attach_hdmi_colorspace_property(connector); } else { intel_attach_dp_colorspace_property(connector); } - if (has_gamut_metadata_dip(dev_priv, port)) + if (has_gamut_metadata_dip(&dp_to_dig_port(intel_dp)->base)) drm_connector_attach_hdr_output_metadata_property(connector); if (HAS_VRR(dev_priv)) @@ -5222,11 +5317,6 @@ static void intel_edp_backlight_setup(struct intel_dp *intel_dp, if (pipe != PIPE_A && pipe != PIPE_B) pipe = PIPE_A; - - drm_dbg_kms(&i915->drm, - "[CONNECTOR:%d:%s] using pipe %c for initial backlight setup\n", - connector->base.base.id, connector->base.name, - pipe_name(pipe)); } intel_backlight_setup(connector, pipe); @@ -5240,7 +5330,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, struct drm_display_mode *fixed_mode; struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; bool has_dpcd; - struct edid *edid; + const struct drm_edid *drm_edid; if (!intel_dp_is_edp(intel_dp)) return true; @@ -5287,29 +5377,28 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } mutex_lock(&dev_priv->drm.mode_config.mutex); - edid = drm_get_edid(connector, &intel_dp->aux.ddc); - if (!edid) { + drm_edid = drm_edid_read_ddc(connector, &intel_dp->aux.ddc); + if (!drm_edid) { /* Fallback to EDID from ACPI OpRegion, if any */ - edid = intel_opregion_get_edid(intel_connector); - if (edid) + drm_edid = intel_opregion_get_edid(intel_connector); + if (drm_edid) drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s] Using OpRegion EDID\n", connector->base.id, connector->name); } - if (edid) { - if (drm_add_edid_modes(connector, edid)) { - drm_connector_update_edid_property(connector, edid); - } else { - kfree(edid); - edid = ERR_PTR(-EINVAL); + if (drm_edid) { + if (drm_edid_connector_update(connector, drm_edid) || + !drm_edid_connector_add_modes(connector)) { + drm_edid_connector_update(connector, NULL); + drm_edid_free(drm_edid); + drm_edid = ERR_PTR(-EINVAL); } } else { - edid = ERR_PTR(-ENOENT); + drm_edid = ERR_PTR(-ENOENT); } - intel_connector->edid = edid; - intel_bios_init_panel_late(dev_priv, &intel_connector->panel, - encoder->devdata, IS_ERR(edid) ? NULL : edid); + intel_bios_init_panel_late(dev_priv, &intel_connector->panel, encoder->devdata, + IS_ERR(drm_edid) ? NULL : drm_edid); intel_panel_add_edid_fixed_modes(intel_connector, true); @@ -5333,7 +5422,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, goto out_vdd_off; } - intel_panel_init(intel_connector); + intel_panel_init(intel_connector, drm_edid); intel_edp_backlight_setup(intel_dp, intel_connector); @@ -5403,7 +5492,7 @@ intel_dp_init_connector(struct intel_digital_port *dig_port, intel_dp->DP = intel_de_read(dev_priv, intel_dp->output_reg); intel_dp->attached_connector = intel_connector; - if (intel_dp_is_port_edp(dev_priv, port)) { + if (_intel_dp_is_port_edp(dev_priv, intel_encoder->devdata, port)) { /* * Currently we don't support eDP on TypeC ports, although in * theory it could work on TypeC legacy ports. |