diff options
34 files changed, 4560 insertions, 4005 deletions
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index d6ac946d0407..e245a036613e 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -200,14 +200,17 @@ i915-y += \ display/intel_color.o \ display/intel_combo_phy.o \ display/intel_connector.o \ + display/intel_crtc.o \ display/intel_csr.o \ display/intel_cursor.o \ display/intel_display.o \ display/intel_display_power.o \ display/intel_dpio_phy.o \ + display/intel_dpll.o \ display/intel_dpll_mgr.o \ display/intel_dsb.o \ display/intel_fbc.o \ + display/intel_fdi.o \ display/intel_fifo_underrun.o \ display/intel_frontbuffer.o \ display/intel_global_state.o \ @@ -252,6 +255,7 @@ i915-y += \ display/intel_lspcon.o \ display/intel_lvds.o \ display/intel_panel.o \ + display/intel_pps.o \ display/intel_sdvo.o \ display/intel_tv.o \ display/intel_vdsc.o \ diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c new file mode 100644 index 000000000000..57b0a3ebe908 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h> + +#include "intel_atomic.h" +#include "intel_atomic_plane.h" +#include "intel_color.h" +#include "intel_crtc.h" +#include "intel_cursor.h" +#include "intel_display_debugfs.h" +#include "intel_display_types.h" +#include "intel_pipe_crc.h" +#include "intel_sprite.h" +#include "i9xx_plane.h" + +static void assert_vblank_disabled(struct drm_crtc *crtc) +{ + if (I915_STATE_WARN_ON(drm_crtc_vblank_get(crtc) == 0)) + drm_crtc_vblank_put(crtc); +} + +u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(&crtc->base)]; + + if (!vblank->max_vblank_count) + return (u32)drm_crtc_accurate_vblank_count(&crtc->base); + + return crtc->base.funcs->get_vblank_counter(&crtc->base); +} + +u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + u32 mode_flags = crtc->mode_flags; + + /* + * From Gen 11, In case of dsi cmd mode, frame counter wouldnt + * have updated at the beginning of TE, if we want to use + * the hw counter, then we would find it updated in only + * the next TE, hence switching to sw counter. + */ + if (mode_flags & (I915_MODE_FLAG_DSI_USE_TE0 | I915_MODE_FLAG_DSI_USE_TE1)) + return 0; + + /* + * On i965gm the hardware frame counter reads + * zero when the TV encoder is enabled :( + */ + if (IS_I965GM(dev_priv) && + (crtc_state->output_types & BIT(INTEL_OUTPUT_TVOUT))) + return 0; + + if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) + return 0xffffffff; /* full 32 bit counter */ + else if (INTEL_GEN(dev_priv) >= 3) + return 0xffffff; /* only 24 bits of frame count */ + else + return 0; /* Gen2 doesn't have a hardware frame counter */ +} + +void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + + assert_vblank_disabled(&crtc->base); + drm_crtc_set_max_vblank_count(&crtc->base, + intel_crtc_max_vblank_count(crtc_state)); + drm_crtc_vblank_on(&crtc->base); +} + +void intel_crtc_vblank_off(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + + drm_crtc_vblank_off(&crtc->base); + assert_vblank_disabled(&crtc->base); +} + +struct intel_crtc_state *intel_crtc_state_alloc(struct intel_crtc *crtc) +{ + struct intel_crtc_state *crtc_state; + + crtc_state = kmalloc(sizeof(*crtc_state), GFP_KERNEL); + + if (crtc_state) + intel_crtc_state_reset(crtc_state, crtc); + + return crtc_state; +} + +void intel_crtc_state_reset(struct intel_crtc_state *crtc_state, + struct intel_crtc *crtc) +{ + memset(crtc_state, 0, sizeof(*crtc_state)); + + __drm_atomic_helper_crtc_state_reset(&crtc_state->uapi, &crtc->base); + + crtc_state->cpu_transcoder = INVALID_TRANSCODER; + crtc_state->master_transcoder = INVALID_TRANSCODER; + crtc_state->hsw_workaround_pipe = INVALID_PIPE; + crtc_state->output_format = INTEL_OUTPUT_FORMAT_INVALID; + crtc_state->scaler_state.scaler_id = -1; + crtc_state->mst_master_transcoder = INVALID_TRANSCODER; +} + +static struct intel_crtc *intel_crtc_alloc(void) +{ + struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + + crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); + if (!crtc) + return ERR_PTR(-ENOMEM); + + crtc_state = intel_crtc_state_alloc(crtc); + if (!crtc_state) { + kfree(crtc); + return ERR_PTR(-ENOMEM); + } + + crtc->base.state = &crtc_state->uapi; + crtc->config = crtc_state; + + return crtc; +} + +static void intel_crtc_free(struct intel_crtc *crtc) +{ + intel_crtc_destroy_state(&crtc->base, crtc->base.state); + kfree(crtc); +} + +static void intel_crtc_destroy(struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + drm_crtc_cleanup(crtc); + kfree(intel_crtc); +} + +static int intel_crtc_late_register(struct drm_crtc *crtc) +{ + intel_crtc_debugfs_add(crtc); + return 0; +} + +#define INTEL_CRTC_FUNCS \ + .set_config = drm_atomic_helper_set_config, \ + .destroy = intel_crtc_destroy, \ + .page_flip = drm_atomic_helper_page_flip, \ + .atomic_duplicate_state = intel_crtc_duplicate_state, \ + .atomic_destroy_state = intel_crtc_destroy_state, \ + .set_crc_source = intel_crtc_set_crc_source, \ + .verify_crc_source = intel_crtc_verify_crc_source, \ + .get_crc_sources = intel_crtc_get_crc_sources, \ + .late_register = intel_crtc_late_register + +static const struct drm_crtc_funcs bdw_crtc_funcs = { + INTEL_CRTC_FUNCS, + + .get_vblank_counter = g4x_get_vblank_counter, + .enable_vblank = bdw_enable_vblank, + .disable_vblank = bdw_disable_vblank, + .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, +}; + +static const struct drm_crtc_funcs ilk_crtc_funcs = { + INTEL_CRTC_FUNCS, + + .get_vblank_counter = g4x_get_vblank_counter, + .enable_vblank = ilk_enable_vblank, + .disable_vblank = ilk_disable_vblank, + .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, +}; + +static const struct drm_crtc_funcs g4x_crtc_funcs = { + INTEL_CRTC_FUNCS, + + .get_vblank_counter = g4x_get_vblank_counter, + .enable_vblank = i965_enable_vblank, + .disable_vblank = i965_disable_vblank, + .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, +}; + +static const struct drm_crtc_funcs i965_crtc_funcs = { + INTEL_CRTC_FUNCS, + + .get_vblank_counter = i915_get_vblank_counter, + .enable_vblank = i965_enable_vblank, + .disable_vblank = i965_disable_vblank, + .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, +}; + +static const struct drm_crtc_funcs i915gm_crtc_funcs = { + INTEL_CRTC_FUNCS, + + .get_vblank_counter = i915_get_vblank_counter, + .enable_vblank = i915gm_enable_vblank, + .disable_vblank = i915gm_disable_vblank, + .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, +}; + +static const struct drm_crtc_funcs i915_crtc_funcs = { + INTEL_CRTC_FUNCS, + + .get_vblank_counter = i915_get_vblank_counter, + .enable_vblank = i8xx_enable_vblank, + .disable_vblank = i8xx_disable_vblank, + .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, +}; + +static const struct drm_crtc_funcs i8xx_crtc_funcs = { + INTEL_CRTC_FUNCS, + + /* no hw vblank counter */ + .enable_vblank = i8xx_enable_vblank, + .disable_vblank = i8xx_disable_vblank, + .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, +}; + +int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) +{ + struct intel_plane *primary, *cursor; + const struct drm_crtc_funcs *funcs; + struct intel_crtc *crtc; + int sprite, ret; + + crtc = intel_crtc_alloc(); + if (IS_ERR(crtc)) + return PTR_ERR(crtc); + + crtc->pipe = pipe; + crtc->num_scalers = RUNTIME_INFO(dev_priv)->num_scalers[pipe]; + + primary = intel_primary_plane_create(dev_priv, pipe); + if (IS_ERR(primary)) { + ret = PTR_ERR(primary); + goto fail; + } + crtc->plane_ids_mask |= BIT(primary->id); + + for_each_sprite(dev_priv, pipe, sprite) { + struct intel_plane *plane; + + plane = intel_sprite_plane_create(dev_priv, pipe, sprite); + if (IS_ERR(plane)) { + ret = PTR_ERR(plane); + goto fail; + } + crtc->plane_ids_mask |= BIT(plane->id); + } + + cursor = intel_cursor_plane_create(dev_priv, pipe); + if (IS_ERR(cursor)) { + ret = PTR_ERR(cursor); + goto fail; + } + crtc->plane_ids_mask |= BIT(cursor->id); + + if (HAS_GMCH(dev_priv)) { + if (IS_CHERRYVIEW(dev_priv) || + IS_VALLEYVIEW(dev_priv) || IS_G4X(dev_priv)) + funcs = &g4x_crtc_funcs; + else if (IS_GEN(dev_priv, 4)) + funcs = &i965_crtc_funcs; + else if (IS_I945GM(dev_priv) || IS_I915GM(dev_priv)) + funcs = &i915gm_crtc_funcs; + else if (IS_GEN(dev_priv, 3)) + funcs = &i915_crtc_funcs; + else + funcs = &i8xx_crtc_funcs; + } else { + if (INTEL_GEN(dev_priv) >= 8) + funcs = &bdw_crtc_funcs; + else + funcs = &ilk_crtc_funcs; + } + + ret = drm_crtc_init_with_planes(&dev_priv->drm, &crtc->base, + &primary->base, &cursor->base, + funcs, "pipe %c", pipe_name(pipe)); + if (ret) + goto fail; + + BUG_ON(pipe >= ARRAY_SIZE(dev_priv->pipe_to_crtc_mapping) || + dev_priv->pipe_to_crtc_mapping[pipe] != NULL); + dev_priv->pipe_to_crtc_mapping[pipe] = crtc; + + if (INTEL_GEN(dev_priv) < 9) { + enum i9xx_plane_id i9xx_plane = primary->i9xx_plane; + + BUG_ON(i9xx_plane >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || + dev_priv->plane_to_crtc_mapping[i9xx_plane] != NULL); + dev_priv->plane_to_crtc_mapping[i9xx_plane] = crtc; + } + + if (INTEL_GEN(dev_priv) >= 10) + drm_crtc_create_scaling_filter_property(&crtc->base, + BIT(DRM_SCALING_FILTER_DEFAULT) | + BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR)); + + intel_color_init(crtc); + + intel_crtc_crc_init(crtc); + + drm_WARN_ON(&dev_priv->drm, drm_crtc_index(&crtc->base) != crtc->pipe); + + return 0; + +fail: + intel_crtc_free(crtc); + + return ret; +} diff --git a/drivers/gpu/drm/i915/display/intel_crtc.h b/drivers/gpu/drm/i915/display/intel_crtc.h new file mode 100644 index 000000000000..08112d557411 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_crtc.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef _INTEL_CRTC_H_ +#define _INTEL_CRTC_H_ + +#include <linux/types.h> + +enum pipe; +struct drm_i915_private; +struct intel_crtc; +struct intel_crtc_state; + +u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state); +int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe); +struct intel_crtc_state *intel_crtc_state_alloc(struct intel_crtc *crtc); +void intel_crtc_state_reset(struct intel_crtc_state *crtc_state, + struct intel_crtc *crtc); + +#endif diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index ef9848001418..d714e8b34d52 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -46,6 +46,7 @@ #include "intel_hotplug.h" #include "intel_lspcon.h" #include "intel_panel.h" +#include "intel_pps.h" #include "intel_psr.h" #include "intel_sprite.h" #include "intel_tc.h" @@ -2099,9 +2100,9 @@ void intel_ddi_disable_transcoder_func(const struct intel_crtc_state *crtc_state } } -int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder, - enum transcoder cpu_transcoder, - bool enable) +int intel_ddi_toggle_hdcp_bits(struct intel_encoder *intel_encoder, + enum transcoder cpu_transcoder, + bool enable, u32 hdcp_mask) { struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -2116,9 +2117,9 @@ int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder, tmp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder)); if (enable) - tmp |= TRANS_DDI_HDCP_SIGNALLING; + tmp |= hdcp_mask; else - tmp &= ~TRANS_DDI_HDCP_SIGNALLING; + tmp &= ~hdcp_mask; intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder), tmp); intel_display_power_put(dev_priv, intel_encoder->power_domain, wakeref); return ret; @@ -3625,7 +3626,7 @@ static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state, */ /* 2. Enable Panel Power if PPS is required */ - intel_edp_panel_on(intel_dp); + intel_pps_on(intel_dp); /* * 3. For non-TBT Type-C ports, set FIA lane count @@ -3768,7 +3769,7 @@ static void hsw_ddi_pre_enable_dp(struct intel_atomic_state *state, crtc_state->port_clock, crtc_state->lane_count); - intel_edp_panel_on(intel_dp); + intel_pps_on(intel_dp); intel_ddi_clk_select(encoder, crtc_state); @@ -4010,8 +4011,8 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, if (INTEL_GEN(dev_priv) >= 12) intel_ddi_disable_pipe_clock(old_crtc_state); - intel_edp_panel_vdd_on(intel_dp); - intel_edp_panel_off(intel_dp); + intel_pps_vdd_on(intel_dp); + intel_pps_off(intel_dp); if (!intel_phy_is_tc(dev_priv, phy) || dig_port->tc_mode != TC_PORT_TBT_ALT) @@ -4326,7 +4327,7 @@ static void intel_enable_ddi(struct intel_atomic_state *state, if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) intel_hdcp_enable(to_intel_connector(conn_state->connector), - crtc_state->cpu_transcoder, + crtc_state, (u8)conn_state->hdcp_content_type); } @@ -5039,6 +5040,8 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder) intel_dp_encoder_flush_work(encoder); drm_encoder_cleanup(encoder); + if (dig_port) + kfree(dig_port->hdcp_port_data.streams); kfree(dig_port); } diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h index dcc711cfe4fe..a4dd815c0000 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.h +++ b/drivers/gpu/drm/i915/display/intel_ddi.h @@ -50,9 +50,9 @@ u32 bxt_signal_levels(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); u32 ddi_signal_levels(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); -int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder, - enum transcoder cpu_transcoder, - bool enable); +int intel_ddi_toggle_hdcp_bits(struct intel_encoder *intel_encoder, + enum transcoder cpu_transcoder, + bool enable, u32 hdcp_mask); void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder); #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 0189d379a55e..b728792e0c27 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -48,6 +48,7 @@ #include "display/intel_display_debugfs.h" #include "display/intel_dp.h" #include "display/intel_dp_mst.h" +#include "display/intel_dpll.h" #include "display/intel_dpll_mgr.h" #include "display/intel_dsi.h" #include "display/intel_dvo.h" @@ -68,11 +69,12 @@ #include "intel_bw.h" #include "intel_cdclk.h" #include "intel_color.h" +#include "intel_crtc.h" #include "intel_csr.h" -#include "intel_cursor.h" #include "intel_display_types.h" #include "intel_dp_link_training.h" #include "intel_fbc.h" +#include "intel_fdi.h" #include "intel_fbdev.h" #include "intel_fifo_underrun.h" #include "intel_frontbuffer.h" @@ -114,18 +116,6 @@ static void skl_pfit_enable(const struct intel_crtc_state *crtc_state); static void ilk_pfit_enable(const struct intel_crtc_state *crtc_state); static void intel_modeset_setup_hw_state(struct drm_device *dev, struct drm_modeset_acquire_ctx *ctx); -static struct intel_crtc_state *intel_crtc_state_alloc(struct intel_crtc *crtc); - -struct intel_limit { - struct { - int min, max; - } dot, vco, n, m, m1, m2, p, p1; - - struct { - int dot_limit; - int p2_slow, p2_fast; - } p2; -}; /* returns HPLL frequency in kHz */ int vlv_get_hpll_vco(struct drm_i915_private *dev_priv) @@ -184,281 +174,6 @@ static void intel_update_czclk(struct drm_i915_private *dev_priv) dev_priv->czclk_freq); } -/* units of 100MHz */ -static u32 intel_fdi_link_freq(struct drm_i915_private *dev_priv, - const struct intel_crtc_state *pipe_config) -{ - if (HAS_DDI(dev_priv)) - return pipe_config->port_clock; /* SPLL */ - else - return dev_priv->fdi_pll_freq; -} - -static const struct intel_limit intel_limits_i8xx_dac = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 908000, .max = 1512000 }, - .n = { .min = 2, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 2, .max = 33 }, - .p2 = { .dot_limit = 165000, - .p2_slow = 4, .p2_fast = 2 }, -}; - -static const struct intel_limit intel_limits_i8xx_dvo = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 908000, .max = 1512000 }, - .n = { .min = 2, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 2, .max = 33 }, - .p2 = { .dot_limit = 165000, - .p2_slow = 4, .p2_fast = 4 }, -}; - -static const struct intel_limit intel_limits_i8xx_lvds = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 908000, .max = 1512000 }, - .n = { .min = 2, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 1, .max = 6 }, - .p2 = { .dot_limit = 165000, - .p2_slow = 14, .p2_fast = 7 }, -}; - -static const struct intel_limit intel_limits_i9xx_sdvo = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1400000, .max = 2800000 }, - .n = { .min = 1, .max = 6 }, - .m = { .min = 70, .max = 120 }, - .m1 = { .min = 8, .max = 18 }, - .m2 = { .min = 3, .max = 7 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 200000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const struct intel_limit intel_limits_i9xx_lvds = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1400000, .max = 2800000 }, - .n = { .min = 1, .max = 6 }, - .m = { .min = 70, .max = 120 }, - .m1 = { .min = 8, .max = 18 }, - .m2 = { .min = 3, .max = 7 }, - .p = { .min = 7, .max = 98 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 112000, - .p2_slow = 14, .p2_fast = 7 }, -}; - - -static const struct intel_limit intel_limits_g4x_sdvo = { - .dot = { .min = 25000, .max = 270000 }, - .vco = { .min = 1750000, .max = 3500000}, - .n = { .min = 1, .max = 4 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 17, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 10, .max = 30 }, - .p1 = { .min = 1, .max = 3}, - .p2 = { .dot_limit = 270000, - .p2_slow = 10, - .p2_fast = 10 - }, -}; - -static const struct intel_limit intel_limits_g4x_hdmi = { - .dot = { .min = 22000, .max = 400000 }, - .vco = { .min = 1750000, .max = 3500000}, - .n = { .min = 1, .max = 4 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 16, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8}, - .p2 = { .dot_limit = 165000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const struct intel_limit intel_limits_g4x_single_channel_lvds = { - .dot = { .min = 20000, .max = 115000 }, - .vco = { .min = 1750000, .max = 3500000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 17, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 28, .max = 112 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 0, - .p2_slow = 14, .p2_fast = 14 - }, -}; - -static const struct intel_limit intel_limits_g4x_dual_channel_lvds = { - .dot = { .min = 80000, .max = 224000 }, - .vco = { .min = 1750000, .max = 3500000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 17, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 14, .max = 42 }, - .p1 = { .min = 2, .max = 6 }, - .p2 = { .dot_limit = 0, - .p2_slow = 7, .p2_fast = 7 - }, -}; - -static const struct intel_limit pnv_limits_sdvo = { - .dot = { .min = 20000, .max = 400000}, - .vco = { .min = 1700000, .max = 3500000 }, - /* Pineview's Ncounter is a ring counter */ - .n = { .min = 3, .max = 6 }, - .m = { .min = 2, .max = 256 }, - /* Pineview only has one combined m divider, which we treat as m2. */ - .m1 = { .min = 0, .max = 0 }, - .m2 = { .min = 0, .max = 254 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 200000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const struct intel_limit pnv_limits_lvds = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1700000, .max = 3500000 }, - .n = { .min = 3, .max = 6 }, - .m = { .min = 2, .max = 256 }, - .m1 = { .min = 0, .max = 0 }, - .m2 = { .min = 0, .max = 254 }, - .p = { .min = 7, .max = 112 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 112000, - .p2_slow = 14, .p2_fast = 14 }, -}; - -/* Ironlake / Sandybridge - * - * We calculate clock using (register_value + 2) for N/M1/M2, so here - * the range value for them is (actual_value - 2). - */ -static const struct intel_limit ilk_limits_dac = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 5 }, - .m = { .min = 79, .max = 127 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const struct intel_limit ilk_limits_single_lvds = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 79, .max = 118 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 28, .max = 112 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 14, .p2_fast = 14 }, -}; - -static const struct intel_limit ilk_limits_dual_lvds = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 79, .max = 127 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 14, .max = 56 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 7, .p2_fast = 7 }, -}; - -/* LVDS 100mhz refclk limits. */ -static const struct intel_limit ilk_limits_single_lvds_100m = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 2 }, - .m = { .min = 79, .max = 126 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 28, .max = 112 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 14, .p2_fast = 14 }, -}; - -static const struct intel_limit ilk_limits_dual_lvds_100m = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 79, .max = 126 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 14, .max = 42 }, - .p1 = { .min = 2, .max = 6 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 7, .p2_fast = 7 }, -}; - -static const struct intel_limit intel_limits_vlv = { - /* - * These are the data rate limits (measured in fast clocks) - * since those are the strictest limits we have. The fast - * clock and actual rate limits are more relaxed, so checking - * them would make no difference. - */ - .dot = { .min = 25000 * 5, .max = 270000 * 5 }, - .vco = { .min = 4000000, .max = 6000000 }, - .n = { .min = 1, .max = 7 }, - .m1 = { .min = 2, .max = 3 }, - .m2 = { .min = 11, .max = 156 }, - .p1 = { .min = 2, .max = 3 }, - .p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */ -}; - -static const struct intel_limit intel_limits_chv = { - /* - * These are the data rate limits (measured in fast clocks) - * since those are the strictest limits we have. The fast - * clock and actual rate limits are more relaxed, so checking - * them would make no difference. - */ - .dot = { .min = 25000 * 5, .max = 540000 * 5}, - .vco = { .min = 4800000, .max = 6480000 }, - .n = { .min = 1, .max = 1 }, - .m1 = { .min = 2, .max = 2 }, - .m2 = { .min = 24 << 22, .max = 175 << 22 }, - .p1 = { .min = 2, .max = 4 }, - .p2 = { .p2_slow = 1, .p2_fast = 14 }, -}; - -static const struct intel_limit intel_limits_bxt = { - /* FIXME: find real dot limits */ - .dot = { .min = 0, .max = INT_MAX }, - .vco = { .min = 4800000, .max = 6700000 }, - .n = { .min = 1, .max = 1 }, - .m1 = { .min = 2, .max = 2 }, - /* FIXME: find real m2 limits */ - .m2 = { .min = 2 << 22, .max = 255 << 22 }, - .p1 = { .min = 2, .max = 4 }, - .p2 = { .p2_slow = 1, .p2_fast = 20 }, -}; - /* WA Display #0827: Gen9:all */ static void skl_wa_827(struct drm_i915_private *dev_priv, enum pipe pipe, bool enable) @@ -503,483 +218,6 @@ is_trans_port_sync_mode(const struct intel_crtc_state *crtc_state) is_trans_port_sync_slave(crtc_state); } -/* - * Platform specific helpers to calculate the port PLL loopback- (clock.m), - * and post-divider (clock.p) values, pre- (clock.vco) and post-divided fast - * (clock.dot) clock rates. This fast dot clock is fed to the port's IO logic. - * The helpers' return value is the rate of the clock that is fed to the - * display engine's pipe which can be the above fast dot clock rate or a - * divided-down version of it. - */ -/* m1 is reserved as 0 in Pineview, n is a ring counter */ -static int pnv_calc_dpll_params(int refclk, struct dpll *clock) -{ - clock->m = clock->m2 + 2; - clock->p = clock->p1 * clock->p2; - if (WARN_ON(clock->n == 0 || clock->p == 0)) - return 0; - clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); - clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); - - return clock->dot; -} - -static u32 i9xx_dpll_compute_m(struct dpll *dpll) -{ - return 5 * (dpll->m1 + 2) + (dpll->m2 + 2); -} - -static int i9xx_calc_dpll_params(int refclk, struct dpll *clock) -{ - clock->m = i9xx_dpll_compute_m(clock); - clock->p = clock->p1 * clock->p2; - if (WARN_ON(clock->n + 2 == 0 || clock->p == 0)) - return 0; - clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2); - clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); - - return clock->dot; -} - -static int vlv_calc_dpll_params(int refclk, struct dpll *clock) -{ - clock->m = clock->m1 * clock->m2; - clock->p = clock->p1 * clock->p2; - if (WARN_ON(clock->n == 0 || clock->p == 0)) - return 0; - clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); - clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); - - return clock->dot / 5; -} - -int chv_calc_dpll_params(int refclk, struct dpll *clock) -{ - clock->m = clock->m1 * clock->m2; - clock->p = clock->p1 * clock->p2; - if (WARN_ON(clock->n == 0 || clock->p == 0)) - return 0; - clock->vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, clock->m), - clock->n << 22); - clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); - - return clock->dot / 5; -} - -/* - * Returns whether the given set of divisors are valid for a given refclk with - * the given connectors. - */ -static bool intel_pll_is_valid(struct drm_i915_private *dev_priv, - const struct intel_limit *limit, - const struct dpll *clock) -{ - if (clock->n < limit->n.min || limit->n.max < clock->n) - return false; - if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) - return false; - if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) - return false; - if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) - return false; - - if (!IS_PINEVIEW(dev_priv) && !IS_VALLEYVIEW(dev_priv) && - !IS_CHERRYVIEW(dev_priv) && !IS_GEN9_LP(dev_priv)) - if (clock->m1 <= clock->m2) - return false; - - if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv) && - !IS_GEN9_LP(dev_priv)) { - if (clock->p < limit->p.min || limit->p.max < clock->p) - return false; - if (clock->m < limit->m.min || limit->m.max < clock->m) - return false; - } - - if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) - return false; - /* XXX: We may need to be checking "Dot clock" depending on the multiplier, - * connector, etc., rather than just a single range. - */ - if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) - return false; - - return true; -} - -static int -i9xx_select_p2_div(const struct intel_limit *limit, - const struct intel_crtc_state *crtc_state, - int target) -{ - struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - /* - * For LVDS just rely on its current settings for dual-channel. - * We haven't figured out how to reliably set up different - * single/dual channel state, if we even can. - */ - if (intel_is_dual_link_lvds(dev_priv)) - return limit->p2.p2_fast; - else - return limit->p2.p2_slow; - } else { - if (target < limit->p2.dot_limit) - return limit->p2.p2_slow; - else - return limit->p2.p2_fast; - } -} - -/* - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - * - * Target and reference clocks are specified in kHz. - * - * If match_clock is provided, then best_clock P divider must match the P - * divider from @match_clock used for LVDS downclocking. - */ -static bool -i9xx_find_best_dpll(const struct intel_limit *limit, - struct intel_crtc_state *crtc_state, - int target, int refclk, struct dpll *match_clock, - struct dpll *best_clock) -{ - struct drm_device *dev = crtc_state->uapi.crtc->dev; - struct dpll clock; - int err = target; - - memset(best_clock, 0, sizeof(*best_clock)); - - clock.p2 = i9xx_select_p2_div(limit, crtc_state, target); - - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; - clock.m1++) { - for (clock.m2 = limit->m2.min; - clock.m2 <= limit->m2.max; clock.m2++) { - if (clock.m2 >= clock.m1) - break; - for (clock.n = limit->n.min; - clock.n <= limit->n.max; clock.n++) { - for (clock.p1 = limit->p1.min; - clock.p1 <= limit->p1.max; clock.p1++) { - int this_err; - - i9xx_calc_dpll_params(refclk, &clock); - if (!intel_pll_is_valid(to_i915(dev), - limit, - &clock)) - continue; - if (match_clock && - clock.p != match_clock->p) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - } - } - - return (err != target); -} - -/* - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - * - * Target and reference clocks are specified in kHz. - * - * If match_clock is provided, then best_clock P divider must match the P - * divider from @match_clock used for LVDS downclocking. - */ -static bool -pnv_find_best_dpll(const struct intel_limit *limit, - struct intel_crtc_state *crtc_state, - int target, int refclk, struct dpll *match_clock, - struct dpll *best_clock) -{ - struct drm_device *dev = crtc_state->uapi.crtc->dev; - struct dpll clock; - int err = target; - - memset(best_clock, 0, sizeof(*best_clock)); - - clock.p2 = i9xx_select_p2_div(limit, crtc_state, target); - - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; - clock.m1++) { - for (clock.m2 = limit->m2.min; - clock.m2 <= limit->m2.max; clock.m2++) { - for (clock.n = limit->n.min; - clock.n <= limit->n.max; clock.n++) { - for (clock.p1 = limit->p1.min; - clock.p1 <= limit->p1.max; clock.p1++) { - int this_err; - - pnv_calc_dpll_params(refclk, &clock); - if (!intel_pll_is_valid(to_i915(dev), - limit, - &clock)) - continue; - if (match_clock && - clock.p != match_clock->p) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - } - } - - return (err != target); -} - -/* - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - * - * Target and reference clocks are specified in kHz. - * - * If match_clock is provided, then best_clock P divider must match the P - * divider from @match_clock used for LVDS downclocking. - */ -static bool -g4x_find_best_dpll(const struct intel_limit *limit, - struct intel_crtc_state *crtc_state, - int target, int refclk, struct dpll *match_clock, - struct dpll *best_clock) -{ - struct drm_device *dev = crtc_state->uapi.crtc->dev; - struct dpll clock; - int max_n; - bool found = false; - /* approximately equals target * 0.00585 */ - int err_most = (target >> 8) + (target >> 9); - - memset(best_clock, 0, sizeof(*best_clock)); - - clock.p2 = i9xx_select_p2_div(limit, crtc_state, target); - - max_n = limit->n.max; - /* based on hardware requirement, prefer smaller n to precision */ - for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { - /* based on hardware requirement, prefere larger m1,m2 */ - for (clock.m1 = limit->m1.max; - clock.m1 >= limit->m1.min; clock.m1--) { - for (clock.m2 = limit->m2.max; - clock.m2 >= limit->m2.min; clock.m2--) { - for (clock.p1 = limit->p1.max; - clock.p1 >= limit->p1.min; clock.p1--) { - int this_err; - - i9xx_calc_dpll_params(refclk, &clock); - if (!intel_pll_is_valid(to_i915(dev), - limit, - &clock)) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err_most) { - *best_clock = clock; - err_most = this_err; - max_n = clock.n; - found = true; - } - } - } - } - } - return found; -} - -/* - * Check if the calculated PLL configuration is more optimal compared to the - * best configuration and error found so far. Return the calculated error. - */ -static bool vlv_PLL_is_optimal(struct drm_device *dev, int target_freq, - const struct dpll *calculated_clock, - const struct dpll *best_clock, - unsigned int best_error_ppm, - unsigned int *error_ppm) -{ - /* - * For CHV ignore the error and consider only the P value. - * Prefer a bigger P value based on HW requirements. - */ - if (IS_CHERRYVIEW(to_i915(dev))) { - *error_ppm = 0; - - return calculated_clock->p > best_clock->p; - } - - if (drm_WARN_ON_ONCE(dev, !target_freq)) - return false; - - *error_ppm = div_u64(1000000ULL * - abs(target_freq - calculated_clock->dot), - target_freq); - /* - * Prefer a better P value over a better (smaller) error if the error - * is small. Ensure this preference for future configurations too by - * setting the error to 0. - */ - if (*error_ppm < 100 && calculated_clock->p > best_clock->p) { - *error_ppm = 0; - - return true; - } - - return *error_ppm + 10 < best_error_ppm; -} - -/* - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - */ -static bool -vlv_find_best_dpll(const struct intel_limit *limit, - struct intel_crtc_state *crtc_state, - int target, int refclk, struct dpll *match_clock, - struct dpll *best_clock) -{ - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_device *dev = crtc->base.dev; - struct dpll clock; - unsigned int bestppm = 1000000; - /* min update 19.2 MHz */ - int max_n = min(limit->n.max, refclk / 19200); - bool found = false; - - target *= 5; /* fast clock */ - - memset(best_clock, 0, sizeof(*best_clock)); - - /* based on hardware requirement, prefer smaller n to precision */ - for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { - for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { - for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow; - clock.p2 -= clock.p2 > 10 ? 2 : 1) { - clock.p = clock.p1 * clock.p2; - /* based on hardware requirement, prefer bigger m1,m2 values */ - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { - unsigned int ppm; - - clock.m2 = DIV_ROUND_CLOSEST(target * clock.p * clock.n, - refclk * clock.m1); - - vlv_calc_dpll_params(refclk, &clock); - - if (!intel_pll_is_valid(to_i915(dev), - limit, - &clock)) - continue; - - if (!vlv_PLL_is_optimal(dev, target, - &clock, - best_clock, - bestppm, &ppm)) - continue; - - *best_clock = clock; - bestppm = ppm; - found = true; - } - } - } - } - - return found; -} - -/* - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - */ -static bool -chv_find_best_dpll(const struct intel_limit *limit, - struct intel_crtc_state *crtc_state, - int target, int refclk, struct dpll *match_clock, - struct dpll *best_clock) -{ - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_device *dev = crtc->base.dev; - unsigned int best_error_ppm; - struct dpll clock; - u64 m2; - int found = false; - - memset(best_clock, 0, sizeof(*best_clock)); - best_error_ppm = 1000000; - - /* - * Based on hardware doc, the n always set to 1, and m1 always - * set to 2. If requires to support 200Mhz refclk, we need to - * revisit this because n may not 1 anymore. - */ - clock.n = 1; - clock.m1 = 2; - target *= 5; /* fast clock */ - - for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { - for (clock.p2 = limit->p2.p2_fast; - clock.p2 >= limit->p2.p2_slow; - clock.p2 -= clock.p2 > 10 ? 2 : 1) { - unsigned int error_ppm; - - clock.p = clock.p1 * clock.p2; - - m2 = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(target, clock.p * clock.n) << 22, - refclk * clock.m1); - - if (m2 > INT_MAX/clock.m1) - continue; - - clock.m2 = m2; - - chv_calc_dpll_params(refclk, &clock); - - if (!intel_pll_is_valid(to_i915(dev), limit, &clock)) - continue; - - if (!vlv_PLL_is_optimal(dev, target, &clock, best_clock, - best_error_ppm, &error_ppm)) - continue; - - *best_clock = clock; - best_error_ppm = error_ppm; - found = true; - } - } - - return found; -} - -bool bxt_find_best_dpll(struct intel_crtc_state *crtc_state, - struct dpll *best_clock) -{ - int refclk = 100000; - const struct intel_limit *limit = &intel_limits_bxt; - - return chv_find_best_dpll(limit, crtc_state, - crtc_state->port_clock, refclk, - NULL, best_clock); -} - static bool pipe_scanline_is_moving(struct drm_i915_private *dev_priv, enum pipe pipe) { @@ -1253,12 +491,6 @@ static void assert_planes_disabled(struct intel_crtc *crtc) assert_plane_disabled(plane); } -static void assert_vblank_disabled(struct drm_crtc *crtc) -{ - if (I915_STATE_WARN_ON(drm_crtc_vblank_get(crtc) == 0)) - drm_crtc_vblank_put(crtc); -} - void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, enum pipe pipe) { @@ -1743,55 +975,6 @@ enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc) return crtc->pipe; } -static u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - u32 mode_flags = crtc->mode_flags; - - /* - * From Gen 11, In case of dsi cmd mode, frame counter wouldnt - * have updated at the beginning of TE, if we want to use - * the hw counter, then we would find it updated in only - * the next TE, hence switching to sw counter. - */ - if (mode_flags & (I915_MODE_FLAG_DSI_USE_TE0 | I915_MODE_FLAG_DSI_USE_TE1)) - return 0; - - /* - * On i965gm the hardware frame counter reads - * zero when the TV encoder is enabled :( - */ - if (IS_I965GM(dev_priv) && - (crtc_state->output_types & BIT(INTEL_OUTPUT_TVOUT))) - return 0; - - if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) - return 0xffffffff; /* full 32 bit counter */ - else if (INTEL_GEN(dev_priv) >= 3) - return 0xffffff; /* only 24 bits of frame count */ - else - return 0; /* Gen2 doesn't have a hardware frame counter */ -} - -void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state) -{ - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - - assert_vblank_disabled(&crtc->base); - drm_crtc_set_max_vblank_count(&crtc->base, - intel_crtc_max_vblank_count(crtc_state)); - drm_crtc_vblank_on(&crtc->base); -} - -void intel_crtc_vblank_off(const struct intel_crtc_state *crtc_state) -{ - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - - drm_crtc_vblank_off(&crtc->base); - assert_vblank_disabled(&crtc->base); -} - void intel_enable_pipe(const struct intel_crtc_state *new_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); @@ -1954,7 +1137,7 @@ int intel_main_to_aux_plane(const struct drm_framebuffer *fb, int main_plane) bool intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info, - uint64_t modifier) + u64 modifier) { return info->is_yuv && info->num_planes == (is_ccs_modifier(modifier) ? 4 : 2); @@ -4535,532 +3718,6 @@ static void icl_set_pipe_chicken(struct intel_crtc *crtc) intel_de_write(dev_priv, PIPE_CHICKEN(pipe), tmp); } -static void intel_fdi_normal_train(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* enable normal train */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (IS_IVYBRIDGE(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_NONE_IVB; - temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; - } - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_NORMAL_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_NONE; - } - intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); - - /* wait one idle pattern time */ - intel_de_posting_read(dev_priv, reg); - udelay(1000); - - /* IVB wants error correction enabled */ - if (IS_IVYBRIDGE(dev_priv)) - intel_de_write(dev_priv, reg, - intel_de_read(dev_priv, reg) | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); -} - -/* The FDI link training functions for ILK/Ibexpeak. */ -static void ilk_fdi_link_train(struct intel_crtc *crtc, - const struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp, tries; - - /* FDI needs bits from pipe first */ - assert_pipe_enabled(dev_priv, crtc_state->cpu_transcoder); - - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit - for train result */ - reg = FDI_RX_IMR(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_RX_SYMBOL_LOCK; - temp &= ~FDI_RX_BIT_LOCK; - intel_de_write(dev_priv, reg, temp); - intel_de_read(dev_priv, reg); - udelay(150); - - /* enable CPU FDI TX and PCH FDI RX */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_DP_PORT_WIDTH_MASK; - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - /* Ironlake workaround, enable clock pointer after FDI enable*/ - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), - FDI_RX_PHASE_SYNC_POINTER_OVR); - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), - FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN); - - reg = FDI_RX_IIR(pipe); - for (tries = 0; tries < 5; tries++) { - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if ((temp & FDI_RX_BIT_LOCK)) { - drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n"); - intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK); - break; - } - } - if (tries == 5) - drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); - - /* Train 2 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - reg = FDI_RX_IIR(pipe); - for (tries = 0; tries < 5; tries++) { - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if (temp & FDI_RX_SYMBOL_LOCK) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_SYMBOL_LOCK); - drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n"); - break; - } - } - if (tries == 5) - drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); - - drm_dbg_kms(&dev_priv->drm, "FDI train done\n"); - -} - -static const int snb_b_fdi_train_param[] = { - FDI_LINK_TRAIN_400MV_0DB_SNB_B, - FDI_LINK_TRAIN_400MV_6DB_SNB_B, - FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, - FDI_LINK_TRAIN_800MV_0DB_SNB_B, -}; - -/* The FDI link training functions for SNB/Cougarpoint. */ -static void gen6_fdi_link_train(struct intel_crtc *crtc, - const struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp, i, retry; - - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit - for train result */ - reg = FDI_RX_IMR(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_RX_SYMBOL_LOCK; - temp &= ~FDI_RX_BIT_LOCK; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - /* enable CPU FDI TX and PCH FDI RX */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_DP_PORT_WIDTH_MASK; - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - /* SNB-B */ - temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); - - intel_de_write(dev_priv, FDI_RX_MISC(pipe), - FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - } - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - for (i = 0; i < 4; i++) { - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - temp |= snb_b_fdi_train_param[i]; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(500); - - for (retry = 0; retry < 5; retry++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - if (temp & FDI_RX_BIT_LOCK) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_BIT_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 1 done.\n"); - break; - } - udelay(50); - } - if (retry < 5) - break; - } - if (i == 4) - drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); - - /* Train 2 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - if (IS_GEN(dev_priv, 6)) { - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - /* SNB-B */ - temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; - } - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - } - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - for (i = 0; i < 4; i++) { - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - temp |= snb_b_fdi_train_param[i]; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(500); - - for (retry = 0; retry < 5; retry++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - if (temp & FDI_RX_SYMBOL_LOCK) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_SYMBOL_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 2 done.\n"); - break; - } - udelay(50); - } - if (retry < 5) - break; - } - if (i == 4) - drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); - - drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); -} - -/* Manual link training for Ivy Bridge A0 parts */ -static void ivb_manual_fdi_link_train(struct intel_crtc *crtc, - const struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp, i, j; - - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit - for train result */ - reg = FDI_RX_IMR(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_RX_SYMBOL_LOCK; - temp &= ~FDI_RX_BIT_LOCK; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n", - intel_de_read(dev_priv, FDI_RX_IIR(pipe))); - - /* Try each vswing and preemphasis setting twice before moving on */ - for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) { - /* disable first in case we need to retry */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); - temp &= ~FDI_TX_ENABLE; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_AUTO; - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp &= ~FDI_RX_ENABLE; - intel_de_write(dev_priv, reg, temp); - - /* enable CPU FDI TX and PCH FDI RX */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_DP_PORT_WIDTH_MASK; - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - temp |= snb_b_fdi_train_param[j/2]; - temp |= FDI_COMPOSITE_SYNC; - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); - - intel_de_write(dev_priv, FDI_RX_MISC(pipe), - FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; - temp |= FDI_COMPOSITE_SYNC; - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(1); /* should be 0.5us */ - - for (i = 0; i < 4; i++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if (temp & FDI_RX_BIT_LOCK || - (intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_BIT_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 1 done, level %i.\n", - i); - break; - } - udelay(1); /* should be 0.5us */ - } - if (i == 4) { - drm_dbg_kms(&dev_priv->drm, - "FDI train 1 fail on vswing %d\n", j / 2); - continue; - } - - /* Train 2 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE_IVB; - temp |= FDI_LINK_TRAIN_PATTERN_2_IVB; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(2); /* should be 1.5us */ - - for (i = 0; i < 4; i++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if (temp & FDI_RX_SYMBOL_LOCK || - (intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_SYMBOL_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 2 done, level %i.\n", - i); - goto train_done; - } - udelay(2); /* should be 1.5us */ - } - if (i == 4) - drm_dbg_kms(&dev_priv->drm, - "FDI train 2 fail on vswing %d\n", j / 2); - } - -train_done: - drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); -} - -static void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); - enum pipe pipe = intel_crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; - intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(200); - - /* Switch from Rawclk to PCDclk */ - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp | FDI_PCDCLK); - - intel_de_posting_read(dev_priv, reg); - udelay(200); - - /* Enable CPU FDI TX PLL, always on for Ironlake */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if ((temp & FDI_TX_PLL_ENABLE) == 0) { - intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(100); - } -} - -static void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc) -{ - struct drm_device *dev = intel_crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = intel_crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* Switch from PCDclk to Rawclk */ - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_PCDCLK); - - /* Disable CPU FDI TX PLL */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_TX_PLL_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(100); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_RX_PLL_ENABLE); - - /* Wait for the clocks to turn off. */ - intel_de_posting_read(dev_priv, reg); - udelay(100); -} - -static void ilk_fdi_disable(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* disable CPU FDI tx and PCH FDI rx */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_TX_ENABLE); - intel_de_posting_read(dev_priv, reg); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~(0x7 << 16); - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; - intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(100); - - /* Ironlake workaround, disable clock pointer after downing FDI */ - if (HAS_PCH_IBX(dev_priv)) - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), - FDI_RX_PHASE_SYNC_POINTER_OVR); - - /* still set train pattern 1 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - } - /* BPC in FDI rx is consistent with that in PIPECONF */ - temp &= ~(0x07 << 16); - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(100); -} - bool intel_has_pending_fb_unpin(struct drm_i915_private *dev_priv) { struct drm_crtc *crtc; @@ -5291,7 +3948,7 @@ static void ivb_update_fdi_bc_bifurcation(const struct intel_crtc_state *crtc_st * Finds the encoder associated with the given CRTC. This can only be * used when we know that the CRTC isn't feeding multiple encoders! */ -static struct intel_encoder * +struct intel_encoder * intel_get_crtc_new_encoder(const struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state) { @@ -5810,7 +4467,7 @@ static void cnl_program_nearest_filter_coefs(struct drm_i915_private *dev_priv, intel_de_write_fw(dev_priv, CNL_PS_COEF_INDEX_SET(pipe, id, set), 0); } -inline u32 skl_scaler_get_filter_select(enum drm_scaling_filter filter, int set) +u32 skl_scaler_get_filter_select(enum drm_scaling_filter filter, int set) { if (filter == DRM_SCALING_FILTER_NEAREST_NEIGHBOR) { return (PS_FILTER_PROGRAMMED | @@ -7467,143 +6124,6 @@ static void intel_connector_verify_state(struct intel_crtc_state *crtc_state, } } -static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state) -{ - if (crtc_state->hw.enable && crtc_state->has_pch_encoder) - return crtc_state->fdi_lanes; - - return 0; -} - -static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, - struct intel_crtc_state *pipe_config) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - struct drm_atomic_state *state = pipe_config->uapi.state; - struct intel_crtc *other_crtc; - struct intel_crtc_state *other_crtc_state; - - drm_dbg_kms(&dev_priv->drm, - "checking fdi config on pipe %c, lanes %i\n", - pipe_name(pipe), pipe_config->fdi_lanes); - if (pipe_config->fdi_lanes > 4) { - drm_dbg_kms(&dev_priv->drm, - "invalid fdi lane config on pipe %c: %i lanes\n", - pipe_name(pipe), pipe_config->fdi_lanes); - return -EINVAL; - } - - if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { - if (pipe_config->fdi_lanes > 2) { - drm_dbg_kms(&dev_priv->drm, - "only 2 lanes on haswell, required: %i lanes\n", - pipe_config->fdi_lanes); - return -EINVAL; - } else { - return 0; - } - } - - if (INTEL_NUM_PIPES(dev_priv) == 2) - return 0; - - /* Ivybridge 3 pipe is really complicated */ - switch (pipe) { - case PIPE_A: - return 0; - case PIPE_B: - if (pipe_config->fdi_lanes <= 2) - return 0; - - other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_C); - other_crtc_state = - intel_atomic_get_crtc_state(state, other_crtc); - if (IS_ERR(other_crtc_state)) - return PTR_ERR(other_crtc_state); - - if (pipe_required_fdi_lanes(other_crtc_state) > 0) { - drm_dbg_kms(&dev_priv->drm, - "invalid shared fdi lane config on pipe %c: %i lanes\n", - pipe_name(pipe), pipe_config->fdi_lanes); - return -EINVAL; - } - return 0; - case PIPE_C: - if (pipe_config->fdi_lanes > 2) { - drm_dbg_kms(&dev_priv->drm, - "only 2 lanes on pipe %c: required %i lanes\n", - pipe_name(pipe), pipe_config->fdi_lanes); - return -EINVAL; - } - - other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_B); - other_crtc_state = - intel_atomic_get_crtc_state(state, other_crtc); - if (IS_ERR(other_crtc_state)) - return PTR_ERR(other_crtc_state); - - if (pipe_required_fdi_lanes(other_crtc_state) > 2) { - drm_dbg_kms(&dev_priv->drm, - "fdi link B uses too many lanes to enable link C\n"); - return -EINVAL; - } - return 0; - default: - BUG(); - } -} - -#define RETRY 1 -static int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, - struct intel_crtc_state *pipe_config) -{ - struct drm_device *dev = intel_crtc->base.dev; - struct drm_i915_private *i915 = to_i915(dev); - const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - int lane, link_bw, fdi_dotclock, ret; - bool needs_recompute = false; - -retry: - /* FDI is a binary signal running at ~2.7GHz, encoding - * each output octet as 10 bits. The actual frequency - * is stored as a divider into a 100MHz clock, and the - * mode pixel clock is stored in units of 1KHz. - * Hence the bw of each lane in terms of the mode signal - * is: - */ - link_bw = intel_fdi_link_freq(i915, pipe_config); - - fdi_dotclock = adjusted_mode->crtc_clock; - - lane = ilk_get_lanes_required(fdi_dotclock, link_bw, - pipe_config->pipe_bpp); - - pipe_config->fdi_lanes = lane; - - intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, - link_bw, &pipe_config->fdi_m_n, false, false); - - ret = ilk_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config); - if (ret == -EDEADLK) - return ret; - - if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) { - pipe_config->pipe_bpp -= 2*3; - drm_dbg_kms(&i915->drm, - "fdi link bw constraint, reducing pipe bpp to %i\n", - pipe_config->pipe_bpp); - needs_recompute = true; - pipe_config->bw_constrained = true; - - goto retry; - } - - if (needs_recompute) - return RETRY; - - return ret; -} - bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); @@ -7959,51 +6479,6 @@ static void intel_panel_sanitize_ssc(struct drm_i915_private *dev_priv) } } -static bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) -{ - if (dev_priv->params.panel_use_ssc >= 0) - return dev_priv->params.panel_use_ssc != 0; - return dev_priv->vbt.lvds_use_ssc - && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); -} - -static u32 pnv_dpll_compute_fp(struct dpll *dpll) -{ - return (1 << dpll->n) << 16 | dpll->m2; -} - -static u32 i9xx_dpll_compute_fp(struct dpll *dpll) -{ - return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; -} - -static void i9xx_update_pll_dividers(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state, - struct dpll *reduced_clock) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - u32 fp, fp2 = 0; - - if (IS_PINEVIEW(dev_priv)) { - fp = pnv_dpll_compute_fp(&crtc_state->dpll); - if (reduced_clock) - fp2 = pnv_dpll_compute_fp(reduced_clock); - } else { - fp = i9xx_dpll_compute_fp(&crtc_state->dpll); - if (reduced_clock) - fp2 = i9xx_dpll_compute_fp(reduced_clock); - } - - crtc_state->dpll_hw_state.fp0 = fp; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && - reduced_clock) { - crtc_state->dpll_hw_state.fp1 = fp2; - } else { - crtc_state->dpll_hw_state.fp1 = fp; - } -} - static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv, enum pipe pipe) { @@ -8128,39 +6603,6 @@ void intel_dp_set_m_n(const struct intel_crtc_state *crtc_state, enum link_m_n_s intel_cpu_transcoder_set_m_n(crtc_state, dp_m_n, dp_m2_n2); } -static void vlv_compute_dpll(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config) -{ - pipe_config->dpll_hw_state.dpll = DPLL_INTEGRATED_REF_CLK_VLV | - DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; - if (crtc->pipe != PIPE_A) - pipe_config->dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; - - /* DPLL not used with DSI, but still need the rest set up */ - if (!intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DSI)) - pipe_config->dpll_hw_state.dpll |= DPLL_VCO_ENABLE | - DPLL_EXT_BUFFER_ENABLE_VLV; - - pipe_config->dpll_hw_state.dpll_md = - (pipe_config->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; -} - -static void chv_compute_dpll(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config) -{ - pipe_config->dpll_hw_state.dpll = DPLL_SSC_REF_CLK_CHV | - DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; - if (crtc->pipe != PIPE_A) - pipe_config->dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; - - /* DPLL not used with DSI, but still need the rest set up */ - if (!intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DSI)) - pipe_config->dpll_hw_state.dpll |= DPLL_VCO_ENABLE; - - pipe_config->dpll_hw_state.dpll_md = - (pipe_config->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; -} - static void vlv_prepare_pll(struct intel_crtc *crtc, const struct intel_crtc_state *pipe_config) { @@ -8420,128 +6862,7 @@ void vlv_force_pll_off(struct drm_i915_private *dev_priv, enum pipe pipe) vlv_disable_pll(dev_priv, pipe); } -static void i9xx_compute_dpll(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state, - struct dpll *reduced_clock) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - u32 dpll; - struct dpll *clock = &crtc_state->dpll; - - i9xx_update_pll_dividers(crtc, crtc_state, reduced_clock); - - dpll = DPLL_VGA_MODE_DIS; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) - dpll |= DPLLB_MODE_LVDS; - else - dpll |= DPLLB_MODE_DAC_SERIAL; - - if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || - IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) { - dpll |= (crtc_state->pixel_multiplier - 1) - << SDVO_MULTIPLIER_SHIFT_HIRES; - } - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO) || - intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) - dpll |= DPLL_SDVO_HIGH_SPEED; - - if (intel_crtc_has_dp_encoder(crtc_state)) - dpll |= DPLL_SDVO_HIGH_SPEED; - - /* compute bitmask from p1 value */ - if (IS_PINEVIEW(dev_priv)) - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW; - else { - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; - if (IS_G4X(dev_priv) && reduced_clock) - dpll |= (1 << (reduced_clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; - } - switch (clock->p2) { - case 5: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; - break; - case 7: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; - break; - case 10: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; - break; - case 14: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; - break; - } - if (INTEL_GEN(dev_priv) >= 4) - dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); - - if (crtc_state->sdvo_tv_clock) - dpll |= PLL_REF_INPUT_TVCLKINBC; - else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && - intel_panel_use_ssc(dev_priv)) - dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; - else - dpll |= PLL_REF_INPUT_DREFCLK; - - dpll |= DPLL_VCO_ENABLE; - crtc_state->dpll_hw_state.dpll = dpll; - - if (INTEL_GEN(dev_priv) >= 4) { - u32 dpll_md = (crtc_state->pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - crtc_state->dpll_hw_state.dpll_md = dpll_md; - } -} - -static void i8xx_compute_dpll(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state, - struct dpll *reduced_clock) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - u32 dpll; - struct dpll *clock = &crtc_state->dpll; - i9xx_update_pll_dividers(crtc, crtc_state, reduced_clock); - - dpll = DPLL_VGA_MODE_DIS; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; - } else { - if (clock->p1 == 2) - dpll |= PLL_P1_DIVIDE_BY_TWO; - else - dpll |= (clock->p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; - if (clock->p2 == 4) - dpll |= PLL_P2_DIVIDE_BY_4; - } - - /* - * Bspec: - * "[Almador Errata}: For the correct operation of the muxed DVO pins - * (GDEVSELB/I2Cdata, GIRDBY/I2CClk) and (GFRAMEB/DVI_Data, - * GTRDYB/DVI_Clk): Bit 31 (DPLL VCO Enable) and Bit 30 (2X Clock - * Enable) must be set to “1” in both the DPLL A Control Register - * (06014h-06017h) and DPLL B Control Register (06018h-0601Bh)." - * - * For simplicity We simply keep both bits always enabled in - * both DPLLS. The spec says we should disable the DVO 2X clock - * when not needed, but this seems to work fine in practice. - */ - if (IS_I830(dev_priv) || - intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DVO)) - dpll |= DPLL_DVO_2X_MODE; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && - intel_panel_use_ssc(dev_priv)) - dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; - else - dpll |= PLL_REF_INPUT_DREFCLK; - - dpll |= DPLL_VCO_ENABLE; - crtc_state->dpll_hw_state.dpll = dpll; -} static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state) { @@ -8747,207 +7068,6 @@ static void i9xx_set_pipeconf(const struct intel_crtc_state *crtc_state) intel_de_posting_read(dev_priv, PIPECONF(crtc->pipe)); } -static int i8xx_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - const struct intel_limit *limit; - int refclk = 48000; - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - if (intel_panel_use_ssc(dev_priv)) { - refclk = dev_priv->vbt.lvds_ssc_freq; - drm_dbg_kms(&dev_priv->drm, - "using SSC reference clock of %d kHz\n", - refclk); - } - - limit = &intel_limits_i8xx_lvds; - } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DVO)) { - limit = &intel_limits_i8xx_dvo; - } else { - limit = &intel_limits_i8xx_dac; - } - - if (!crtc_state->clock_set && - !i9xx_find_best_dpll(limit, crtc_state, crtc_state->port_clock, - refclk, NULL, &crtc_state->dpll)) { - drm_err(&dev_priv->drm, - "Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - i8xx_compute_dpll(crtc, crtc_state, NULL); - - return 0; -} - -static int g4x_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - const struct intel_limit *limit; - int refclk = 96000; - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - if (intel_panel_use_ssc(dev_priv)) { - refclk = dev_priv->vbt.lvds_ssc_freq; - drm_dbg_kms(&dev_priv->drm, - "using SSC reference clock of %d kHz\n", - refclk); - } - - if (intel_is_dual_link_lvds(dev_priv)) - limit = &intel_limits_g4x_dual_channel_lvds; - else - limit = &intel_limits_g4x_single_channel_lvds; - } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) || - intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG)) { - limit = &intel_limits_g4x_hdmi; - } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO)) { - limit = &intel_limits_g4x_sdvo; - } else { - /* The option is for other outputs */ - limit = &intel_limits_i9xx_sdvo; - } - - if (!crtc_state->clock_set && - !g4x_find_best_dpll(limit, crtc_state, crtc_state->port_clock, - refclk, NULL, &crtc_state->dpll)) { - drm_err(&dev_priv->drm, - "Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - i9xx_compute_dpll(crtc, crtc_state, NULL); - - return 0; -} - -static int pnv_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - const struct intel_limit *limit; - int refclk = 96000; - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - if (intel_panel_use_ssc(dev_priv)) { - refclk = dev_priv->vbt.lvds_ssc_freq; - drm_dbg_kms(&dev_priv->drm, - "using SSC reference clock of %d kHz\n", - refclk); - } - - limit = &pnv_limits_lvds; - } else { - limit = &pnv_limits_sdvo; - } - - if (!crtc_state->clock_set && - !pnv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, - refclk, NULL, &crtc_state->dpll)) { - drm_err(&dev_priv->drm, - "Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - i9xx_compute_dpll(crtc, crtc_state, NULL); - - return 0; -} - -static int i9xx_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - const struct intel_limit *limit; - int refclk = 96000; - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - if (intel_panel_use_ssc(dev_priv)) { - refclk = dev_priv->vbt.lvds_ssc_freq; - drm_dbg_kms(&dev_priv->drm, - "using SSC reference clock of %d kHz\n", - refclk); - } - - limit = &intel_limits_i9xx_lvds; - } else { - limit = &intel_limits_i9xx_sdvo; - } - - if (!crtc_state->clock_set && - !i9xx_find_best_dpll(limit, crtc_state, crtc_state->port_clock, - refclk, NULL, &crtc_state->dpll)) { - drm_err(&dev_priv->drm, - "Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - i9xx_compute_dpll(crtc, crtc_state, NULL); - - return 0; -} - -static int chv_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - int refclk = 100000; - const struct intel_limit *limit = &intel_limits_chv; - struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - if (!crtc_state->clock_set && - !chv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, - refclk, NULL, &crtc_state->dpll)) { - drm_err(&i915->drm, "Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - chv_compute_dpll(crtc, crtc_state); - - return 0; -} - -static int vlv_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - int refclk = 100000; - const struct intel_limit *limit = &intel_limits_vlv; - struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - if (!crtc_state->clock_set && - !vlv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, - refclk, NULL, &crtc_state->dpll)) { - drm_err(&i915->drm, "Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - vlv_compute_dpll(crtc, crtc_state); - - return 0; -} static bool i9xx_has_pfit(struct drm_i915_private *dev_priv) { @@ -9958,172 +8078,6 @@ int ilk_get_lanes_required(int target_clock, int link_bw, int bpp) return DIV_ROUND_UP(bps, link_bw * 8); } -static bool ilk_needs_fb_cb_tune(struct dpll *dpll, int factor) -{ - return i9xx_dpll_compute_m(dpll) < factor * dpll->n; -} - -static void ilk_compute_dpll(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state, - struct dpll *reduced_clock) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - u32 dpll, fp, fp2; - int factor; - - /* Enable autotuning of the PLL clock (if permissible) */ - factor = 21; - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - if ((intel_panel_use_ssc(dev_priv) && - dev_priv->vbt.lvds_ssc_freq == 100000) || - (HAS_PCH_IBX(dev_priv) && - intel_is_dual_link_lvds(dev_priv))) - factor = 25; - } else if (crtc_state->sdvo_tv_clock) { - factor = 20; - } - - fp = i9xx_dpll_compute_fp(&crtc_state->dpll); - - if (ilk_needs_fb_cb_tune(&crtc_state->dpll, factor)) - fp |= FP_CB_TUNE; - - if (reduced_clock) { - fp2 = i9xx_dpll_compute_fp(reduced_clock); - - if (reduced_clock->m < factor * reduced_clock->n) - fp2 |= FP_CB_TUNE; - } else { - fp2 = fp; - } - - dpll = 0; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) - dpll |= DPLLB_MODE_LVDS; - else - dpll |= DPLLB_MODE_DAC_SERIAL; - - dpll |= (crtc_state->pixel_multiplier - 1) - << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO) || - intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) - dpll |= DPLL_SDVO_HIGH_SPEED; - - if (intel_crtc_has_dp_encoder(crtc_state)) - dpll |= DPLL_SDVO_HIGH_SPEED; - - /* - * The high speed IO clock is only really required for - * SDVO/HDMI/DP, but we also enable it for CRT to make it - * possible to share the DPLL between CRT and HDMI. Enabling - * the clock needlessly does no real harm, except use up a - * bit of power potentially. - * - * We'll limit this to IVB with 3 pipes, since it has only two - * DPLLs and so DPLL sharing is the only way to get three pipes - * driving PCH ports at the same time. On SNB we could do this, - * and potentially avoid enabling the second DPLL, but it's not - * clear if it''s a win or loss power wise. No point in doing - * this on ILK at all since it has a fixed DPLL<->pipe mapping. - */ - if (INTEL_NUM_PIPES(dev_priv) == 3 && - intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG)) - dpll |= DPLL_SDVO_HIGH_SPEED; - - /* compute bitmask from p1 value */ - dpll |= (1 << (crtc_state->dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; - /* also FPA1 */ - dpll |= (1 << (crtc_state->dpll.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; - - switch (crtc_state->dpll.p2) { - case 5: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; - break; - case 7: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; - break; - case 10: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; - break; - case 14: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; - break; - } - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && - intel_panel_use_ssc(dev_priv)) - dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; - else - dpll |= PLL_REF_INPUT_DREFCLK; - - dpll |= DPLL_VCO_ENABLE; - - crtc_state->dpll_hw_state.dpll = dpll; - crtc_state->dpll_hw_state.fp0 = fp; - crtc_state->dpll_hw_state.fp1 = fp2; -} - -static int ilk_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct intel_atomic_state *state = - to_intel_atomic_state(crtc_state->uapi.state); - const struct intel_limit *limit; - int refclk = 120000; - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ - if (!crtc_state->has_pch_encoder) - return 0; - - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { - if (intel_panel_use_ssc(dev_priv)) { - drm_dbg_kms(&dev_priv->drm, - "using SSC reference clock of %d kHz\n", - dev_priv->vbt.lvds_ssc_freq); - refclk = dev_priv->vbt.lvds_ssc_freq; - } - - if (intel_is_dual_link_lvds(dev_priv)) { - if (refclk == 100000) - limit = &ilk_limits_dual_lvds_100m; - else - limit = &ilk_limits_dual_lvds; - } else { - if (refclk == 100000) - limit = &ilk_limits_single_lvds_100m; - else - limit = &ilk_limits_single_lvds; - } - } else { - limit = &ilk_limits_dac; - } - - if (!crtc_state->clock_set && - !g4x_find_best_dpll(limit, crtc_state, crtc_state->port_clock, - refclk, NULL, &crtc_state->dpll)) { - drm_err(&dev_priv->drm, - "Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - ilk_compute_dpll(crtc, crtc_state, NULL); - - if (!intel_reserve_shared_dplls(state, crtc, NULL)) { - drm_dbg_kms(&dev_priv->drm, - "failed to find PLL for pipe %c\n", - pipe_name(crtc->pipe)); - return -EINVAL; - } - - return 0; -} - static void intel_pch_transcoder_get_m_n(struct intel_crtc *crtc, struct intel_link_m_n *m_n) { @@ -10536,29 +8490,6 @@ out: return ret; } -static int hsw_crtc_compute_clock(struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct intel_atomic_state *state = - to_intel_atomic_state(crtc_state->uapi.state); - - if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI) || - INTEL_GEN(dev_priv) >= 11) { - struct intel_encoder *encoder = - intel_get_crtc_new_encoder(state, crtc_state); - - if (!intel_reserve_shared_dplls(state, crtc, encoder)) { - drm_dbg_kms(&dev_priv->drm, - "failed to find PLL for pipe %c\n", - pipe_name(crtc->pipe)); - return -EINVAL; - } - } - - return 0; -} - static void dg1_get_ddi_pll(struct drm_i915_private *dev_priv, enum port port, struct intel_crtc_state *pipe_config) { @@ -10952,8 +8883,6 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc, bool active; u32 tmp; - pipe_config->master_transcoder = INVALID_TRANSCODER; - if (!intel_display_power_get_in_set_if_enabled(dev_priv, &power_domain_set, POWER_DOMAIN_PIPE(crtc->pipe))) return false; @@ -11464,33 +9393,6 @@ static void ilk_pch_clock_get(struct intel_crtc *crtc, &pipe_config->fdi_m_n); } -static void intel_crtc_state_reset(struct intel_crtc_state *crtc_state, - struct intel_crtc *crtc) -{ - memset(crtc_state, 0, sizeof(*crtc_state)); - - __drm_atomic_helper_crtc_state_reset(&crtc_state->uapi, &crtc->base); - - crtc_state->cpu_transcoder = INVALID_TRANSCODER; - crtc_state->master_transcoder = INVALID_TRANSCODER; - crtc_state->hsw_workaround_pipe = INVALID_PIPE; - crtc_state->output_format = INTEL_OUTPUT_FORMAT_INVALID; - crtc_state->scaler_state.scaler_id = -1; - crtc_state->mst_master_transcoder = INVALID_TRANSCODER; -} - -static struct intel_crtc_state *intel_crtc_state_alloc(struct intel_crtc *crtc) -{ - struct intel_crtc_state *crtc_state; - - crtc_state = kmalloc(sizeof(*crtc_state), GFP_KERNEL); - - if (crtc_state) - intel_crtc_state_reset(crtc_state, crtc); - - return crtc_state; -} - /* Returns the currently programmed mode of the given encoder. */ struct drm_display_mode * intel_encoder_current_mode(struct intel_encoder *encoder) @@ -11531,14 +9433,6 @@ intel_encoder_current_mode(struct intel_encoder *encoder) return mode; } -static void intel_crtc_destroy(struct drm_crtc *crtc) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - drm_crtc_cleanup(crtc); - kfree(intel_crtc); -} - /** * intel_wm_need_update - Check whether watermarks need updating * @cur: current plane state @@ -12754,7 +10648,7 @@ encoder_retry: return ret; } - if (ret == RETRY) { + if (ret == I915_DISPLAY_CONFIG_RETRY) { if (drm_WARN(&i915->drm, !retry, "loop in pipe configuration computation\n")) return -EINVAL; @@ -14742,17 +12636,6 @@ static int intel_atomic_prepare_commit(struct intel_atomic_state *state) return 0; } -u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(&crtc->base)]; - - if (!vblank->max_vblank_count) - return (u32)drm_crtc_accurate_vblank_count(&crtc->base); - - return crtc->base.funcs->get_vblank_counter(&crtc->base); -} - void intel_crtc_arm_fifo_underrun(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state) { @@ -15805,113 +13688,6 @@ void intel_plane_destroy(struct drm_plane *plane) kfree(to_intel_plane(plane)); } -static int intel_crtc_late_register(struct drm_crtc *crtc) -{ - intel_crtc_debugfs_add(crtc); - return 0; -} - -#define INTEL_CRTC_FUNCS \ - .set_config = drm_atomic_helper_set_config, \ - .destroy = intel_crtc_destroy, \ - .page_flip = drm_atomic_helper_page_flip, \ - .atomic_duplicate_state = intel_crtc_duplicate_state, \ - .atomic_destroy_state = intel_crtc_destroy_state, \ - .set_crc_source = intel_crtc_set_crc_source, \ - .verify_crc_source = intel_crtc_verify_crc_source, \ - .get_crc_sources = intel_crtc_get_crc_sources, \ - .late_register = intel_crtc_late_register - -static const struct drm_crtc_funcs bdw_crtc_funcs = { - INTEL_CRTC_FUNCS, - - .get_vblank_counter = g4x_get_vblank_counter, - .enable_vblank = bdw_enable_vblank, - .disable_vblank = bdw_disable_vblank, - .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, -}; - -static const struct drm_crtc_funcs ilk_crtc_funcs = { - INTEL_CRTC_FUNCS, - - .get_vblank_counter = g4x_get_vblank_counter, - .enable_vblank = ilk_enable_vblank, - .disable_vblank = ilk_disable_vblank, - .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, -}; - -static const struct drm_crtc_funcs g4x_crtc_funcs = { - INTEL_CRTC_FUNCS, - - .get_vblank_counter = g4x_get_vblank_counter, - .enable_vblank = i965_enable_vblank, - .disable_vblank = i965_disable_vblank, - .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, -}; - -static const struct drm_crtc_funcs i965_crtc_funcs = { - INTEL_CRTC_FUNCS, - - .get_vblank_counter = i915_get_vblank_counter, - .enable_vblank = i965_enable_vblank, - .disable_vblank = i965_disable_vblank, - .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, -}; - -static const struct drm_crtc_funcs i915gm_crtc_funcs = { - INTEL_CRTC_FUNCS, - - .get_vblank_counter = i915_get_vblank_counter, - .enable_vblank = i915gm_enable_vblank, - .disable_vblank = i915gm_disable_vblank, - .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, -}; - -static const struct drm_crtc_funcs i915_crtc_funcs = { - INTEL_CRTC_FUNCS, - - .get_vblank_counter = i915_get_vblank_counter, - .enable_vblank = i8xx_enable_vblank, - .disable_vblank = i8xx_disable_vblank, - .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, -}; - -static const struct drm_crtc_funcs i8xx_crtc_funcs = { - INTEL_CRTC_FUNCS, - - /* no hw vblank counter */ - .enable_vblank = i8xx_enable_vblank, - .disable_vblank = i8xx_disable_vblank, - .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, -}; - -static struct intel_crtc *intel_crtc_alloc(void) -{ - struct intel_crtc_state *crtc_state; - struct intel_crtc *crtc; - - crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); - if (!crtc) - return ERR_PTR(-ENOMEM); - - crtc_state = intel_crtc_state_alloc(crtc); - if (!crtc_state) { - kfree(crtc); - return ERR_PTR(-ENOMEM); - } - - crtc->base.state = &crtc_state->uapi; - crtc->config = crtc_state; - - return crtc; -} - -static void intel_crtc_free(struct intel_crtc *crtc) -{ - intel_crtc_destroy_state(&crtc->base, crtc->base.state); - kfree(crtc); -} - static void intel_plane_possible_crtcs_init(struct drm_i915_private *dev_priv) { struct intel_plane *plane; @@ -15924,100 +13700,6 @@ static void intel_plane_possible_crtcs_init(struct drm_i915_private *dev_priv) } } -static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) -{ - struct intel_plane *primary, *cursor; - const struct drm_crtc_funcs *funcs; - struct intel_crtc *crtc; - int sprite, ret; - - crtc = intel_crtc_alloc(); - if (IS_ERR(crtc)) - return PTR_ERR(crtc); - - crtc->pipe = pipe; - crtc->num_scalers = RUNTIME_INFO(dev_priv)->num_scalers[pipe]; - - primary = intel_primary_plane_create(dev_priv, pipe); - if (IS_ERR(primary)) { - ret = PTR_ERR(primary); - goto fail; - } - crtc->plane_ids_mask |= BIT(primary->id); - - for_each_sprite(dev_priv, pipe, sprite) { - struct intel_plane *plane; - - plane = intel_sprite_plane_create(dev_priv, pipe, sprite); - if (IS_ERR(plane)) { - ret = PTR_ERR(plane); - goto fail; - } - crtc->plane_ids_mask |= BIT(plane->id); - } - - cursor = intel_cursor_plane_create(dev_priv, pipe); - if (IS_ERR(cursor)) { - ret = PTR_ERR(cursor); - goto fail; - } - crtc->plane_ids_mask |= BIT(cursor->id); - - if (HAS_GMCH(dev_priv)) { - if (IS_CHERRYVIEW(dev_priv) || - IS_VALLEYVIEW(dev_priv) || IS_G4X(dev_priv)) - funcs = &g4x_crtc_funcs; - else if (IS_GEN(dev_priv, 4)) - funcs = &i965_crtc_funcs; - else if (IS_I945GM(dev_priv) || IS_I915GM(dev_priv)) - funcs = &i915gm_crtc_funcs; - else if (IS_GEN(dev_priv, 3)) - funcs = &i915_crtc_funcs; - else - funcs = &i8xx_crtc_funcs; - } else { - if (INTEL_GEN(dev_priv) >= 8) - funcs = &bdw_crtc_funcs; - else - funcs = &ilk_crtc_funcs; - } - - ret = drm_crtc_init_with_planes(&dev_priv->drm, &crtc->base, - &primary->base, &cursor->base, - funcs, "pipe %c", pipe_name(pipe)); - if (ret) - goto fail; - - BUG_ON(pipe >= ARRAY_SIZE(dev_priv->pipe_to_crtc_mapping) || - dev_priv->pipe_to_crtc_mapping[pipe] != NULL); - dev_priv->pipe_to_crtc_mapping[pipe] = crtc; - - if (INTEL_GEN(dev_priv) < 9) { - enum i9xx_plane_id i9xx_plane = primary->i9xx_plane; - - BUG_ON(i9xx_plane >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || - dev_priv->plane_to_crtc_mapping[i9xx_plane] != NULL); - dev_priv->plane_to_crtc_mapping[i9xx_plane] = crtc; - } - - if (INTEL_GEN(dev_priv) >= 10) - drm_crtc_create_scaling_filter_property(&crtc->base, - BIT(DRM_SCALING_FILTER_DEFAULT) | - BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR)); - - intel_color_init(crtc); - - intel_crtc_crc_init(crtc); - - drm_WARN_ON(&dev_priv->drm, drm_crtc_index(&crtc->base) != crtc->pipe); - - return 0; - -fail: - intel_crtc_free(crtc); - - return ret; -} int intel_get_pipe_from_crtc_id_ioctl(struct drm_device *dev, void *data, struct drm_file *file) @@ -16740,86 +14422,40 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) { intel_init_cdclk_hooks(dev_priv); + intel_dpll_init_clock_hook(dev_priv); + if (INTEL_GEN(dev_priv) >= 9) { dev_priv->display.get_pipe_config = hsw_get_pipe_config; - dev_priv->display.get_initial_plane_config = - skl_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = hsw_crtc_compute_clock; dev_priv->display.crtc_enable = hsw_crtc_enable; dev_priv->display.crtc_disable = hsw_crtc_disable; } else if (HAS_DDI(dev_priv)) { dev_priv->display.get_pipe_config = hsw_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = - hsw_crtc_compute_clock; dev_priv->display.crtc_enable = hsw_crtc_enable; dev_priv->display.crtc_disable = hsw_crtc_disable; } else if (HAS_PCH_SPLIT(dev_priv)) { dev_priv->display.get_pipe_config = ilk_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = - ilk_crtc_compute_clock; dev_priv->display.crtc_enable = ilk_crtc_enable; dev_priv->display.crtc_disable = ilk_crtc_disable; - } else if (IS_CHERRYVIEW(dev_priv)) { - dev_priv->display.get_pipe_config = i9xx_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = chv_crtc_compute_clock; - dev_priv->display.crtc_enable = valleyview_crtc_enable; - dev_priv->display.crtc_disable = i9xx_crtc_disable; - } else if (IS_VALLEYVIEW(dev_priv)) { + } else if (IS_CHERRYVIEW(dev_priv) || + IS_VALLEYVIEW(dev_priv)) { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = vlv_crtc_compute_clock; dev_priv->display.crtc_enable = valleyview_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; - } else if (IS_G4X(dev_priv)) { - dev_priv->display.get_pipe_config = i9xx_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = g4x_crtc_compute_clock; - dev_priv->display.crtc_enable = i9xx_crtc_enable; - dev_priv->display.crtc_disable = i9xx_crtc_disable; - } else if (IS_PINEVIEW(dev_priv)) { - dev_priv->display.get_pipe_config = i9xx_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = pnv_crtc_compute_clock; - dev_priv->display.crtc_enable = i9xx_crtc_enable; - dev_priv->display.crtc_disable = i9xx_crtc_disable; - } else if (!IS_GEN(dev_priv, 2)) { - dev_priv->display.get_pipe_config = i9xx_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = i9xx_crtc_compute_clock; - dev_priv->display.crtc_enable = i9xx_crtc_enable; - dev_priv->display.crtc_disable = i9xx_crtc_disable; } else { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; - dev_priv->display.get_initial_plane_config = - i9xx_get_initial_plane_config; - dev_priv->display.crtc_compute_clock = i8xx_crtc_compute_clock; dev_priv->display.crtc_enable = i9xx_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; } - if (IS_GEN(dev_priv, 5)) { - dev_priv->display.fdi_link_train = ilk_fdi_link_train; - } else if (IS_GEN(dev_priv, 6)) { - dev_priv->display.fdi_link_train = gen6_fdi_link_train; - } else if (IS_IVYBRIDGE(dev_priv)) { - /* FIXME: detect B0+ stepping and use auto training */ - dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; - } + intel_fdi_init_hook(dev_priv); - if (INTEL_GEN(dev_priv) >= 9) + if (INTEL_GEN(dev_priv) >= 9) { dev_priv->display.commit_modeset_enables = skl_commit_modeset_enables; - else + dev_priv->display.get_initial_plane_config = skl_get_initial_plane_config; + } else { dev_priv->display.commit_modeset_enables = intel_commit_modeset_enables; + dev_priv->display.get_initial_plane_config = i9xx_get_initial_plane_config; + } } diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 7ddbc00a0f41..bb72de152949 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -643,7 +643,7 @@ void intel_display_print_error_state(struct drm_i915_error_state_buf *e, bool intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info, - uint64_t modifier); + u64 modifier); int intel_plane_compute_gtt(struct intel_plane_state *plane_state); u32 intel_plane_compute_aligned_offset(int *x, int *y, @@ -651,6 +651,9 @@ u32 intel_plane_compute_aligned_offset(int *x, int *y, int color_plane); int intel_plane_pin_fb(struct intel_plane_state *plane_state); void intel_plane_unpin_fb(struct intel_plane_state *old_plane_state); +struct intel_encoder * +intel_get_crtc_new_encoder(const struct intel_atomic_state *state, + const struct intel_crtc_state *crtc_state); /* modesetting */ void intel_modeset_init_hw(struct drm_i915_private *i915); diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index d52374f01316..c11c37c65d86 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -4,7 +4,6 @@ */ #include "display/intel_crt.h" -#include "display/intel_dp.h" #include "i915_drv.h" #include "i915_irq.h" @@ -16,6 +15,7 @@ #include "intel_dpio_phy.h" #include "intel_hotplug.h" #include "intel_pm.h" +#include "intel_pps.h" #include "intel_sideband.h" #include "intel_tc.h" #include "intel_vga.h" @@ -936,7 +936,7 @@ static void bxt_enable_dc9(struct drm_i915_private *dev_priv) * because PPS registers are always on. */ if (!HAS_PCH_SPLIT(dev_priv)) - intel_power_sequencer_reset(dev_priv); + intel_pps_reset_all(dev_priv); gen9_set_dc_state(dev_priv, DC_STATE_EN_DC9); } @@ -1446,7 +1446,7 @@ static void vlv_display_power_well_deinit(struct drm_i915_private *dev_priv) /* make sure we're done processing display irqs */ intel_synchronize_irq(dev_priv); - intel_power_sequencer_reset(dev_priv); + intel_pps_reset_all(dev_priv); /* Prevent us from re-enabling polling on accident in late suspend */ if (!dev_priv->drm.dev->power.is_suspended) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 9dfad41eb3dc..3c7be009a444 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -228,7 +228,7 @@ struct intel_encoder { struct intel_panel_bl_funcs { /* Connector and platform specific backlight functions */ int (*setup)(struct intel_connector *connector, enum pipe pipe); - u32 (*get)(struct intel_connector *connector); + u32 (*get)(struct intel_connector *connector, enum pipe pipe); void (*set)(const struct drm_connector_state *conn_state, u32 level); void (*disable)(const struct drm_connector_state *conn_state, u32 level); void (*enable)(const struct intel_crtc_state *crtc_state, @@ -343,6 +343,10 @@ struct intel_hdcp_shim { enum transcoder cpu_transcoder, bool enable); + /* Enable/Disable stream encryption on DP MST Transport Link */ + int (*stream_encryption)(struct intel_connector *connector, + bool enable); + /* Ensures the link is still protected */ bool (*check_link)(struct intel_digital_port *dig_port, struct intel_connector *connector); @@ -374,8 +378,13 @@ struct intel_hdcp_shim { int (*config_stream_type)(struct intel_digital_port *dig_port, bool is_repeater, u8 type); + /* Enable/Disable HDCP 2.2 stream encryption on DP MST Transport Link */ + int (*stream_2_2_encryption)(struct intel_connector *connector, + bool enable); + /* HDCP2.2 Link Integrity Check */ - int (*check_2_2_link)(struct intel_digital_port *dig_port); + int (*check_2_2_link)(struct intel_digital_port *dig_port, + struct intel_connector *connector); }; struct intel_hdcp { @@ -402,7 +411,6 @@ struct intel_hdcp { * content can flow only through a link protected by HDCP2.2. */ u8 content_type; - struct hdcp_port_data port_data; bool is_paired; bool is_repeater; @@ -436,6 +444,8 @@ struct intel_hdcp { * Hence caching the transcoder here. */ enum transcoder cpu_transcoder; + /* Only used for DP MST stream encryption */ + enum transcoder stream_transcoder; }; struct intel_connector { @@ -535,7 +545,7 @@ struct intel_plane_state { struct drm_framebuffer *fb; u16 alpha; - uint16_t pixel_blend_mode; + u16 pixel_blend_mode; unsigned int rotation; enum drm_color_encoding color_encoding; enum drm_color_range color_range; @@ -1511,10 +1521,14 @@ struct intel_digital_port { enum phy_fia tc_phy_fia; u8 tc_phy_fia_idx; - /* protects num_hdcp_streams reference count */ + /* protects num_hdcp_streams reference count, hdcp_port_data and hdcp_auth_status */ struct mutex hdcp_mutex; /* the number of pipes using HDCP signalling out of this port */ unsigned int num_hdcp_streams; + /* port HDCP auth status */ + bool hdcp_auth_status; + /* HDCP port data need to pass to security f/w */ + struct hdcp_port_data hdcp_port_data; void (*write_infoframe)(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, @@ -1825,4 +1839,26 @@ to_intel_frontbuffer(struct drm_framebuffer *fb) return fb ? to_intel_framebuffer(fb)->frontbuffer : NULL; } +static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) +{ + if (dev_priv->params.panel_use_ssc >= 0) + return dev_priv->params.panel_use_ssc != 0; + return dev_priv->vbt.lvds_use_ssc + && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); +} + +static inline u32 i9xx_dpll_compute_fp(struct dpll *dpll) +{ + return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; +} + +static inline u32 intel_fdi_link_freq(struct drm_i915_private *dev_priv, + const struct intel_crtc_state *pipe_config) +{ + if (HAS_DDI(dev_priv)) + return pipe_config->port_clock; /* SPLL */ + else + return dev_priv->fdi_pll_freq; +} + #endif /* __INTEL_DISPLAY_TYPES_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 469e765a1b7b..4a2945324e7c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -58,6 +58,7 @@ #include "intel_lspcon.h" #include "intel_lvds.h" #include "intel_panel.h" +#include "intel_pps.h" #include "intel_psr.h" #include "intel_sideband.h" #include "intel_tc.h" @@ -121,6 +122,11 @@ static const struct dp_link_dpll chv_dpll[] = { { .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } }, }; +const struct dpll *vlv_get_dpll(struct drm_i915_private *i915) +{ + return IS_CHERRYVIEW(i915) ? &chv_dpll[0].dpll : &vlv_dpll[0].dpll; +} + /* Constants for DP DSC configurations */ static const u8 valid_dsc_bpp[] = {6, 8, 10, 12, 15}; @@ -145,12 +151,6 @@ bool intel_dp_is_edp(struct intel_dp *intel_dp) static void intel_dp_link_down(struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state); -static bool edp_panel_vdd_on(struct intel_dp *intel_dp); -static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); -static void vlv_init_panel_power_sequencer(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state); -static void vlv_steal_power_sequencer(struct drm_i915_private *dev_priv, - enum pipe pipe); static void intel_dp_unset_edid(struct intel_dp *intel_dp); /* update sink rates from dpcd */ @@ -884,447 +884,6 @@ static void intel_dp_unpack_aux(u32 src, u8 *dst, int dst_bytes) dst[i] = src >> ((3-i) * 8); } -static void -intel_dp_init_panel_power_sequencer(struct intel_dp *intel_dp); -static void -intel_dp_init_panel_power_sequencer_registers(struct intel_dp *intel_dp, - bool force_disable_vdd); -static void -intel_dp_pps_init(struct intel_dp *intel_dp); - -static intel_wakeref_t -pps_lock(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - intel_wakeref_t wakeref; - - /* - * See intel_power_sequencer_reset() why we need - * a power domain reference here. - */ - wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_DISPLAY_CORE); - mutex_lock(&dev_priv->pps_mutex); - - return wakeref; -} - -static intel_wakeref_t -pps_unlock(struct intel_dp *intel_dp, intel_wakeref_t wakeref) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - - mutex_unlock(&dev_priv->pps_mutex); - intel_display_power_put(dev_priv, POWER_DOMAIN_DISPLAY_CORE, wakeref); - return 0; -} - -#define with_pps_lock(dp, wf) \ - for ((wf) = pps_lock(dp); (wf); (wf) = pps_unlock((dp), (wf))) - -static void -vlv_power_sequencer_kick(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum pipe pipe = intel_dp->pps_pipe; - bool pll_enabled, release_cl_override = false; - enum dpio_phy phy = DPIO_PHY(pipe); - enum dpio_channel ch = vlv_pipe_to_channel(pipe); - u32 DP; - - if (drm_WARN(&dev_priv->drm, - intel_de_read(dev_priv, intel_dp->output_reg) & DP_PORT_EN, - "skipping pipe %c power sequencer kick due to [ENCODER:%d:%s] being active\n", - pipe_name(pipe), dig_port->base.base.base.id, - dig_port->base.base.name)) - return; - - drm_dbg_kms(&dev_priv->drm, - "kicking pipe %c power sequencer for [ENCODER:%d:%s]\n", - pipe_name(pipe), dig_port->base.base.base.id, - dig_port->base.base.name); - - /* Preserve the BIOS-computed detected bit. This is - * supposed to be read-only. - */ - DP = intel_de_read(dev_priv, intel_dp->output_reg) & DP_DETECTED; - DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; - DP |= DP_PORT_WIDTH(1); - DP |= DP_LINK_TRAIN_PAT_1; - - if (IS_CHERRYVIEW(dev_priv)) - DP |= DP_PIPE_SEL_CHV(pipe); - else - DP |= DP_PIPE_SEL(pipe); - - pll_enabled = intel_de_read(dev_priv, DPLL(pipe)) & DPLL_VCO_ENABLE; - - /* - * The DPLL for the pipe must be enabled for this to work. - * So enable temporarily it if it's not already enabled. - */ - if (!pll_enabled) { - release_cl_override = IS_CHERRYVIEW(dev_priv) && - !chv_phy_powergate_ch(dev_priv, phy, ch, true); - - if (vlv_force_pll_on(dev_priv, pipe, IS_CHERRYVIEW(dev_priv) ? - &chv_dpll[0].dpll : &vlv_dpll[0].dpll)) { - drm_err(&dev_priv->drm, - "Failed to force on pll for pipe %c!\n", - pipe_name(pipe)); - return; - } - } - - /* - * Similar magic as in intel_dp_enable_port(). - * We _must_ do this port enable + disable trick - * to make this power sequencer lock onto the port. - * Otherwise even VDD force bit won't work. - */ - intel_de_write(dev_priv, intel_dp->output_reg, DP); - intel_de_posting_read(dev_priv, intel_dp->output_reg); - - intel_de_write(dev_priv, intel_dp->output_reg, DP | DP_PORT_EN); - intel_de_posting_read(dev_priv, intel_dp->output_reg); - - intel_de_write(dev_priv, intel_dp->output_reg, DP & ~DP_PORT_EN); - intel_de_posting_read(dev_priv, intel_dp->output_reg); - - if (!pll_enabled) { - vlv_force_pll_off(dev_priv, pipe); - - if (release_cl_override) - chv_phy_powergate_ch(dev_priv, phy, ch, false); - } -} - -static enum pipe vlv_find_free_pps(struct drm_i915_private *dev_priv) -{ - struct intel_encoder *encoder; - unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B); - - /* - * We don't have power sequencer currently. - * Pick one that's not used by other ports. - */ - for_each_intel_dp(&dev_priv->drm, encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - - if (encoder->type == INTEL_OUTPUT_EDP) { - drm_WARN_ON(&dev_priv->drm, - intel_dp->active_pipe != INVALID_PIPE && - intel_dp->active_pipe != - intel_dp->pps_pipe); - - if (intel_dp->pps_pipe != INVALID_PIPE) - pipes &= ~(1 << intel_dp->pps_pipe); - } else { - drm_WARN_ON(&dev_priv->drm, - intel_dp->pps_pipe != INVALID_PIPE); - - if (intel_dp->active_pipe != INVALID_PIPE) - pipes &= ~(1 << intel_dp->active_pipe); - } - } - - if (pipes == 0) - return INVALID_PIPE; - - return ffs(pipes) - 1; -} - -static enum pipe -vlv_power_sequencer_pipe(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum pipe pipe; - - lockdep_assert_held(&dev_priv->pps_mutex); - - /* We should never land here with regular DP ports */ - drm_WARN_ON(&dev_priv->drm, !intel_dp_is_edp(intel_dp)); - - drm_WARN_ON(&dev_priv->drm, intel_dp->active_pipe != INVALID_PIPE && - intel_dp->active_pipe != intel_dp->pps_pipe); - - if (intel_dp->pps_pipe != INVALID_PIPE) - return intel_dp->pps_pipe; - - pipe = vlv_find_free_pps(dev_priv); - - /* - * Didn't find one. This should not happen since there - * are two power sequencers and up to two eDP ports. - */ - if (drm_WARN_ON(&dev_priv->drm, pipe == INVALID_PIPE)) - pipe = PIPE_A; - - vlv_steal_power_sequencer(dev_priv, pipe); - intel_dp->pps_pipe = pipe; - - drm_dbg_kms(&dev_priv->drm, - "picked pipe %c power sequencer for [ENCODER:%d:%s]\n", - pipe_name(intel_dp->pps_pipe), - dig_port->base.base.base.id, - dig_port->base.base.name); - - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(intel_dp); - intel_dp_init_panel_power_sequencer_registers(intel_dp, true); - - /* - * Even vdd force doesn't work until we've made - * the power sequencer lock in on the port. - */ - vlv_power_sequencer_kick(intel_dp); - - return intel_dp->pps_pipe; -} - -static int -bxt_power_sequencer_idx(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - int backlight_controller = dev_priv->vbt.backlight.controller; - - lockdep_assert_held(&dev_priv->pps_mutex); - - /* We should never land here with regular DP ports */ - drm_WARN_ON(&dev_priv->drm, !intel_dp_is_edp(intel_dp)); - - if (!intel_dp->pps_reset) - return backlight_controller; - - intel_dp->pps_reset = false; - - /* - * Only the HW needs to be reprogrammed, the SW state is fixed and - * has been setup during connector init. - */ - intel_dp_init_panel_power_sequencer_registers(intel_dp, false); - - return backlight_controller; -} - -typedef bool (*vlv_pipe_check)(struct drm_i915_private *dev_priv, - enum pipe pipe); - -static bool vlv_pipe_has_pp_on(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - return intel_de_read(dev_priv, PP_STATUS(pipe)) & PP_ON; -} - -static bool vlv_pipe_has_vdd_on(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - return intel_de_read(dev_priv, PP_CONTROL(pipe)) & EDP_FORCE_VDD; -} - -static bool vlv_pipe_any(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - return true; -} - -static enum pipe -vlv_initial_pps_pipe(struct drm_i915_private *dev_priv, - enum port port, - vlv_pipe_check pipe_check) -{ - enum pipe pipe; - - for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) { - u32 port_sel = intel_de_read(dev_priv, PP_ON_DELAYS(pipe)) & - PANEL_PORT_SELECT_MASK; - - if (port_sel != PANEL_PORT_SELECT_VLV(port)) - continue; - - if (!pipe_check(dev_priv, pipe)) - continue; - - return pipe; - } - - return INVALID_PIPE; -} - -static void -vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum port port = dig_port->base.port; - - lockdep_assert_held(&dev_priv->pps_mutex); - - /* try to find a pipe with this port selected */ - /* first pick one where the panel is on */ - intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, - vlv_pipe_has_pp_on); - /* didn't find one? pick one where vdd is on */ - if (intel_dp->pps_pipe == INVALID_PIPE) - intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, - vlv_pipe_has_vdd_on); - /* didn't find one? pick one with just the correct port */ - if (intel_dp->pps_pipe == INVALID_PIPE) - intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, - vlv_pipe_any); - - /* didn't find one? just let vlv_power_sequencer_pipe() pick one when needed */ - if (intel_dp->pps_pipe == INVALID_PIPE) { - drm_dbg_kms(&dev_priv->drm, - "no initial power sequencer for [ENCODER:%d:%s]\n", - dig_port->base.base.base.id, - dig_port->base.base.name); - return; - } - - drm_dbg_kms(&dev_priv->drm, - "initial power sequencer for [ENCODER:%d:%s]: pipe %c\n", - dig_port->base.base.base.id, - dig_port->base.base.name, - pipe_name(intel_dp->pps_pipe)); - - intel_dp_init_panel_power_sequencer(intel_dp); - intel_dp_init_panel_power_sequencer_registers(intel_dp, false); -} - -void intel_power_sequencer_reset(struct drm_i915_private *dev_priv) -{ - struct intel_encoder *encoder; - - if (drm_WARN_ON(&dev_priv->drm, - !(IS_VALLEYVIEW(dev_priv) || - IS_CHERRYVIEW(dev_priv) || - IS_GEN9_LP(dev_priv)))) - return; - - /* - * We can't grab pps_mutex here due to deadlock with power_domain - * mutex when power_domain functions are called while holding pps_mutex. - * That also means that in order to use pps_pipe the code needs to - * hold both a power domain reference and pps_mutex, and the power domain - * reference get/put must be done while _not_ holding pps_mutex. - * pps_{lock,unlock}() do these steps in the correct order, so one - * should use them always. - */ - - for_each_intel_dp(&dev_priv->drm, encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - - drm_WARN_ON(&dev_priv->drm, - intel_dp->active_pipe != INVALID_PIPE); - - if (encoder->type != INTEL_OUTPUT_EDP) - continue; - - if (IS_GEN9_LP(dev_priv)) - intel_dp->pps_reset = true; - else - intel_dp->pps_pipe = INVALID_PIPE; - } -} - -struct pps_registers { - i915_reg_t pp_ctrl; - i915_reg_t pp_stat; - i915_reg_t pp_on; - i915_reg_t pp_off; - i915_reg_t pp_div; -}; - -static void intel_pps_get_registers(struct intel_dp *intel_dp, - struct pps_registers *regs) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - int pps_idx = 0; - - memset(regs, 0, sizeof(*regs)); - - if (IS_GEN9_LP(dev_priv)) - pps_idx = bxt_power_sequencer_idx(intel_dp); - else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - pps_idx = vlv_power_sequencer_pipe(intel_dp); - - regs->pp_ctrl = PP_CONTROL(pps_idx); - regs->pp_stat = PP_STATUS(pps_idx); - regs->pp_on = PP_ON_DELAYS(pps_idx); - regs->pp_off = PP_OFF_DELAYS(pps_idx); - - /* Cycle delay moved from PP_DIVISOR to PP_CONTROL */ - if (IS_GEN9_LP(dev_priv) || INTEL_PCH_TYPE(dev_priv) >= PCH_CNP) - regs->pp_div = INVALID_MMIO_REG; - else - regs->pp_div = PP_DIVISOR(pps_idx); -} - -static i915_reg_t -_pp_ctrl_reg(struct intel_dp *intel_dp) -{ - struct pps_registers regs; - - intel_pps_get_registers(intel_dp, ®s); - - return regs.pp_ctrl; -} - -static i915_reg_t -_pp_stat_reg(struct intel_dp *intel_dp) -{ - struct pps_registers regs; - - intel_pps_get_registers(intel_dp, ®s); - - return regs.pp_stat; -} - -static bool edp_have_panel_power(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - - lockdep_assert_held(&dev_priv->pps_mutex); - - if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && - intel_dp->pps_pipe == INVALID_PIPE) - return false; - - return (intel_de_read(dev_priv, _pp_stat_reg(intel_dp)) & PP_ON) != 0; -} - -static bool edp_have_panel_vdd(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - - lockdep_assert_held(&dev_priv->pps_mutex); - - if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && - intel_dp->pps_pipe == INVALID_PIPE) - return false; - - return intel_de_read(dev_priv, _pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD; -} - -static void -intel_dp_check_edp(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - - if (!intel_dp_is_edp(intel_dp)) - return; - - if (!edp_have_panel_power(intel_dp) && !edp_have_panel_vdd(intel_dp)) { - drm_WARN(&dev_priv->drm, 1, - "eDP powered off while attempting aux channel communication.\n"); - drm_dbg_kms(&dev_priv->drm, "Status 0x%08x Control 0x%08x\n", - intel_de_read(dev_priv, _pp_stat_reg(intel_dp)), - intel_de_read(dev_priv, _pp_ctrl_reg(intel_dp))); - } -} - static u32 intel_dp_aux_wait_done(struct intel_dp *intel_dp) { @@ -1501,7 +1060,7 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp, aux_domain = intel_aux_power_domain(dig_port); aux_wakeref = intel_display_power_get(i915, aux_domain); - pps_wakeref = pps_lock(intel_dp); + pps_wakeref = intel_pps_lock(intel_dp); /* * We will be called with VDD already enabled for dpcd/edid/oui reads. @@ -1509,7 +1068,7 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp, * to turn it off. But for eg. i2c-dev access we need to turn it on/off * ourselves. */ - vdd = edp_panel_vdd_on(intel_dp); + vdd = intel_pps_vdd_on_unlocked(intel_dp); /* dp aux is extremely sensitive to irq latency, hence request the * lowest possible wakeup latency and so prevent the cpu from going into @@ -1517,7 +1076,7 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp, */ cpu_latency_qos_update_request(&intel_dp->pm_qos, 0); - intel_dp_check_edp(intel_dp); + intel_pps_check_power_unlocked(intel_dp); /* Try to wait for any previous AUX channel activity */ for (try = 0; try < 3; try++) { @@ -1651,9 +1210,9 @@ out: cpu_latency_qos_update_request(&intel_dp->pm_qos, PM_QOS_DEFAULT_VALUE); if (vdd) - edp_panel_vdd_off(intel_dp, false); + intel_pps_vdd_off_unlocked(intel_dp, false); - pps_unlock(intel_dp, pps_wakeref); + intel_pps_unlock(intel_dp, pps_wakeref); intel_display_power_put_async(i915, aux_domain, aux_wakeref); if (is_tc_port) @@ -3047,431 +2606,6 @@ static void intel_dp_prepare(struct intel_encoder *encoder, } } -#define IDLE_ON_MASK (PP_ON | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) -#define IDLE_ON_VALUE (PP_ON | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE) - -#define IDLE_OFF_MASK (PP_ON | PP_SEQUENCE_MASK | 0 | 0) -#define IDLE_OFF_VALUE (0 | PP_SEQUENCE_NONE | 0 | 0) - -#define IDLE_CYCLE_MASK (PP_ON | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK) -#define IDLE_CYCLE_VALUE (0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE) - -static void intel_pps_verify_state(struct intel_dp *intel_dp); - -static void wait_panel_status(struct intel_dp *intel_dp, - u32 mask, - u32 value) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - i915_reg_t pp_stat_reg, pp_ctrl_reg; - - lockdep_assert_held(&dev_priv->pps_mutex); - - intel_pps_verify_state(intel_dp); - - pp_stat_reg = _pp_stat_reg(intel_dp); - pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - - drm_dbg_kms(&dev_priv->drm, - "mask %08x value %08x status %08x control %08x\n", - mask, value, - intel_de_read(dev_priv, pp_stat_reg), - intel_de_read(dev_priv, pp_ctrl_reg)); - - if (intel_de_wait_for_register(dev_priv, pp_stat_reg, - mask, value, 5000)) - drm_err(&dev_priv->drm, - "Panel status timeout: status %08x control %08x\n", - intel_de_read(dev_priv, pp_stat_reg), - intel_de_read(dev_priv, pp_ctrl_reg)); - - drm_dbg_kms(&dev_priv->drm, "Wait complete\n"); -} - -static void wait_panel_on(struct intel_dp *intel_dp) -{ - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - - drm_dbg_kms(&i915->drm, "Wait for panel power on\n"); - wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE); -} - -static void wait_panel_off(struct intel_dp *intel_dp) -{ - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - - drm_dbg_kms(&i915->drm, "Wait for panel power off time\n"); - wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE); -} - -static void wait_panel_power_cycle(struct intel_dp *intel_dp) -{ - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - ktime_t panel_power_on_time; - s64 panel_power_off_duration; - - drm_dbg_kms(&i915->drm, "Wait for panel power cycle\n"); - - /* take the difference of currrent time and panel power off time - * and then make panel wait for t11_t12 if needed. */ - panel_power_on_time = ktime_get_boottime(); - panel_power_off_duration = ktime_ms_delta(panel_power_on_time, intel_dp->panel_power_off_time); - - /* When we disable the VDD override bit last we have to do the manual - * wait. */ - if (panel_power_off_duration < (s64)intel_dp->panel_power_cycle_delay) - wait_remaining_ms_from_jiffies(jiffies, - intel_dp->panel_power_cycle_delay - panel_power_off_duration); - - wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE); -} - -static void wait_backlight_on(struct intel_dp *intel_dp) -{ - wait_remaining_ms_from_jiffies(intel_dp->last_power_on, - intel_dp->backlight_on_delay); -} - -static void edp_wait_backlight_off(struct intel_dp *intel_dp) -{ - wait_remaining_ms_from_jiffies(intel_dp->last_backlight_off, - intel_dp->backlight_off_delay); -} - -/* Read the current pp_control value, unlocking the register if it - * is locked - */ - -static u32 ilk_get_pp_control(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - u32 control; - - lockdep_assert_held(&dev_priv->pps_mutex); - - control = intel_de_read(dev_priv, _pp_ctrl_reg(intel_dp)); - if (drm_WARN_ON(&dev_priv->drm, !HAS_DDI(dev_priv) && - (control & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS)) { - control &= ~PANEL_UNLOCK_MASK; - control |= PANEL_UNLOCK_REGS; - } - return control; -} - -/* - * Must be paired with edp_panel_vdd_off(). - * Must hold pps_mutex around the whole on/off sequence. - * Can be nested with intel_edp_panel_vdd_{on,off}() calls. - */ -static bool edp_panel_vdd_on(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - u32 pp; - i915_reg_t pp_stat_reg, pp_ctrl_reg; - bool need_to_disable = !intel_dp->want_panel_vdd; - - lockdep_assert_held(&dev_priv->pps_mutex); - - if (!intel_dp_is_edp(intel_dp)) - return false; - - cancel_delayed_work(&intel_dp->panel_vdd_work); - intel_dp->want_panel_vdd = true; - - if (edp_have_panel_vdd(intel_dp)) - return need_to_disable; - - drm_WARN_ON(&dev_priv->drm, intel_dp->vdd_wakeref); - intel_dp->vdd_wakeref = intel_display_power_get(dev_priv, - intel_aux_power_domain(dig_port)); - - drm_dbg_kms(&dev_priv->drm, "Turning [ENCODER:%d:%s] VDD on\n", - dig_port->base.base.base.id, - dig_port->base.base.name); - - if (!edp_have_panel_power(intel_dp)) - wait_panel_power_cycle(intel_dp); - - pp = ilk_get_pp_control(intel_dp); - pp |= EDP_FORCE_VDD; - - pp_stat_reg = _pp_stat_reg(intel_dp); - pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - drm_dbg_kms(&dev_priv->drm, "PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", - intel_de_read(dev_priv, pp_stat_reg), - intel_de_read(dev_priv, pp_ctrl_reg)); - /* - * If the panel wasn't on, delay before accessing aux channel - */ - if (!edp_have_panel_power(intel_dp)) { - drm_dbg_kms(&dev_priv->drm, - "[ENCODER:%d:%s] panel power wasn't enabled\n", - dig_port->base.base.base.id, - dig_port->base.base.name); - msleep(intel_dp->panel_power_up_delay); - } - - return need_to_disable; -} - -/* - * Must be paired with intel_edp_panel_vdd_off() or - * intel_edp_panel_off(). - * Nested calls to these functions are not allowed since - * we drop the lock. Caller must use some higher level - * locking to prevent nested calls from other threads. - */ -void intel_edp_panel_vdd_on(struct intel_dp *intel_dp) -{ - intel_wakeref_t wakeref; - bool vdd; - - if (!intel_dp_is_edp(intel_dp)) - return; - - vdd = false; - with_pps_lock(intel_dp, wakeref) - vdd = edp_panel_vdd_on(intel_dp); - I915_STATE_WARN(!vdd, "[ENCODER:%d:%s] VDD already requested on\n", - dp_to_dig_port(intel_dp)->base.base.base.id, - dp_to_dig_port(intel_dp)->base.base.name); -} - -static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = - dp_to_dig_port(intel_dp); - u32 pp; - i915_reg_t pp_stat_reg, pp_ctrl_reg; - - lockdep_assert_held(&dev_priv->pps_mutex); - - drm_WARN_ON(&dev_priv->drm, intel_dp->want_panel_vdd); - - if (!edp_have_panel_vdd(intel_dp)) - return; - - drm_dbg_kms(&dev_priv->drm, "Turning [ENCODER:%d:%s] VDD off\n", - dig_port->base.base.base.id, - dig_port->base.base.name); - - pp = ilk_get_pp_control(intel_dp); - pp &= ~EDP_FORCE_VDD; - - pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - pp_stat_reg = _pp_stat_reg(intel_dp); - - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - - /* Make sure sequencer is idle before allowing subsequent activity */ - drm_dbg_kms(&dev_priv->drm, "PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", - intel_de_read(dev_priv, pp_stat_reg), - intel_de_read(dev_priv, pp_ctrl_reg)); - - if ((pp & PANEL_POWER_ON) == 0) - intel_dp->panel_power_off_time = ktime_get_boottime(); - - intel_display_power_put(dev_priv, - intel_aux_power_domain(dig_port), - fetch_and_zero(&intel_dp->vdd_wakeref)); -} - -static void edp_panel_vdd_work(struct work_struct *__work) -{ - struct intel_dp *intel_dp = - container_of(to_delayed_work(__work), - struct intel_dp, panel_vdd_work); - intel_wakeref_t wakeref; - - with_pps_lock(intel_dp, wakeref) { - if (!intel_dp->want_panel_vdd) - edp_panel_vdd_off_sync(intel_dp); - } -} - -static void edp_panel_vdd_schedule_off(struct intel_dp *intel_dp) -{ - unsigned long delay; - - /* - * Queue the timer to fire a long time from now (relative to the power - * down delay) to keep the panel power up across a sequence of - * operations. - */ - delay = msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5); - schedule_delayed_work(&intel_dp->panel_vdd_work, delay); -} - -/* - * Must be paired with edp_panel_vdd_on(). - * Must hold pps_mutex around the whole on/off sequence. - * Can be nested with intel_edp_panel_vdd_{on,off}() calls. - */ -static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - - lockdep_assert_held(&dev_priv->pps_mutex); - - if (!intel_dp_is_edp(intel_dp)) - return; - - I915_STATE_WARN(!intel_dp->want_panel_vdd, "[ENCODER:%d:%s] VDD not forced on", - dp_to_dig_port(intel_dp)->base.base.base.id, - dp_to_dig_port(intel_dp)->base.base.name); - - intel_dp->want_panel_vdd = false; - - if (sync) - edp_panel_vdd_off_sync(intel_dp); - else - edp_panel_vdd_schedule_off(intel_dp); -} - -static void edp_panel_on(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - u32 pp; - i915_reg_t pp_ctrl_reg; - - lockdep_assert_held(&dev_priv->pps_mutex); - - if (!intel_dp_is_edp(intel_dp)) - return; - - drm_dbg_kms(&dev_priv->drm, "Turn [ENCODER:%d:%s] panel power on\n", - dp_to_dig_port(intel_dp)->base.base.base.id, - dp_to_dig_port(intel_dp)->base.base.name); - - if (drm_WARN(&dev_priv->drm, edp_have_panel_power(intel_dp), - "[ENCODER:%d:%s] panel power already on\n", - dp_to_dig_port(intel_dp)->base.base.base.id, - dp_to_dig_port(intel_dp)->base.base.name)) - return; - - wait_panel_power_cycle(intel_dp); - - pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - pp = ilk_get_pp_control(intel_dp); - if (IS_GEN(dev_priv, 5)) { - /* ILK workaround: disable reset around power sequence */ - pp &= ~PANEL_POWER_RESET; - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - } - - pp |= PANEL_POWER_ON; - if (!IS_GEN(dev_priv, 5)) - pp |= PANEL_POWER_RESET; - - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - - wait_panel_on(intel_dp); - intel_dp->last_power_on = jiffies; - - if (IS_GEN(dev_priv, 5)) { - pp |= PANEL_POWER_RESET; /* restore panel reset bit */ - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - } -} - -void intel_edp_panel_on(struct intel_dp *intel_dp) -{ - intel_wakeref_t wakeref; - - if (!intel_dp_is_edp(intel_dp)) - return; - - with_pps_lock(intel_dp, wakeref) - edp_panel_on(intel_dp); -} - - -static void edp_panel_off(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - u32 pp; - i915_reg_t pp_ctrl_reg; - - lockdep_assert_held(&dev_priv->pps_mutex); - - if (!intel_dp_is_edp(intel_dp)) - return; - - drm_dbg_kms(&dev_priv->drm, "Turn [ENCODER:%d:%s] panel power off\n", - dig_port->base.base.base.id, dig_port->base.base.name); - - drm_WARN(&dev_priv->drm, !intel_dp->want_panel_vdd, - "Need [ENCODER:%d:%s] VDD to turn off panel\n", - dig_port->base.base.base.id, dig_port->base.base.name); - - pp = ilk_get_pp_control(intel_dp); - /* We need to switch off panel power _and_ force vdd, for otherwise some - * panels get very unhappy and cease to work. */ - pp &= ~(PANEL_POWER_ON | PANEL_POWER_RESET | EDP_FORCE_VDD | - EDP_BLC_ENABLE); - - pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - - intel_dp->want_panel_vdd = false; - - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - - wait_panel_off(intel_dp); - intel_dp->panel_power_off_time = ktime_get_boottime(); - - /* We got a reference when we enabled the VDD. */ - intel_display_power_put(dev_priv, - intel_aux_power_domain(dig_port), - fetch_and_zero(&intel_dp->vdd_wakeref)); -} - -void intel_edp_panel_off(struct intel_dp *intel_dp) -{ - intel_wakeref_t wakeref; - - if (!intel_dp_is_edp(intel_dp)) - return; - - with_pps_lock(intel_dp, wakeref) - edp_panel_off(intel_dp); -} - -/* Enable backlight in the panel power control. */ -static void _intel_edp_backlight_on(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - intel_wakeref_t wakeref; - - /* - * If we enable the backlight right away following a panel power - * on, we may see slight flicker as the panel syncs with the eDP - * link. So delay a bit to make sure the image is solid before - * allowing it to appear. - */ - wait_backlight_on(intel_dp); - - with_pps_lock(intel_dp, wakeref) { - i915_reg_t pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - u32 pp; - - pp = ilk_get_pp_control(intel_dp); - pp |= EDP_BLC_ENABLE; - - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - } -} /* Enable backlight PWM and backlight PP control. */ void intel_edp_backlight_on(const struct intel_crtc_state *crtc_state, @@ -3486,31 +2620,7 @@ void intel_edp_backlight_on(const struct intel_crtc_state *crtc_state, drm_dbg_kms(&i915->drm, "\n"); intel_panel_enable_backlight(crtc_state, conn_state); - _intel_edp_backlight_on(intel_dp); -} - -/* Disable backlight in the panel power control. */ -static void _intel_edp_backlight_off(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - intel_wakeref_t wakeref; - - if (!intel_dp_is_edp(intel_dp)) - return; - - with_pps_lock(intel_dp, wakeref) { - i915_reg_t pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - u32 pp; - - pp = ilk_get_pp_control(intel_dp); - pp &= ~EDP_BLC_ENABLE; - - intel_de_write(dev_priv, pp_ctrl_reg, pp); - intel_de_posting_read(dev_priv, pp_ctrl_reg); - } - - intel_dp->last_backlight_off = jiffies; - edp_wait_backlight_off(intel_dp); + intel_pps_backlight_on(intel_dp); } /* Disable backlight PP control and backlight PWM. */ @@ -3524,37 +2634,10 @@ void intel_edp_backlight_off(const struct drm_connector_state *old_conn_state) drm_dbg_kms(&i915->drm, "\n"); - _intel_edp_backlight_off(intel_dp); + intel_pps_backlight_off(intel_dp); intel_panel_disable_backlight(old_conn_state); } -/* - * Hook for controlling the panel power control backlight through the bl_power - * sysfs attribute. Take care to handle multiple calls. - */ -static void intel_edp_backlight_power(struct intel_connector *connector, - bool enable) -{ - struct drm_i915_private *i915 = to_i915(connector->base.dev); - struct intel_dp *intel_dp = intel_attached_dp(connector); - intel_wakeref_t wakeref; - bool is_enabled; - - is_enabled = false; - with_pps_lock(intel_dp, wakeref) - is_enabled = ilk_get_pp_control(intel_dp) & EDP_BLC_ENABLE; - if (is_enabled == enable) - return; - - drm_dbg_kms(&i915->drm, "panel power control backlight %s\n", - enable ? "enable" : "disable"); - - if (enable) - _intel_edp_backlight_on(intel_dp); - else - _intel_edp_backlight_off(intel_dp); -} - static void assert_dp_port(struct intel_dp *intel_dp, bool state) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); @@ -3975,10 +3058,10 @@ static void intel_disable_dp(struct intel_atomic_state *state, /* Make sure the panel is off before trying to change the mode. But also * ensure that we have vdd while we switch off the panel. */ - intel_edp_panel_vdd_on(intel_dp); + intel_pps_vdd_on(intel_dp); intel_edp_backlight_off(old_conn_state); intel_dp_set_power(intel_dp, DP_SET_POWER_D3); - intel_edp_panel_off(intel_dp); + intel_pps_off(intel_dp); intel_dp->frl.is_trained = false; intel_dp->frl.trained_rate_gbps = 0; } @@ -4425,8 +3508,8 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, drm_dbg_kms(&i915->drm, "Failed to set protocol converter HDMI mode to %s\n", enableddisabled(intel_dp->has_hdmi_sink)); - tmp = intel_dp->dfp.ycbcr_444_to_420 ? - DP_CONVERSION_TO_YCBCR420_ENABLE : 0; + tmp = crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 && + intel_dp->dfp.ycbcr_444_to_420 ? DP_CONVERSION_TO_YCBCR420_ENABLE : 0; if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_PROTOCOL_CONVERTER_CONTROL_1, tmp) != 1) @@ -4488,15 +3571,15 @@ static void intel_enable_dp(struct intel_atomic_state *state, if (drm_WARN_ON(&dev_priv->drm, dp_reg & DP_PORT_EN)) return; - with_pps_lock(intel_dp, wakeref) { + with_intel_pps_lock(intel_dp, wakeref) { if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - vlv_init_panel_power_sequencer(encoder, pipe_config); + vlv_pps_init(encoder, pipe_config); intel_dp_enable_port(intel_dp, pipe_config); - edp_panel_vdd_on(intel_dp); - edp_panel_on(intel_dp); - edp_panel_vdd_off(intel_dp, true); + intel_pps_vdd_on_unlocked(intel_dp); + intel_pps_on_unlocked(intel_dp); + intel_pps_vdd_off_unlocked(intel_dp, true); } if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { @@ -4555,112 +3638,6 @@ static void g4x_pre_enable_dp(struct intel_atomic_state *state, ilk_edp_pll_on(intel_dp, pipe_config); } -static void vlv_detach_power_sequencer(struct intel_dp *intel_dp) -{ - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev); - enum pipe pipe = intel_dp->pps_pipe; - i915_reg_t pp_on_reg = PP_ON_DELAYS(pipe); - - drm_WARN_ON(&dev_priv->drm, intel_dp->active_pipe != INVALID_PIPE); - - if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B)) - return; - - edp_panel_vdd_off_sync(intel_dp); - - /* - * VLV seems to get confused when multiple power sequencers - * have the same port selected (even if only one has power/vdd - * enabled). The failure manifests as vlv_wait_port_ready() failing - * CHV on the other hand doesn't seem to mind having the same port - * selected in multiple power sequencers, but let's clear the - * port select always when logically disconnecting a power sequencer - * from a port. - */ - drm_dbg_kms(&dev_priv->drm, - "detaching pipe %c power sequencer from [ENCODER:%d:%s]\n", - pipe_name(pipe), dig_port->base.base.base.id, - dig_port->base.base.name); - intel_de_write(dev_priv, pp_on_reg, 0); - intel_de_posting_read(dev_priv, pp_on_reg); - - intel_dp->pps_pipe = INVALID_PIPE; -} - -static void vlv_steal_power_sequencer(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - struct intel_encoder *encoder; - - lockdep_assert_held(&dev_priv->pps_mutex); - - for_each_intel_dp(&dev_priv->drm, encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - - drm_WARN(&dev_priv->drm, intel_dp->active_pipe == pipe, - "stealing pipe %c power sequencer from active [ENCODER:%d:%s]\n", - pipe_name(pipe), encoder->base.base.id, - encoder->base.name); - - if (intel_dp->pps_pipe != pipe) - continue; - - drm_dbg_kms(&dev_priv->drm, - "stealing pipe %c power sequencer from [ENCODER:%d:%s]\n", - pipe_name(pipe), encoder->base.base.id, - encoder->base.name); - - /* make sure vdd is off before we steal it */ - vlv_detach_power_sequencer(intel_dp); - } -} - -static void vlv_init_panel_power_sequencer(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - - lockdep_assert_held(&dev_priv->pps_mutex); - - drm_WARN_ON(&dev_priv->drm, intel_dp->active_pipe != INVALID_PIPE); - - if (intel_dp->pps_pipe != INVALID_PIPE && - intel_dp->pps_pipe != crtc->pipe) { - /* - * If another power sequencer was being used on this - * port previously make sure to turn off vdd there while - * we still have control of it. - */ - vlv_detach_power_sequencer(intel_dp); - } - - /* - * We may be stealing the power - * sequencer from another port. - */ - vlv_steal_power_sequencer(dev_priv, crtc->pipe); - - intel_dp->active_pipe = crtc->pipe; - - if (!intel_dp_is_edp(intel_dp)) - return; - - /* now it's all ours */ - intel_dp->pps_pipe = crtc->pipe; - - drm_dbg_kms(&dev_priv->drm, - "initializing pipe %c power sequencer for [ENCODER:%d:%s]\n", - pipe_name(intel_dp->pps_pipe), encoder->base.base.id, - encoder->base.name); - - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(intel_dp); - intel_dp_init_panel_power_sequencer_registers(intel_dp, true); -} - static void vlv_pre_enable_dp(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *pipe_config, @@ -5060,24 +4037,6 @@ ivb_cpu_edp_set_signal_levels(struct intel_dp *intel_dp, intel_de_posting_read(dev_priv, intel_dp->output_reg); } -void intel_dp_set_signal_levels(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - u8 train_set = intel_dp->train_set[0]; - - drm_dbg_kms(&dev_priv->drm, "Using vswing level %d%s\n", - train_set & DP_TRAIN_VOLTAGE_SWING_MASK, - train_set & DP_TRAIN_MAX_SWING_REACHED ? " (max)" : ""); - drm_dbg_kms(&dev_priv->drm, "Using pre-emphasis level %d%s\n", - (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) >> - DP_TRAIN_PRE_EMPHASIS_SHIFT, - train_set & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED ? - " (max)" : ""); - - intel_dp->set_signal_levels(intel_dp, crtc_state); -} - void intel_dp_program_link_training_pattern(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, @@ -5162,7 +4121,7 @@ intel_dp_link_down(struct intel_encoder *encoder, if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { intel_wakeref_t wakeref; - with_pps_lock(intel_dp, wakeref) + with_intel_pps_lock(intel_dp, wakeref) intel_dp->active_pipe = INVALID_PIPE; } } @@ -6132,7 +5091,7 @@ static void intel_dp_process_phy_request(struct intel_dp *intel_dp, intel_dp_autotest_phy_ddi_disable(intel_dp, crtc_state); - intel_dp_set_signal_levels(intel_dp, crtc_state); + intel_dp_set_signal_levels(intel_dp, crtc_state, DP_PHY_DPRX); intel_dp_phy_pattern_update(intel_dp, crtc_state); @@ -6205,6 +5164,17 @@ update_status: "Could not write test response to sink\n"); } +static void +intel_dp_mst_hpd_irq(struct intel_dp *intel_dp, u8 *esi, bool *handled) +{ + drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, handled); + + if (esi[1] & DP_CP_IRQ) { + intel_hdcp_handle_cp_irq(intel_dp->attached_connector); + *handled = true; + } +} + /** * intel_dp_check_mst_status - service any pending MST interrupts, check link status * @intel_dp: Intel DP struct @@ -6249,7 +5219,8 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) drm_dbg_kms(&i915->drm, "got esi %3ph\n", esi); - drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, &handled); + intel_dp_mst_hpd_irq(intel_dp, esi, &handled); + if (!handled) break; @@ -7008,8 +5979,8 @@ intel_dp_update_420(struct intel_dp *intel_dp) intel_dp->downstream_ports); rgb_to_ycbcr = drm_dp_downstream_rgb_to_ycbcr_conversion(intel_dp->dpcd, intel_dp->downstream_ports, - DP_DS_HDMI_BT601_RGB_YCBCR_CONV || - DP_DS_HDMI_BT709_RGB_YCBCR_CONV || + DP_DS_HDMI_BT601_RGB_YCBCR_CONV | + DP_DS_HDMI_BT709_RGB_YCBCR_CONV | DP_DS_HDMI_BT2020_RGB_YCBCR_CONV); if (INTEL_GEN(i915) >= 11) { @@ -7329,17 +6300,8 @@ void intel_dp_encoder_flush_work(struct drm_encoder *encoder) struct intel_dp *intel_dp = &dig_port->dp; intel_dp_mst_encoder_cleanup(dig_port); - if (intel_dp_is_edp(intel_dp)) { - intel_wakeref_t wakeref; - cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - /* - * vdd might still be enabled do to the delayed vdd off. - * Make sure vdd is actually turned off here. - */ - with_pps_lock(intel_dp, wakeref) - edp_panel_vdd_off_sync(intel_dp); - } + intel_pps_vdd_off_sync(intel_dp); intel_dp_aux_fini(intel_dp); } @@ -7355,55 +6317,15 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder) void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(intel_encoder); - intel_wakeref_t wakeref; - if (!intel_dp_is_edp(intel_dp)) - return; - - /* - * vdd might still be enabled do to the delayed vdd off. - * Make sure vdd is actually turned off here. - */ - cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - with_pps_lock(intel_dp, wakeref) - edp_panel_vdd_off_sync(intel_dp); + intel_pps_vdd_off_sync(intel_dp); } void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(intel_encoder); - intel_wakeref_t wakeref; - - if (!intel_dp_is_edp(intel_dp)) - return; - - with_pps_lock(intel_dp, wakeref) - wait_panel_power_cycle(intel_dp); -} - -static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - - lockdep_assert_held(&dev_priv->pps_mutex); - - if (!edp_have_panel_vdd(intel_dp)) - return; - - /* - * The VDD bit needs a power domain reference, so if the bit is - * already enabled when we boot or resume, grab this reference and - * schedule a vdd off, so we don't hold on to the reference - * indefinitely. - */ - drm_dbg_kms(&dev_priv->drm, - "VDD left on by BIOS, adjusting state tracking\n"); - drm_WARN_ON(&dev_priv->drm, intel_dp->vdd_wakeref); - intel_dp->vdd_wakeref = intel_display_power_get(dev_priv, - intel_aux_power_domain(dig_port)); - edp_panel_vdd_schedule_off(intel_dp); + intel_pps_wait_power_cycle(intel_dp); } static enum pipe vlv_active_pipe(struct intel_dp *intel_dp) @@ -7423,30 +6345,20 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); struct intel_dp *intel_dp = enc_to_intel_dp(to_intel_encoder(encoder)); - intel_wakeref_t wakeref; if (!HAS_DDI(dev_priv)) intel_dp->DP = intel_de_read(dev_priv, intel_dp->output_reg); intel_dp->reset_link_params = true; - if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv) && - !intel_dp_is_edp(intel_dp)) - return; + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + intel_wakeref_t wakeref; - with_pps_lock(intel_dp, wakeref) { - if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + with_intel_pps_lock(intel_dp, wakeref) intel_dp->active_pipe = vlv_active_pipe(intel_dp); - - if (intel_dp_is_edp(intel_dp)) { - /* - * Reinit the power sequencer, in case BIOS did - * something nasty with it. - */ - intel_dp_pps_init(intel_dp); - intel_edp_panel_vdd_sanitize(intel_dp); - } } + + intel_pps_encoder_reset(intel_dp); } static int intel_modeset_tile_group(struct intel_atomic_state *state, @@ -7611,19 +6523,6 @@ static const struct drm_encoder_funcs intel_dp_enc_funcs = { .destroy = intel_dp_encoder_destroy, }; -static bool intel_edp_have_power(struct intel_dp *intel_dp) -{ - intel_wakeref_t wakeref; - bool have_power = false; - - with_pps_lock(intel_dp, wakeref) { - have_power = edp_have_panel_power(intel_dp) && - edp_have_panel_vdd(intel_dp); - } - - return have_power; -} - enum irqreturn intel_dp_hpd_pulse(struct intel_digital_port *dig_port, bool long_hpd) { @@ -7631,7 +6530,7 @@ intel_dp_hpd_pulse(struct intel_digital_port *dig_port, bool long_hpd) struct intel_dp *intel_dp = &dig_port->dp; if (dig_port->base.type == INTEL_OUTPUT_EDP && - (long_hpd || !intel_edp_have_power(intel_dp))) { + (long_hpd || !intel_pps_have_power(intel_dp))) { /* * vdd off can generate a long/short pulse on eDP which * would require vdd on to handle it, and thus we @@ -7727,277 +6626,6 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect } } -static void intel_dp_init_panel_power_timestamps(struct intel_dp *intel_dp) -{ - intel_dp->panel_power_off_time = ktime_get_boottime(); - intel_dp->last_power_on = jiffies; - intel_dp->last_backlight_off = jiffies; -} - -static void -intel_pps_readout_hw_state(struct intel_dp *intel_dp, struct edp_power_seq *seq) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - u32 pp_on, pp_off, pp_ctl; - struct pps_registers regs; - - intel_pps_get_registers(intel_dp, ®s); - - pp_ctl = ilk_get_pp_control(intel_dp); - - /* Ensure PPS is unlocked */ - if (!HAS_DDI(dev_priv)) - intel_de_write(dev_priv, regs.pp_ctrl, pp_ctl); - - pp_on = intel_de_read(dev_priv, regs.pp_on); - pp_off = intel_de_read(dev_priv, regs.pp_off); - - /* Pull timing values out of registers */ - seq->t1_t3 = REG_FIELD_GET(PANEL_POWER_UP_DELAY_MASK, pp_on); - seq->t8 = REG_FIELD_GET(PANEL_LIGHT_ON_DELAY_MASK, pp_on); - seq->t9 = REG_FIELD_GET(PANEL_LIGHT_OFF_DELAY_MASK, pp_off); - seq->t10 = REG_FIELD_GET(PANEL_POWER_DOWN_DELAY_MASK, pp_off); - - if (i915_mmio_reg_valid(regs.pp_div)) { - u32 pp_div; - - pp_div = intel_de_read(dev_priv, regs.pp_div); - - seq->t11_t12 = REG_FIELD_GET(PANEL_POWER_CYCLE_DELAY_MASK, pp_div) * 1000; - } else { - seq->t11_t12 = REG_FIELD_GET(BXT_POWER_CYCLE_DELAY_MASK, pp_ctl) * 1000; - } -} - -static void -intel_pps_dump_state(const char *state_name, const struct edp_power_seq *seq) -{ - DRM_DEBUG_KMS("%s t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", - state_name, - seq->t1_t3, seq->t8, seq->t9, seq->t10, seq->t11_t12); -} - -static void -intel_pps_verify_state(struct intel_dp *intel_dp) -{ - struct edp_power_seq hw; - struct edp_power_seq *sw = &intel_dp->pps_delays; - - intel_pps_readout_hw_state(intel_dp, &hw); - - if (hw.t1_t3 != sw->t1_t3 || hw.t8 != sw->t8 || hw.t9 != sw->t9 || - hw.t10 != sw->t10 || hw.t11_t12 != sw->t11_t12) { - DRM_ERROR("PPS state mismatch\n"); - intel_pps_dump_state("sw", sw); - intel_pps_dump_state("hw", &hw); - } -} - -static void -intel_dp_init_panel_power_sequencer(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct edp_power_seq cur, vbt, spec, - *final = &intel_dp->pps_delays; - - lockdep_assert_held(&dev_priv->pps_mutex); - - /* already initialized? */ - if (final->t11_t12 != 0) - return; - - intel_pps_readout_hw_state(intel_dp, &cur); - - intel_pps_dump_state("cur", &cur); - - vbt = dev_priv->vbt.edp.pps; - /* On Toshiba Satellite P50-C-18C system the VBT T12 delay - * of 500ms appears to be too short. Ocassionally the panel - * just fails to power back on. Increasing the delay to 800ms - * seems sufficient to avoid this problem. - */ - if (dev_priv->quirks & QUIRK_INCREASE_T12_DELAY) { - vbt.t11_t12 = max_t(u16, vbt.t11_t12, 1300 * 10); - drm_dbg_kms(&dev_priv->drm, - "Increasing T12 panel delay as per the quirk to %d\n", - vbt.t11_t12); - } - /* T11_T12 delay is special and actually in units of 100ms, but zero - * based in the hw (so we need to add 100 ms). But the sw vbt - * table multiplies it with 1000 to make it in units of 100usec, - * too. */ - vbt.t11_t12 += 100 * 10; - - /* Upper limits from eDP 1.3 spec. Note that we use the clunky units of - * our hw here, which are all in 100usec. */ - spec.t1_t3 = 210 * 10; - spec.t8 = 50 * 10; /* no limit for t8, use t7 instead */ - spec.t9 = 50 * 10; /* no limit for t9, make it symmetric with t8 */ - spec.t10 = 500 * 10; - /* This one is special and actually in units of 100ms, but zero - * based in the hw (so we need to add 100 ms). But the sw vbt - * table multiplies it with 1000 to make it in units of 100usec, - * too. */ - spec.t11_t12 = (510 + 100) * 10; - - intel_pps_dump_state("vbt", &vbt); - - /* Use the max of the register settings and vbt. If both are - * unset, fall back to the spec limits. */ -#define assign_final(field) final->field = (max(cur.field, vbt.field) == 0 ? \ - spec.field : \ - max(cur.field, vbt.field)) - assign_final(t1_t3); - assign_final(t8); - assign_final(t9); - assign_final(t10); - assign_final(t11_t12); -#undef assign_final - -#define get_delay(field) (DIV_ROUND_UP(final->field, 10)) - intel_dp->panel_power_up_delay = get_delay(t1_t3); - intel_dp->backlight_on_delay = get_delay(t8); - intel_dp->backlight_off_delay = get_delay(t9); - intel_dp->panel_power_down_delay = get_delay(t10); - intel_dp->panel_power_cycle_delay = get_delay(t11_t12); -#undef get_delay - - drm_dbg_kms(&dev_priv->drm, - "panel power up delay %d, power down delay %d, power cycle delay %d\n", - intel_dp->panel_power_up_delay, - intel_dp->panel_power_down_delay, - intel_dp->panel_power_cycle_delay); - - drm_dbg_kms(&dev_priv->drm, "backlight on delay %d, off delay %d\n", - intel_dp->backlight_on_delay, - intel_dp->backlight_off_delay); - - /* - * We override the HW backlight delays to 1 because we do manual waits - * on them. For T8, even BSpec recommends doing it. For T9, if we - * don't do this, we'll end up waiting for the backlight off delay - * twice: once when we do the manual sleep, and once when we disable - * the panel and wait for the PP_STATUS bit to become zero. - */ - final->t8 = 1; - final->t9 = 1; - - /* - * HW has only a 100msec granularity for t11_t12 so round it up - * accordingly. - */ - final->t11_t12 = roundup(final->t11_t12, 100 * 10); -} - -static void -intel_dp_init_panel_power_sequencer_registers(struct intel_dp *intel_dp, - bool force_disable_vdd) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - u32 pp_on, pp_off, port_sel = 0; - int div = RUNTIME_INFO(dev_priv)->rawclk_freq / 1000; - struct pps_registers regs; - enum port port = dp_to_dig_port(intel_dp)->base.port; - const struct edp_power_seq *seq = &intel_dp->pps_delays; - - lockdep_assert_held(&dev_priv->pps_mutex); - - intel_pps_get_registers(intel_dp, ®s); - - /* - * On some VLV machines the BIOS can leave the VDD - * enabled even on power sequencers which aren't - * hooked up to any port. This would mess up the - * power domain tracking the first time we pick - * one of these power sequencers for use since - * edp_panel_vdd_on() would notice that the VDD was - * already on and therefore wouldn't grab the power - * domain reference. Disable VDD first to avoid this. - * This also avoids spuriously turning the VDD on as - * soon as the new power sequencer gets initialized. - */ - if (force_disable_vdd) { - u32 pp = ilk_get_pp_control(intel_dp); - - drm_WARN(&dev_priv->drm, pp & PANEL_POWER_ON, - "Panel power already on\n"); - - if (pp & EDP_FORCE_VDD) - drm_dbg_kms(&dev_priv->drm, - "VDD already on, disabling first\n"); - - pp &= ~EDP_FORCE_VDD; - - intel_de_write(dev_priv, regs.pp_ctrl, pp); - } - - pp_on = REG_FIELD_PREP(PANEL_POWER_UP_DELAY_MASK, seq->t1_t3) | - REG_FIELD_PREP(PANEL_LIGHT_ON_DELAY_MASK, seq->t8); - pp_off = REG_FIELD_PREP(PANEL_LIGHT_OFF_DELAY_MASK, seq->t9) | - REG_FIELD_PREP(PANEL_POWER_DOWN_DELAY_MASK, seq->t10); - - /* Haswell doesn't have any port selection bits for the panel - * power sequencer any more. */ - if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { - port_sel = PANEL_PORT_SELECT_VLV(port); - } else if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv)) { - switch (port) { - case PORT_A: - port_sel = PANEL_PORT_SELECT_DPA; - break; - case PORT_C: - port_sel = PANEL_PORT_SELECT_DPC; - break; - case PORT_D: - port_sel = PANEL_PORT_SELECT_DPD; - break; - default: - MISSING_CASE(port); - break; - } - } - - pp_on |= port_sel; - - intel_de_write(dev_priv, regs.pp_on, pp_on); - intel_de_write(dev_priv, regs.pp_off, pp_off); - - /* - * Compute the divisor for the pp clock, simply match the Bspec formula. - */ - if (i915_mmio_reg_valid(regs.pp_div)) { - intel_de_write(dev_priv, regs.pp_div, - REG_FIELD_PREP(PP_REFERENCE_DIVIDER_MASK, (100 * div) / 2 - 1) | REG_FIELD_PREP(PANEL_POWER_CYCLE_DELAY_MASK, DIV_ROUND_UP(seq->t11_t12, 1000))); - } else { - u32 pp_ctl; - - pp_ctl = intel_de_read(dev_priv, regs.pp_ctrl); - pp_ctl &= ~BXT_POWER_CYCLE_DELAY_MASK; - pp_ctl |= REG_FIELD_PREP(BXT_POWER_CYCLE_DELAY_MASK, DIV_ROUND_UP(seq->t11_t12, 1000)); - intel_de_write(dev_priv, regs.pp_ctrl, pp_ctl); - } - - drm_dbg_kms(&dev_priv->drm, - "panel power sequencer register settings: PP_ON %#x, PP_OFF %#x, PP_DIV %#x\n", - intel_de_read(dev_priv, regs.pp_on), - intel_de_read(dev_priv, regs.pp_off), - i915_mmio_reg_valid(regs.pp_div) ? - intel_de_read(dev_priv, regs.pp_div) : - (intel_de_read(dev_priv, regs.pp_ctrl) & BXT_POWER_CYCLE_DELAY_MASK)); -} - -static void intel_dp_pps_init(struct intel_dp *intel_dp) -{ - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - - if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { - vlv_initial_power_sequencer_setup(intel_dp); - } else { - intel_dp_init_panel_power_sequencer(intel_dp); - intel_dp_init_panel_power_sequencer_registers(intel_dp, false); - } -} - /** * intel_dp_set_drrs_state - program registers for RR switch to take effect * @dev_priv: i915 device @@ -8434,14 +7062,11 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, struct drm_display_mode *downclock_mode = NULL; bool has_dpcd; enum pipe pipe = INVALID_PIPE; - intel_wakeref_t wakeref; struct edid *edid; if (!intel_dp_is_edp(intel_dp)) return true; - INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, edp_panel_vdd_work); - /* * On IBX/CPT we may get here with LVDS already registered. Since the * driver uses the only internal power sequencer available for both @@ -8457,11 +7082,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, return false; } - with_pps_lock(intel_dp, wakeref) { - intel_dp_init_panel_power_timestamps(intel_dp); - intel_dp_pps_init(intel_dp); - intel_edp_panel_vdd_sanitize(intel_dp); - } + intel_pps_init(intel_dp); /* Cache DPCD and EDID for edp. */ has_dpcd = intel_edp_init_dpcd(intel_dp); @@ -8517,7 +7138,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); - intel_connector->panel.backlight.power = intel_edp_backlight_power; + intel_connector->panel.backlight.power = intel_pps_backlight_power; intel_panel_setup_backlight(connector, pipe); if (fixed_mode) { @@ -8529,13 +7150,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, return true; out_vdd_off: - cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - /* - * vdd might still be enabled do to the delayed vdd off. - * Make sure vdd is actually turned off here. - */ - with_pps_lock(intel_dp, wakeref) - edp_panel_vdd_off_sync(intel_dp); + intel_pps_vdd_off_sync(intel_dp); return false; } diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 4280a09fd8fd..abf834729309 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -70,15 +70,11 @@ enum irqreturn intel_dp_hpd_pulse(struct intel_digital_port *dig_port, void intel_edp_backlight_on(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); void intel_edp_backlight_off(const struct drm_connector_state *conn_state); -void intel_edp_panel_vdd_on(struct intel_dp *intel_dp); -void intel_edp_panel_on(struct intel_dp *intel_dp); -void intel_edp_panel_off(struct intel_dp *intel_dp); void intel_dp_mst_suspend(struct drm_i915_private *dev_priv); void intel_dp_mst_resume(struct drm_i915_private *dev_priv); int intel_dp_max_link_rate(struct intel_dp *intel_dp); int intel_dp_max_lane_count(struct intel_dp *intel_dp); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); -void intel_power_sequencer_reset(struct drm_i915_private *dev_priv); u32 intel_dp_pack_aux(const u8 *src, int src_bytes); void intel_edp_drrs_enable(struct intel_dp *intel_dp, @@ -96,9 +92,6 @@ void intel_dp_program_link_training_pattern(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, u8 dp_train_pat); -void -intel_dp_set_signal_levels(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state); void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, u8 *link_bw, u8 *rate_select); bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp); @@ -144,6 +137,7 @@ bool intel_dp_initial_fastset_check(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state); void intel_dp_sync_state(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); +const struct dpll *vlv_get_dpll(struct drm_i915_private *i915); void intel_dp_check_frl_training(struct intel_dp *intel_dp); void intel_dp_pcon_dsc_configure(struct intel_dp *intel_dp, diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index 9775f33d1aac..de764dae1e66 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -128,7 +128,7 @@ static bool intel_dp_aux_vesa_backlight_dpcd_mode(struct intel_connector *connec * Read the current backlight value from DPCD register(s) based * on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported */ -static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector) +static u32 intel_dp_aux_vesa_get_backlight(struct intel_connector *connector, enum pipe unused) { struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_i915_private *i915 = dp_to_i915(intel_dp); @@ -381,7 +381,7 @@ static int intel_dp_aux_vesa_setup_backlight(struct intel_connector *connector, return -ENODEV; panel->backlight.min = 0; - panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector); + panel->backlight.level = intel_dp_aux_vesa_get_backlight(connector, pipe); panel->backlight.enabled = intel_dp_aux_vesa_backlight_dpcd_mode(connector) && panel->backlight.level != 0; diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 03424d20e9f7..f372e25edab4 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -16,6 +16,30 @@ #include "intel_dp.h" #include "intel_hdcp.h" +static unsigned int transcoder_to_stream_enc_status(enum transcoder cpu_transcoder) +{ + u32 stream_enc_mask; + + switch (cpu_transcoder) { + case TRANSCODER_A: + stream_enc_mask = HDCP_STATUS_STREAM_A_ENC; + break; + case TRANSCODER_B: + stream_enc_mask = HDCP_STATUS_STREAM_B_ENC; + break; + case TRANSCODER_C: + stream_enc_mask = HDCP_STATUS_STREAM_C_ENC; + break; + case TRANSCODER_D: + stream_enc_mask = HDCP_STATUS_STREAM_D_ENC; + break; + default: + stream_enc_mask = 0; + } + + return stream_enc_mask; +} + static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int timeout) { long ret; @@ -561,7 +585,8 @@ int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *dig_port, } static -int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port) +int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port, + struct intel_connector *connector) { u8 rx_status; int ret; @@ -622,48 +647,153 @@ static const struct intel_hdcp_shim intel_dp_hdcp_shim = { }; static int -intel_dp_mst_hdcp_toggle_signalling(struct intel_digital_port *dig_port, - enum transcoder cpu_transcoder, - bool enable) +intel_dp_mst_toggle_hdcp_stream_select(struct intel_connector *connector, + bool enable) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct intel_hdcp *hdcp = &connector->hdcp; int ret; - if (!enable) - usleep_range(6, 60); /* Bspec says >= 6us */ - - ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, - cpu_transcoder, enable); + ret = intel_ddi_toggle_hdcp_bits(&dig_port->base, + hdcp->stream_transcoder, enable, + TRANS_DDI_HDCP_SELECT); if (ret) - drm_dbg_kms(&i915->drm, "%s HDCP signalling failed (%d)\n", - enable ? "Enable" : "Disable", ret); + drm_err(&i915->drm, "%s HDCP stream select failed (%d)\n", + enable ? "Enable" : "Disable", ret); return ret; } -static -bool intel_dp_mst_hdcp_check_link(struct intel_digital_port *dig_port, - struct intel_connector *connector) +static int +intel_dp_mst_hdcp_stream_encryption(struct intel_connector *connector, + bool enable) +{ + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct intel_hdcp *hdcp = &connector->hdcp; + enum port port = dig_port->base.port; + enum transcoder cpu_transcoder = hdcp->stream_transcoder; + u32 stream_enc_status; + int ret; + + ret = intel_dp_mst_toggle_hdcp_stream_select(connector, enable); + if (ret) + return ret; + + stream_enc_status = transcoder_to_stream_enc_status(cpu_transcoder); + if (!stream_enc_status) + return -EINVAL; + + /* Wait for encryption confirmation */ + if (intel_de_wait_for_register(i915, + HDCP_STATUS(i915, cpu_transcoder, port), + stream_enc_status, + enable ? stream_enc_status : 0, + HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { + drm_err(&i915->drm, "Timed out waiting for transcoder: %s stream encryption %s\n", + transcoder_name(cpu_transcoder), enable ? "enabled" : "disabled"); + return -ETIMEDOUT; + } + + return 0; +} + +static bool intel_dp_mst_get_qses_status(struct intel_digital_port *dig_port, + struct intel_connector *connector) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - struct intel_dp *intel_dp = &dig_port->dp; struct drm_dp_query_stream_enc_status_ack_reply reply; + struct intel_dp *intel_dp = &dig_port->dp; int ret; - if (!intel_dp_hdcp_check_link(dig_port, connector)) - return false; - ret = drm_dp_send_query_stream_enc_status(&intel_dp->mst_mgr, connector->port, &reply); if (ret) { drm_dbg_kms(&i915->drm, - "[CONNECTOR:%d:%s] failed QSES ret=%d\n", - connector->base.base.id, connector->base.name, ret); + "[%s:%d] failed QSES ret=%d\n", + connector->base.name, connector->base.base.id, ret); return false; } + drm_dbg_kms(&i915->drm, "[%s:%d] QSES stream auth: %d stream enc: %d\n", + connector->base.name, connector->base.base.id, + reply.auth_completed, reply.encryption_enabled); + return reply.auth_completed && reply.encryption_enabled; } +static +bool intel_dp_mst_hdcp_check_link(struct intel_digital_port *dig_port, + struct intel_connector *connector) +{ + if (!intel_dp_hdcp_check_link(dig_port, connector)) + return false; + + return intel_dp_mst_get_qses_status(dig_port, connector); +} + +static int +intel_dp_mst_hdcp2_stream_encryption(struct intel_connector *connector, + bool enable) +{ + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct hdcp_port_data *data = &dig_port->hdcp_port_data; + struct intel_hdcp *hdcp = &connector->hdcp; + enum transcoder cpu_transcoder = hdcp->stream_transcoder; + enum pipe pipe = (enum pipe)cpu_transcoder; + enum port port = dig_port->base.port; + int ret; + + drm_WARN_ON(&i915->drm, enable && + !!(intel_de_read(i915, HDCP2_AUTH_STREAM(i915, cpu_transcoder, port)) + & AUTH_STREAM_TYPE) != data->streams[0].stream_type); + + ret = intel_dp_mst_toggle_hdcp_stream_select(connector, enable); + if (ret) + return ret; + + /* Wait for encryption confirmation */ + if (intel_de_wait_for_register(i915, + HDCP2_STREAM_STATUS(i915, cpu_transcoder, pipe), + STREAM_ENCRYPTION_STATUS, + enable ? STREAM_ENCRYPTION_STATUS : 0, + HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { + drm_err(&i915->drm, "Timed out waiting for transcoder: %s stream encryption %s\n", + transcoder_name(cpu_transcoder), enable ? "enabled" : "disabled"); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * DP v2.0 I.3.3 ignore the stream signature L' in QSES reply msg reply. + * I.3.5 MST source device may use a QSES msg to query downstream status + * for a particular stream. + */ +static +int intel_dp_mst_hdcp2_check_link(struct intel_digital_port *dig_port, + struct intel_connector *connector) +{ + struct intel_hdcp *hdcp = &connector->hdcp; + int ret; + + /* + * We do need to do the Link Check only for the connector involved with + * HDCP port authentication and encryption. + * We can re-use the hdcp->is_repeater flag to know that the connector + * involved with HDCP port authentication and encryption. + */ + if (hdcp->is_repeater) { + ret = intel_dp_hdcp2_check_link(dig_port, connector); + if (ret) + return ret; + } + + return intel_dp_mst_get_qses_status(dig_port, connector) ? 0 : -EINVAL; +} + static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = { .write_an_aksv = intel_dp_hdcp_write_an_aksv, .read_bksv = intel_dp_hdcp_read_bksv, @@ -673,10 +803,16 @@ static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = { .read_ksv_ready = intel_dp_hdcp_read_ksv_ready, .read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo, .read_v_prime_part = intel_dp_hdcp_read_v_prime_part, - .toggle_signalling = intel_dp_mst_hdcp_toggle_signalling, + .toggle_signalling = intel_dp_hdcp_toggle_signalling, + .stream_encryption = intel_dp_mst_hdcp_stream_encryption, .check_link = intel_dp_mst_hdcp_check_link, .hdcp_capable = intel_dp_hdcp_capable, - + .write_2_2_msg = intel_dp_hdcp2_write_msg, + .read_2_2_msg = intel_dp_hdcp2_read_msg, + .config_stream_type = intel_dp_hdcp2_config_stream_type, + .stream_2_2_encryption = intel_dp_mst_hdcp2_stream_encryption, + .check_2_2_link = intel_dp_mst_hdcp2_check_link, + .hdcp_2_2_capable = intel_dp_hdcp2_capable, .protocol = HDCP_PROTOCOL_DP, }; @@ -693,10 +829,10 @@ int intel_dp_init_hdcp(struct intel_digital_port *dig_port, return 0; if (intel_connector->mst_port) - return intel_hdcp_init(intel_connector, port, + return intel_hdcp_init(intel_connector, dig_port, &intel_dp_mst_hdcp_shim); else if (!intel_dp_is_edp(intel_dp)) - return intel_hdcp_init(intel_connector, port, + return intel_hdcp_init(intel_connector, dig_port, &intel_dp_hdcp_shim); return 0; 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 91d3979902d0..d8c6d7054d11 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -334,6 +334,27 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, return drm_dp_dpcd_write(&intel_dp->aux, reg, buf, len) == len; } +void intel_dp_set_signal_levels(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state, + enum drm_dp_phy dp_phy) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + u8 train_set = intel_dp->train_set[0]; + char phy_name[10]; + + drm_dbg_kms(&dev_priv->drm, "Using vswing level %d%s, pre-emphasis level %d%s, at %s\n", + train_set & DP_TRAIN_VOLTAGE_SWING_MASK, + train_set & DP_TRAIN_MAX_SWING_REACHED ? " (max)" : "", + (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT, + train_set & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED ? + " (max)" : "", + intel_dp_phy_name(dp_phy, phy_name, sizeof(phy_name))); + + if (intel_dp_phy_is_downstream_of_source(intel_dp, dp_phy)) + intel_dp->set_signal_levels(intel_dp, crtc_state); +} + static bool intel_dp_reset_link_train(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, @@ -341,7 +362,7 @@ intel_dp_reset_link_train(struct intel_dp *intel_dp, u8 dp_train_pat) { memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set)); - intel_dp_set_signal_levels(intel_dp, crtc_state); + intel_dp_set_signal_levels(intel_dp, crtc_state, dp_phy); return intel_dp_set_link_train(intel_dp, crtc_state, dp_phy, dp_train_pat); } @@ -355,7 +376,7 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy); int ret; - intel_dp_set_signal_levels(intel_dp, crtc_state); + intel_dp_set_signal_levels(intel_dp, crtc_state, dp_phy); ret = drm_dp_dpcd_write(&intel_dp->aux, reg, intel_dp->train_set, crtc_state->lane_count); 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 86905aa24db7..6a1f76bd8c75 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -17,6 +17,9 @@ void intel_dp_get_adjust_train(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, enum drm_dp_phy dp_phy, const u8 link_status[DP_LINK_STATUS_SIZE]); +void intel_dp_set_signal_levels(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state, + enum drm_dp_phy dp_phy); void intel_dp_start_link_train(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); void intel_dp_stop_link_train(struct intel_dp *intel_dp, diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 27f04aed8764..f76e2c2a83b8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -569,7 +569,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) intel_hdcp_enable(to_intel_connector(conn_state->connector), - pipe_config->cpu_transcoder, + pipe_config, (u8)conn_state->hdcp_content_type); } @@ -829,12 +829,11 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); - - /* TODO: Figure out how to make HDCP work on GEN12+ */ - if (INTEL_GEN(dev_priv) < 12) { + if (INTEL_GEN(dev_priv) <= 12) { ret = intel_dp_init_hdcp(dig_port, intel_connector); if (ret) - DRM_DEBUG_KMS("HDCP init failed, skipping.\n"); + drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP MST init failed, skipping.\n", + connector->name, connector->base.id); } /* diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c new file mode 100644 index 000000000000..7ba7f315aaee --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -0,0 +1,1363 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ +#include <linux/kernel.h> +#include "intel_display_types.h" +#include "intel_display.h" +#include "intel_dpll.h" +#include "intel_lvds.h" +#include "intel_panel.h" + +struct intel_limit { + struct { + int min, max; + } dot, vco, n, m, m1, m2, p, p1; + + struct { + int dot_limit; + int p2_slow, p2_fast; + } p2; +}; +static const struct intel_limit intel_limits_i8xx_dac = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 908000, .max = 1512000 }, + .n = { .min = 2, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 2, .max = 33 }, + .p2 = { .dot_limit = 165000, + .p2_slow = 4, .p2_fast = 2 }, +}; + +static const struct intel_limit intel_limits_i8xx_dvo = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 908000, .max = 1512000 }, + .n = { .min = 2, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 2, .max = 33 }, + .p2 = { .dot_limit = 165000, + .p2_slow = 4, .p2_fast = 4 }, +}; + +static const struct intel_limit intel_limits_i8xx_lvds = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 908000, .max = 1512000 }, + .n = { .min = 2, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 1, .max = 6 }, + .p2 = { .dot_limit = 165000, + .p2_slow = 14, .p2_fast = 7 }, +}; + +static const struct intel_limit intel_limits_i9xx_sdvo = { + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1400000, .max = 2800000 }, + .n = { .min = 1, .max = 6 }, + .m = { .min = 70, .max = 120 }, + .m1 = { .min = 8, .max = 18 }, + .m2 = { .min = 3, .max = 7 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 200000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const struct intel_limit intel_limits_i9xx_lvds = { + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1400000, .max = 2800000 }, + .n = { .min = 1, .max = 6 }, + .m = { .min = 70, .max = 120 }, + .m1 = { .min = 8, .max = 18 }, + .m2 = { .min = 3, .max = 7 }, + .p = { .min = 7, .max = 98 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 112000, + .p2_slow = 14, .p2_fast = 7 }, +}; + + +static const struct intel_limit intel_limits_g4x_sdvo = { + .dot = { .min = 25000, .max = 270000 }, + .vco = { .min = 1750000, .max = 3500000}, + .n = { .min = 1, .max = 4 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 17, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 10, .max = 30 }, + .p1 = { .min = 1, .max = 3}, + .p2 = { .dot_limit = 270000, + .p2_slow = 10, + .p2_fast = 10 + }, +}; + +static const struct intel_limit intel_limits_g4x_hdmi = { + .dot = { .min = 22000, .max = 400000 }, + .vco = { .min = 1750000, .max = 3500000}, + .n = { .min = 1, .max = 4 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 16, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8}, + .p2 = { .dot_limit = 165000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const struct intel_limit intel_limits_g4x_single_channel_lvds = { + .dot = { .min = 20000, .max = 115000 }, + .vco = { .min = 1750000, .max = 3500000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 17, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 28, .max = 112 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 0, + .p2_slow = 14, .p2_fast = 14 + }, +}; + +static const struct intel_limit intel_limits_g4x_dual_channel_lvds = { + .dot = { .min = 80000, .max = 224000 }, + .vco = { .min = 1750000, .max = 3500000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 17, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 14, .max = 42 }, + .p1 = { .min = 2, .max = 6 }, + .p2 = { .dot_limit = 0, + .p2_slow = 7, .p2_fast = 7 + }, +}; + +static const struct intel_limit pnv_limits_sdvo = { + .dot = { .min = 20000, .max = 400000}, + .vco = { .min = 1700000, .max = 3500000 }, + /* Pineview's Ncounter is a ring counter */ + .n = { .min = 3, .max = 6 }, + .m = { .min = 2, .max = 256 }, + /* Pineview only has one combined m divider, which we treat as m2. */ + .m1 = { .min = 0, .max = 0 }, + .m2 = { .min = 0, .max = 254 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 200000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const struct intel_limit pnv_limits_lvds = { + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1700000, .max = 3500000 }, + .n = { .min = 3, .max = 6 }, + .m = { .min = 2, .max = 256 }, + .m1 = { .min = 0, .max = 0 }, + .m2 = { .min = 0, .max = 254 }, + .p = { .min = 7, .max = 112 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 112000, + .p2_slow = 14, .p2_fast = 14 }, +}; + +/* Ironlake / Sandybridge + * + * We calculate clock using (register_value + 2) for N/M1/M2, so here + * the range value for them is (actual_value - 2). + */ +static const struct intel_limit ilk_limits_dac = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 5 }, + .m = { .min = 79, .max = 127 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const struct intel_limit ilk_limits_single_lvds = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 79, .max = 118 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 28, .max = 112 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 14, .p2_fast = 14 }, +}; + +static const struct intel_limit ilk_limits_dual_lvds = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 79, .max = 127 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 14, .max = 56 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 7, .p2_fast = 7 }, +}; + +/* LVDS 100mhz refclk limits. */ +static const struct intel_limit ilk_limits_single_lvds_100m = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 2 }, + .m = { .min = 79, .max = 126 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 28, .max = 112 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 14, .p2_fast = 14 }, +}; + +static const struct intel_limit ilk_limits_dual_lvds_100m = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 79, .max = 126 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 14, .max = 42 }, + .p1 = { .min = 2, .max = 6 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 7, .p2_fast = 7 }, +}; + +static const struct intel_limit intel_limits_vlv = { + /* + * These are the data rate limits (measured in fast clocks) + * since those are the strictest limits we have. The fast + * clock and actual rate limits are more relaxed, so checking + * them would make no difference. + */ + .dot = { .min = 25000 * 5, .max = 270000 * 5 }, + .vco = { .min = 4000000, .max = 6000000 }, + .n = { .min = 1, .max = 7 }, + .m1 = { .min = 2, .max = 3 }, + .m2 = { .min = 11, .max = 156 }, + .p1 = { .min = 2, .max = 3 }, + .p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */ +}; + +static const struct intel_limit intel_limits_chv = { + /* + * These are the data rate limits (measured in fast clocks) + * since those are the strictest limits we have. The fast + * clock and actual rate limits are more relaxed, so checking + * them would make no difference. + */ + .dot = { .min = 25000 * 5, .max = 540000 * 5}, + .vco = { .min = 4800000, .max = 6480000 }, + .n = { .min = 1, .max = 1 }, + .m1 = { .min = 2, .max = 2 }, + .m2 = { .min = 24 << 22, .max = 175 << 22 }, + .p1 = { .min = 2, .max = 4 }, + .p2 = { .p2_slow = 1, .p2_fast = 14 }, +}; + +static const struct intel_limit intel_limits_bxt = { + /* FIXME: find real dot limits */ + .dot = { .min = 0, .max = INT_MAX }, + .vco = { .min = 4800000, .max = 6700000 }, + .n = { .min = 1, .max = 1 }, + .m1 = { .min = 2, .max = 2 }, + /* FIXME: find real m2 limits */ + .m2 = { .min = 2 << 22, .max = 255 << 22 }, + .p1 = { .min = 2, .max = 4 }, + .p2 = { .p2_slow = 1, .p2_fast = 20 }, +}; + +/* + * Platform specific helpers to calculate the port PLL loopback- (clock.m), + * and post-divider (clock.p) values, pre- (clock.vco) and post-divided fast + * (clock.dot) clock rates. This fast dot clock is fed to the port's IO logic. + * The helpers' return value is the rate of the clock that is fed to the + * display engine's pipe which can be the above fast dot clock rate or a + * divided-down version of it. + */ +/* m1 is reserved as 0 in Pineview, n is a ring counter */ +int pnv_calc_dpll_params(int refclk, struct dpll *clock) +{ + clock->m = clock->m2 + 2; + clock->p = clock->p1 * clock->p2; + if (WARN_ON(clock->n == 0 || clock->p == 0)) + return 0; + clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); + clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); + + return clock->dot; +} + +static u32 i9xx_dpll_compute_m(struct dpll *dpll) +{ + return 5 * (dpll->m1 + 2) + (dpll->m2 + 2); +} + +int i9xx_calc_dpll_params(int refclk, struct dpll *clock) +{ + clock->m = i9xx_dpll_compute_m(clock); + clock->p = clock->p1 * clock->p2; + if (WARN_ON(clock->n + 2 == 0 || clock->p == 0)) + return 0; + clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2); + clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); + + return clock->dot; +} + +int vlv_calc_dpll_params(int refclk, struct dpll *clock) +{ + clock->m = clock->m1 * clock->m2; + clock->p = clock->p1 * clock->p2; + if (WARN_ON(clock->n == 0 || clock->p == 0)) + return 0; + clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); + clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); + + return clock->dot / 5; +} + +int chv_calc_dpll_params(int refclk, struct dpll *clock) +{ + clock->m = clock->m1 * clock->m2; + clock->p = clock->p1 * clock->p2; + if (WARN_ON(clock->n == 0 || clock->p == 0)) + return 0; + clock->vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, clock->m), + clock->n << 22); + clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); + + return clock->dot / 5; +} + +/* + * Returns whether the given set of divisors are valid for a given refclk with + * the given connectors. + */ +static bool intel_pll_is_valid(struct drm_i915_private *dev_priv, + const struct intel_limit *limit, + const struct dpll *clock) +{ + if (clock->n < limit->n.min || limit->n.max < clock->n) + return false; + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) + return false; + if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) + return false; + if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) + return false; + + if (!IS_PINEVIEW(dev_priv) && !IS_VALLEYVIEW(dev_priv) && + !IS_CHERRYVIEW(dev_priv) && !IS_GEN9_LP(dev_priv)) + if (clock->m1 <= clock->m2) + return false; + + if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv) && + !IS_GEN9_LP(dev_priv)) { + if (clock->p < limit->p.min || limit->p.max < clock->p) + return false; + if (clock->m < limit->m.min || limit->m.max < clock->m) + return false; + } + + if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) + return false; + /* XXX: We may need to be checking "Dot clock" depending on the multiplier, + * connector, etc., rather than just a single range. + */ + if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) + return false; + + return true; +} + +static int +i9xx_select_p2_div(const struct intel_limit *limit, + const struct intel_crtc_state *crtc_state, + int target) +{ + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + /* + * For LVDS just rely on its current settings for dual-channel. + * We haven't figured out how to reliably set up different + * single/dual channel state, if we even can. + */ + if (intel_is_dual_link_lvds(dev_priv)) + return limit->p2.p2_fast; + else + return limit->p2.p2_slow; + } else { + if (target < limit->p2.dot_limit) + return limit->p2.p2_slow; + else + return limit->p2.p2_fast; + } +} + +/* + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * + * Target and reference clocks are specified in kHz. + * + * If match_clock is provided, then best_clock P divider must match the P + * divider from @match_clock used for LVDS downclocking. + */ +static bool +i9xx_find_best_dpll(const struct intel_limit *limit, + struct intel_crtc_state *crtc_state, + int target, int refclk, struct dpll *match_clock, + struct dpll *best_clock) +{ + struct drm_device *dev = crtc_state->uapi.crtc->dev; + struct dpll clock; + int err = target; + + memset(best_clock, 0, sizeof(*best_clock)); + + clock.p2 = i9xx_select_p2_div(limit, crtc_state, target); + + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; + clock.m1++) { + for (clock.m2 = limit->m2.min; + clock.m2 <= limit->m2.max; clock.m2++) { + if (clock.m2 >= clock.m1) + break; + for (clock.n = limit->n.min; + clock.n <= limit->n.max; clock.n++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; clock.p1++) { + int this_err; + + i9xx_calc_dpll_params(refclk, &clock); + if (!intel_pll_is_valid(to_i915(dev), + limit, + &clock)) + continue; + if (match_clock && + clock.p != match_clock->p) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + + return (err != target); +} + +/* + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * + * Target and reference clocks are specified in kHz. + * + * If match_clock is provided, then best_clock P divider must match the P + * divider from @match_clock used for LVDS downclocking. + */ +static bool +pnv_find_best_dpll(const struct intel_limit *limit, + struct intel_crtc_state *crtc_state, + int target, int refclk, struct dpll *match_clock, + struct dpll *best_clock) +{ + struct drm_device *dev = crtc_state->uapi.crtc->dev; + struct dpll clock; + int err = target; + + memset(best_clock, 0, sizeof(*best_clock)); + + clock.p2 = i9xx_select_p2_div(limit, crtc_state, target); + + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; + clock.m1++) { + for (clock.m2 = limit->m2.min; + clock.m2 <= limit->m2.max; clock.m2++) { + for (clock.n = limit->n.min; + clock.n <= limit->n.max; clock.n++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; clock.p1++) { + int this_err; + + pnv_calc_dpll_params(refclk, &clock); + if (!intel_pll_is_valid(to_i915(dev), + limit, + &clock)) + continue; + if (match_clock && + clock.p != match_clock->p) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + + return (err != target); +} + +/* + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + * + * Target and reference clocks are specified in kHz. + * + * If match_clock is provided, then best_clock P divider must match the P + * divider from @match_clock used for LVDS downclocking. + */ +static bool +g4x_find_best_dpll(const struct intel_limit *limit, + struct intel_crtc_state *crtc_state, + int target, int refclk, struct dpll *match_clock, + struct dpll *best_clock) +{ + struct drm_device *dev = crtc_state->uapi.crtc->dev; + struct dpll clock; + int max_n; + bool found = false; + /* approximately equals target * 0.00585 */ + int err_most = (target >> 8) + (target >> 9); + + memset(best_clock, 0, sizeof(*best_clock)); + + clock.p2 = i9xx_select_p2_div(limit, crtc_state, target); + + max_n = limit->n.max; + /* based on hardware requirement, prefer smaller n to precision */ + for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { + /* based on hardware requirement, prefere larger m1,m2 */ + for (clock.m1 = limit->m1.max; + clock.m1 >= limit->m1.min; clock.m1--) { + for (clock.m2 = limit->m2.max; + clock.m2 >= limit->m2.min; clock.m2--) { + for (clock.p1 = limit->p1.max; + clock.p1 >= limit->p1.min; clock.p1--) { + int this_err; + + i9xx_calc_dpll_params(refclk, &clock); + if (!intel_pll_is_valid(to_i915(dev), + limit, + &clock)) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err_most) { + *best_clock = clock; + err_most = this_err; + max_n = clock.n; + found = true; + } + } + } + } + } + return found; +} + +/* + * Check if the calculated PLL configuration is more optimal compared to the + * best configuration and error found so far. Return the calculated error. + */ +static bool vlv_PLL_is_optimal(struct drm_device *dev, int target_freq, + const struct dpll *calculated_clock, + const struct dpll *best_clock, + unsigned int best_error_ppm, + unsigned int *error_ppm) +{ + /* + * For CHV ignore the error and consider only the P value. + * Prefer a bigger P value based on HW requirements. + */ + if (IS_CHERRYVIEW(to_i915(dev))) { + *error_ppm = 0; + + return calculated_clock->p > best_clock->p; + } + + if (drm_WARN_ON_ONCE(dev, !target_freq)) + return false; + + *error_ppm = div_u64(1000000ULL * + abs(target_freq - calculated_clock->dot), + target_freq); + /* + * Prefer a better P value over a better (smaller) error if the error + * is small. Ensure this preference for future configurations too by + * setting the error to 0. + */ + if (*error_ppm < 100 && calculated_clock->p > best_clock->p) { + *error_ppm = 0; + + return true; + } + + return *error_ppm + 10 < best_error_ppm; +} + +/* + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + */ +static bool +vlv_find_best_dpll(const struct intel_limit *limit, + struct intel_crtc_state *crtc_state, + int target, int refclk, struct dpll *match_clock, + struct dpll *best_clock) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_device *dev = crtc->base.dev; + struct dpll clock; + unsigned int bestppm = 1000000; + /* min update 19.2 MHz */ + int max_n = min(limit->n.max, refclk / 19200); + bool found = false; + + target *= 5; /* fast clock */ + + memset(best_clock, 0, sizeof(*best_clock)); + + /* based on hardware requirement, prefer smaller n to precision */ + for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { + for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { + for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow; + clock.p2 -= clock.p2 > 10 ? 2 : 1) { + clock.p = clock.p1 * clock.p2; + /* based on hardware requirement, prefer bigger m1,m2 values */ + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { + unsigned int ppm; + + clock.m2 = DIV_ROUND_CLOSEST(target * clock.p * clock.n, + refclk * clock.m1); + + vlv_calc_dpll_params(refclk, &clock); + + if (!intel_pll_is_valid(to_i915(dev), + limit, + &clock)) + continue; + + if (!vlv_PLL_is_optimal(dev, target, + &clock, + best_clock, + bestppm, &ppm)) + continue; + + *best_clock = clock; + bestppm = ppm; + found = true; + } + } + } + } + + return found; +} + +/* + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + */ +static bool +chv_find_best_dpll(const struct intel_limit *limit, + struct intel_crtc_state *crtc_state, + int target, int refclk, struct dpll *match_clock, + struct dpll *best_clock) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_device *dev = crtc->base.dev; + unsigned int best_error_ppm; + struct dpll clock; + u64 m2; + int found = false; + + memset(best_clock, 0, sizeof(*best_clock)); + best_error_ppm = 1000000; + + /* + * Based on hardware doc, the n always set to 1, and m1 always + * set to 2. If requires to support 200Mhz refclk, we need to + * revisit this because n may not 1 anymore. + */ + clock.n = 1; + clock.m1 = 2; + target *= 5; /* fast clock */ + + for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { + for (clock.p2 = limit->p2.p2_fast; + clock.p2 >= limit->p2.p2_slow; + clock.p2 -= clock.p2 > 10 ? 2 : 1) { + unsigned int error_ppm; + + clock.p = clock.p1 * clock.p2; + + m2 = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(target, clock.p * clock.n) << 22, + refclk * clock.m1); + + if (m2 > INT_MAX/clock.m1) + continue; + + clock.m2 = m2; + + chv_calc_dpll_params(refclk, &clock); + + if (!intel_pll_is_valid(to_i915(dev), limit, &clock)) + continue; + + if (!vlv_PLL_is_optimal(dev, target, &clock, best_clock, + best_error_ppm, &error_ppm)) + continue; + + *best_clock = clock; + best_error_ppm = error_ppm; + found = true; + } + } + + return found; +} + +bool bxt_find_best_dpll(struct intel_crtc_state *crtc_state, + struct dpll *best_clock) +{ + int refclk = 100000; + const struct intel_limit *limit = &intel_limits_bxt; + + return chv_find_best_dpll(limit, crtc_state, + crtc_state->port_clock, refclk, + NULL, best_clock); +} + +static u32 pnv_dpll_compute_fp(struct dpll *dpll) +{ + return (1 << dpll->n) << 16 | dpll->m2; +} + +static void i9xx_update_pll_dividers(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct dpll *reduced_clock) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + u32 fp, fp2 = 0; + + if (IS_PINEVIEW(dev_priv)) { + fp = pnv_dpll_compute_fp(&crtc_state->dpll); + if (reduced_clock) + fp2 = pnv_dpll_compute_fp(reduced_clock); + } else { + fp = i9xx_dpll_compute_fp(&crtc_state->dpll); + if (reduced_clock) + fp2 = i9xx_dpll_compute_fp(reduced_clock); + } + + crtc_state->dpll_hw_state.fp0 = fp; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && + reduced_clock) { + crtc_state->dpll_hw_state.fp1 = fp2; + } else { + crtc_state->dpll_hw_state.fp1 = fp; + } +} + +static void i9xx_compute_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct dpll *reduced_clock) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + u32 dpll; + struct dpll *clock = &crtc_state->dpll; + + i9xx_update_pll_dividers(crtc, crtc_state, reduced_clock); + + dpll = DPLL_VGA_MODE_DIS; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) + dpll |= DPLLB_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + + if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || + IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) { + dpll |= (crtc_state->pixel_multiplier - 1) + << SDVO_MULTIPLIER_SHIFT_HIRES; + } + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO) || + intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) + dpll |= DPLL_SDVO_HIGH_SPEED; + + if (intel_crtc_has_dp_encoder(crtc_state)) + dpll |= DPLL_SDVO_HIGH_SPEED; + + /* compute bitmask from p1 value */ + if (IS_PINEVIEW(dev_priv)) + dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW; + else { + dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + if (IS_G4X(dev_priv) && reduced_clock) + dpll |= (1 << (reduced_clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + } + switch (clock->p2) { + case 5: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; + break; + case 7: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; + break; + case 10: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; + break; + case 14: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; + break; + } + if (INTEL_GEN(dev_priv) >= 4) + dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); + + if (crtc_state->sdvo_tv_clock) + dpll |= PLL_REF_INPUT_TVCLKINBC; + else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && + intel_panel_use_ssc(dev_priv)) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; + else + dpll |= PLL_REF_INPUT_DREFCLK; + + dpll |= DPLL_VCO_ENABLE; + crtc_state->dpll_hw_state.dpll = dpll; + + if (INTEL_GEN(dev_priv) >= 4) { + u32 dpll_md = (crtc_state->pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; + crtc_state->dpll_hw_state.dpll_md = dpll_md; + } +} + +static void i8xx_compute_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct dpll *reduced_clock) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + u32 dpll; + struct dpll *clock = &crtc_state->dpll; + + i9xx_update_pll_dividers(crtc, crtc_state, reduced_clock); + + dpll = DPLL_VGA_MODE_DIS; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + } else { + if (clock->p1 == 2) + dpll |= PLL_P1_DIVIDE_BY_TWO; + else + dpll |= (clock->p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; + if (clock->p2 == 4) + dpll |= PLL_P2_DIVIDE_BY_4; + } + + /* + * Bspec: + * "[Almador Errata}: For the correct operation of the muxed DVO pins + * (GDEVSELB/I2Cdata, GIRDBY/I2CClk) and (GFRAMEB/DVI_Data, + * GTRDYB/DVI_Clk): Bit 31 (DPLL VCO Enable) and Bit 30 (2X Clock + * Enable) must be set to “1” in both the DPLL A Control Register + * (06014h-06017h) and DPLL B Control Register (06018h-0601Bh)." + * + * For simplicity We simply keep both bits always enabled in + * both DPLLS. The spec says we should disable the DVO 2X clock + * when not needed, but this seems to work fine in practice. + */ + if (IS_I830(dev_priv) || + intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DVO)) + dpll |= DPLL_DVO_2X_MODE; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && + intel_panel_use_ssc(dev_priv)) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; + else + dpll |= PLL_REF_INPUT_DREFCLK; + + dpll |= DPLL_VCO_ENABLE; + crtc_state->dpll_hw_state.dpll = dpll; +} + +static int hsw_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct intel_atomic_state *state = + to_intel_atomic_state(crtc_state->uapi.state); + + if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI) || + INTEL_GEN(dev_priv) >= 11) { + struct intel_encoder *encoder = + intel_get_crtc_new_encoder(state, crtc_state); + + if (!intel_reserve_shared_dplls(state, crtc, encoder)) { + drm_dbg_kms(&dev_priv->drm, + "failed to find PLL for pipe %c\n", + pipe_name(crtc->pipe)); + return -EINVAL; + } + } + + return 0; +} + +static bool ilk_needs_fb_cb_tune(struct dpll *dpll, int factor) +{ + return i9xx_dpll_compute_m(dpll) < factor * dpll->n; +} + + +static void ilk_compute_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct dpll *reduced_clock) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + u32 dpll, fp, fp2; + int factor; + + /* Enable autotuning of the PLL clock (if permissible) */ + factor = 21; + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + if ((intel_panel_use_ssc(dev_priv) && + dev_priv->vbt.lvds_ssc_freq == 100000) || + (HAS_PCH_IBX(dev_priv) && + intel_is_dual_link_lvds(dev_priv))) + factor = 25; + } else if (crtc_state->sdvo_tv_clock) { + factor = 20; + } + + fp = i9xx_dpll_compute_fp(&crtc_state->dpll); + + if (ilk_needs_fb_cb_tune(&crtc_state->dpll, factor)) + fp |= FP_CB_TUNE; + + if (reduced_clock) { + fp2 = i9xx_dpll_compute_fp(reduced_clock); + + if (reduced_clock->m < factor * reduced_clock->n) + fp2 |= FP_CB_TUNE; + } else { + fp2 = fp; + } + + dpll = 0; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) + dpll |= DPLLB_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + + dpll |= (crtc_state->pixel_multiplier - 1) + << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO) || + intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) + dpll |= DPLL_SDVO_HIGH_SPEED; + + if (intel_crtc_has_dp_encoder(crtc_state)) + dpll |= DPLL_SDVO_HIGH_SPEED; + + /* + * The high speed IO clock is only really required for + * SDVO/HDMI/DP, but we also enable it for CRT to make it + * possible to share the DPLL between CRT and HDMI. Enabling + * the clock needlessly does no real harm, except use up a + * bit of power potentially. + * + * We'll limit this to IVB with 3 pipes, since it has only two + * DPLLs and so DPLL sharing is the only way to get three pipes + * driving PCH ports at the same time. On SNB we could do this, + * and potentially avoid enabling the second DPLL, but it's not + * clear if it''s a win or loss power wise. No point in doing + * this on ILK at all since it has a fixed DPLL<->pipe mapping. + */ + if (INTEL_NUM_PIPES(dev_priv) == 3 && + intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG)) + dpll |= DPLL_SDVO_HIGH_SPEED; + + /* compute bitmask from p1 value */ + dpll |= (1 << (crtc_state->dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + /* also FPA1 */ + dpll |= (1 << (crtc_state->dpll.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + + switch (crtc_state->dpll.p2) { + case 5: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; + break; + case 7: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; + break; + case 10: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; + break; + case 14: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; + break; + } + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) && + intel_panel_use_ssc(dev_priv)) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; + else + dpll |= PLL_REF_INPUT_DREFCLK; + + dpll |= DPLL_VCO_ENABLE; + + crtc_state->dpll_hw_state.dpll = dpll; + crtc_state->dpll_hw_state.fp0 = fp; + crtc_state->dpll_hw_state.fp1 = fp2; +} + +static int ilk_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct intel_atomic_state *state = + to_intel_atomic_state(crtc_state->uapi.state); + const struct intel_limit *limit; + int refclk = 120000; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ + if (!crtc_state->has_pch_encoder) + return 0; + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + if (intel_panel_use_ssc(dev_priv)) { + drm_dbg_kms(&dev_priv->drm, + "using SSC reference clock of %d kHz\n", + dev_priv->vbt.lvds_ssc_freq); + refclk = dev_priv->vbt.lvds_ssc_freq; + } + + if (intel_is_dual_link_lvds(dev_priv)) { + if (refclk == 100000) + limit = &ilk_limits_dual_lvds_100m; + else + limit = &ilk_limits_dual_lvds; + } else { + if (refclk == 100000) + limit = &ilk_limits_single_lvds_100m; + else + limit = &ilk_limits_single_lvds; + } + } else { + limit = &ilk_limits_dac; + } + + if (!crtc_state->clock_set && + !g4x_find_best_dpll(limit, crtc_state, crtc_state->port_clock, + refclk, NULL, &crtc_state->dpll)) { + drm_err(&dev_priv->drm, + "Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + ilk_compute_dpll(crtc, crtc_state, NULL); + + if (!intel_reserve_shared_dplls(state, crtc, NULL)) { + drm_dbg_kms(&dev_priv->drm, + "failed to find PLL for pipe %c\n", + pipe_name(crtc->pipe)); + return -EINVAL; + } + + return 0; +} + +void vlv_compute_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config) +{ + pipe_config->dpll_hw_state.dpll = DPLL_INTEGRATED_REF_CLK_VLV | + DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; + if (crtc->pipe != PIPE_A) + pipe_config->dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; + + /* DPLL not used with DSI, but still need the rest set up */ + if (!intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DSI)) + pipe_config->dpll_hw_state.dpll |= DPLL_VCO_ENABLE | + DPLL_EXT_BUFFER_ENABLE_VLV; + + pipe_config->dpll_hw_state.dpll_md = + (pipe_config->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; +} + +void chv_compute_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config) +{ + pipe_config->dpll_hw_state.dpll = DPLL_SSC_REF_CLK_CHV | + DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; + if (crtc->pipe != PIPE_A) + pipe_config->dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; + + /* DPLL not used with DSI, but still need the rest set up */ + if (!intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DSI)) + pipe_config->dpll_hw_state.dpll |= DPLL_VCO_ENABLE; + + pipe_config->dpll_hw_state.dpll_md = + (pipe_config->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; +} + +static int chv_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + int refclk = 100000; + const struct intel_limit *limit = &intel_limits_chv; + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + if (!crtc_state->clock_set && + !chv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, + refclk, NULL, &crtc_state->dpll)) { + drm_err(&i915->drm, "Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + chv_compute_dpll(crtc, crtc_state); + + return 0; +} + +static int vlv_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + int refclk = 100000; + const struct intel_limit *limit = &intel_limits_vlv; + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + if (!crtc_state->clock_set && + !vlv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, + refclk, NULL, &crtc_state->dpll)) { + drm_err(&i915->drm, "Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + vlv_compute_dpll(crtc, crtc_state); + + return 0; +} + +static int g4x_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + const struct intel_limit *limit; + int refclk = 96000; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + if (intel_panel_use_ssc(dev_priv)) { + refclk = dev_priv->vbt.lvds_ssc_freq; + drm_dbg_kms(&dev_priv->drm, + "using SSC reference clock of %d kHz\n", + refclk); + } + + if (intel_is_dual_link_lvds(dev_priv)) + limit = &intel_limits_g4x_dual_channel_lvds; + else + limit = &intel_limits_g4x_single_channel_lvds; + } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) || + intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG)) { + limit = &intel_limits_g4x_hdmi; + } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO)) { + limit = &intel_limits_g4x_sdvo; + } else { + /* The option is for other outputs */ + limit = &intel_limits_i9xx_sdvo; + } + + if (!crtc_state->clock_set && + !g4x_find_best_dpll(limit, crtc_state, crtc_state->port_clock, + refclk, NULL, &crtc_state->dpll)) { + drm_err(&dev_priv->drm, + "Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + i9xx_compute_dpll(crtc, crtc_state, NULL); + + return 0; +} + +static int pnv_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + const struct intel_limit *limit; + int refclk = 96000; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + if (intel_panel_use_ssc(dev_priv)) { + refclk = dev_priv->vbt.lvds_ssc_freq; + drm_dbg_kms(&dev_priv->drm, + "using SSC reference clock of %d kHz\n", + refclk); + } + + limit = &pnv_limits_lvds; + } else { + limit = &pnv_limits_sdvo; + } + + if (!crtc_state->clock_set && + !pnv_find_best_dpll(limit, crtc_state, crtc_state->port_clock, + refclk, NULL, &crtc_state->dpll)) { + drm_err(&dev_priv->drm, + "Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + i9xx_compute_dpll(crtc, crtc_state, NULL); + + return 0; +} + +static int i9xx_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + const struct intel_limit *limit; + int refclk = 96000; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + if (intel_panel_use_ssc(dev_priv)) { + refclk = dev_priv->vbt.lvds_ssc_freq; + drm_dbg_kms(&dev_priv->drm, + "using SSC reference clock of %d kHz\n", + refclk); + } + + limit = &intel_limits_i9xx_lvds; + } else { + limit = &intel_limits_i9xx_sdvo; + } + + if (!crtc_state->clock_set && + !i9xx_find_best_dpll(limit, crtc_state, crtc_state->port_clock, + refclk, NULL, &crtc_state->dpll)) { + drm_err(&dev_priv->drm, + "Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + i9xx_compute_dpll(crtc, crtc_state, NULL); + + return 0; +} + +static int i8xx_crtc_compute_clock(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + const struct intel_limit *limit; + int refclk = 48000; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) { + if (intel_panel_use_ssc(dev_priv)) { + refclk = dev_priv->vbt.lvds_ssc_freq; + drm_dbg_kms(&dev_priv->drm, + "using SSC reference clock of %d kHz\n", + refclk); + } + + limit = &intel_limits_i8xx_lvds; + } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DVO)) { + limit = &intel_limits_i8xx_dvo; + } else { + limit = &intel_limits_i8xx_dac; + } + + if (!crtc_state->clock_set && + !i9xx_find_best_dpll(limit, crtc_state, crtc_state->port_clock, + refclk, NULL, &crtc_state->dpll)) { + drm_err(&dev_priv->drm, + "Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + i8xx_compute_dpll(crtc, crtc_state, NULL); + + return 0; +} + +void +intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv) +{ + if (INTEL_GEN(dev_priv) >= 9 || HAS_DDI(dev_priv)) + dev_priv->display.crtc_compute_clock = hsw_crtc_compute_clock; + else if (HAS_PCH_SPLIT(dev_priv)) + dev_priv->display.crtc_compute_clock = ilk_crtc_compute_clock; + else if (IS_CHERRYVIEW(dev_priv)) + dev_priv->display.crtc_compute_clock = chv_crtc_compute_clock; + else if (IS_VALLEYVIEW(dev_priv)) + dev_priv->display.crtc_compute_clock = vlv_crtc_compute_clock; + else if (IS_G4X(dev_priv)) + dev_priv->display.crtc_compute_clock = g4x_crtc_compute_clock; + else if (IS_PINEVIEW(dev_priv)) + dev_priv->display.crtc_compute_clock = pnv_crtc_compute_clock; + else if (!IS_GEN(dev_priv, 2)) + dev_priv->display.crtc_compute_clock = i9xx_crtc_compute_clock; + else + dev_priv->display.crtc_compute_clock = i8xx_crtc_compute_clock; +} diff --git a/drivers/gpu/drm/i915/display/intel_dpll.h b/drivers/gpu/drm/i915/display/intel_dpll.h new file mode 100644 index 000000000000..caf4615092e1 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dpll.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef _INTEL_DPLL_H_ +#define _INTEL_DPLL_H_ + +struct dpll; +struct drm_i915_private; +struct intel_crtc; +struct intel_crtc_state; + +void intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv); +int vlv_calc_dpll_params(int refclk, struct dpll *clock); +int pnv_calc_dpll_params(int refclk, struct dpll *clock); +int i9xx_calc_dpll_params(int refclk, struct dpll *clock); +void vlv_compute_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config); +void chv_compute_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config); + +#endif diff --git a/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c b/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c index 88628764956d..584c14c4cbd0 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c @@ -43,7 +43,7 @@ #define PANEL_PWM_MAX_VALUE 0xFF -static u32 dcs_get_backlight(struct intel_connector *connector) +static u32 dcs_get_backlight(struct intel_connector *connector, enum pipe unused) { struct intel_encoder *encoder = intel_attached_encoder(connector); struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index 33200b5cfad0..5fd4fa4805ef 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -676,7 +676,7 @@ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc) } static bool tiling_is_valid(struct drm_i915_private *dev_priv, - uint64_t modifier) + u64 modifier) { switch (modifier) { case DRM_FORMAT_MOD_LINEAR: diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c new file mode 100644 index 000000000000..b2eb96ae10a2 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_fdi.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ +#include "intel_atomic.h" +#include "intel_display_types.h" +#include "intel_fdi.h" + +/* units of 100MHz */ +static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state) +{ + if (crtc_state->hw.enable && crtc_state->has_pch_encoder) + return crtc_state->fdi_lanes; + + return 0; +} + +static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, + struct intel_crtc_state *pipe_config) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_atomic_state *state = pipe_config->uapi.state; + struct intel_crtc *other_crtc; + struct intel_crtc_state *other_crtc_state; + + drm_dbg_kms(&dev_priv->drm, + "checking fdi config on pipe %c, lanes %i\n", + pipe_name(pipe), pipe_config->fdi_lanes); + if (pipe_config->fdi_lanes > 4) { + drm_dbg_kms(&dev_priv->drm, + "invalid fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return -EINVAL; + } + + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { + if (pipe_config->fdi_lanes > 2) { + drm_dbg_kms(&dev_priv->drm, + "only 2 lanes on haswell, required: %i lanes\n", + pipe_config->fdi_lanes); + return -EINVAL; + } else { + return 0; + } + } + + if (INTEL_NUM_PIPES(dev_priv) == 2) + return 0; + + /* Ivybridge 3 pipe is really complicated */ + switch (pipe) { + case PIPE_A: + return 0; + case PIPE_B: + if (pipe_config->fdi_lanes <= 2) + return 0; + + other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_C); + other_crtc_state = + intel_atomic_get_crtc_state(state, other_crtc); + if (IS_ERR(other_crtc_state)) + return PTR_ERR(other_crtc_state); + + if (pipe_required_fdi_lanes(other_crtc_state) > 0) { + drm_dbg_kms(&dev_priv->drm, + "invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return -EINVAL; + } + return 0; + case PIPE_C: + if (pipe_config->fdi_lanes > 2) { + drm_dbg_kms(&dev_priv->drm, + "only 2 lanes on pipe %c: required %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return -EINVAL; + } + + other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_B); + other_crtc_state = + intel_atomic_get_crtc_state(state, other_crtc); + if (IS_ERR(other_crtc_state)) + return PTR_ERR(other_crtc_state); + + if (pipe_required_fdi_lanes(other_crtc_state) > 2) { + drm_dbg_kms(&dev_priv->drm, + "fdi link B uses too many lanes to enable link C\n"); + return -EINVAL; + } + return 0; + default: + BUG(); + } +} + +int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, + struct intel_crtc_state *pipe_config) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *i915 = to_i915(dev); + const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; + int lane, link_bw, fdi_dotclock, ret; + bool needs_recompute = false; + +retry: + /* FDI is a binary signal running at ~2.7GHz, encoding + * each output octet as 10 bits. The actual frequency + * is stored as a divider into a 100MHz clock, and the + * mode pixel clock is stored in units of 1KHz. + * Hence the bw of each lane in terms of the mode signal + * is: + */ + link_bw = intel_fdi_link_freq(i915, pipe_config); + + fdi_dotclock = adjusted_mode->crtc_clock; + + lane = ilk_get_lanes_required(fdi_dotclock, link_bw, + pipe_config->pipe_bpp); + + pipe_config->fdi_lanes = lane; + + intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, + link_bw, &pipe_config->fdi_m_n, false, false); + + ret = ilk_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config); + if (ret == -EDEADLK) + return ret; + + if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) { + pipe_config->pipe_bpp -= 2*3; + drm_dbg_kms(&i915->drm, + "fdi link bw constraint, reducing pipe bpp to %i\n", + pipe_config->pipe_bpp); + needs_recompute = true; + pipe_config->bw_constrained = true; + + goto retry; + } + + if (needs_recompute) + return I915_DISPLAY_CONFIG_RETRY; + + return ret; +} + +void intel_fdi_normal_train(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* enable normal train */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (IS_IVYBRIDGE(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_NONE_IVB; + temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; + } + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_NORMAL_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE; + } + intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); + + /* wait one idle pattern time */ + intel_de_posting_read(dev_priv, reg); + udelay(1000); + + /* IVB wants error correction enabled */ + if (IS_IVYBRIDGE(dev_priv)) + intel_de_write(dev_priv, reg, + intel_de_read(dev_priv, reg) | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); +} + +/* The FDI link training functions for ILK/Ibexpeak. */ +static void ilk_fdi_link_train(struct intel_crtc *crtc, + const struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp, tries; + + /* FDI needs bits from pipe first */ + assert_pipe_enabled(dev_priv, crtc_state->cpu_transcoder); + + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + reg = FDI_RX_IMR(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_RX_SYMBOL_LOCK; + temp &= ~FDI_RX_BIT_LOCK; + intel_de_write(dev_priv, reg, temp); + intel_de_read(dev_priv, reg); + udelay(150); + + /* enable CPU FDI TX and PCH FDI RX */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + /* Ironlake workaround, enable clock pointer after FDI enable*/ + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), + FDI_RX_PHASE_SYNC_POINTER_OVR); + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), + FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN); + + reg = FDI_RX_IIR(pipe); + for (tries = 0; tries < 5; tries++) { + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if ((temp & FDI_RX_BIT_LOCK)) { + drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n"); + intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK); + break; + } + } + if (tries == 5) + drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); + + /* Train 2 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + reg = FDI_RX_IIR(pipe); + for (tries = 0; tries < 5; tries++) { + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if (temp & FDI_RX_SYMBOL_LOCK) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_SYMBOL_LOCK); + drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n"); + break; + } + } + if (tries == 5) + drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); + + drm_dbg_kms(&dev_priv->drm, "FDI train done\n"); + +} + +static const int snb_b_fdi_train_param[] = { + FDI_LINK_TRAIN_400MV_0DB_SNB_B, + FDI_LINK_TRAIN_400MV_6DB_SNB_B, + FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, + FDI_LINK_TRAIN_800MV_0DB_SNB_B, +}; + +/* The FDI link training functions for SNB/Cougarpoint. */ +static void gen6_fdi_link_train(struct intel_crtc *crtc, + const struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp, i, retry; + + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + reg = FDI_RX_IMR(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_RX_SYMBOL_LOCK; + temp &= ~FDI_RX_BIT_LOCK; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + /* enable CPU FDI TX and PCH FDI RX */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + /* SNB-B */ + temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); + + intel_de_write(dev_priv, FDI_RX_MISC(pipe), + FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + } + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + for (i = 0; i < 4; i++) { + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + temp |= snb_b_fdi_train_param[i]; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(500); + + for (retry = 0; retry < 5; retry++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + if (temp & FDI_RX_BIT_LOCK) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_BIT_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 1 done.\n"); + break; + } + udelay(50); + } + if (retry < 5) + break; + } + if (i == 4) + drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); + + /* Train 2 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + if (IS_GEN(dev_priv, 6)) { + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + /* SNB-B */ + temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; + } + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + } + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + for (i = 0; i < 4; i++) { + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + temp |= snb_b_fdi_train_param[i]; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(500); + + for (retry = 0; retry < 5; retry++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + if (temp & FDI_RX_SYMBOL_LOCK) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_SYMBOL_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 2 done.\n"); + break; + } + udelay(50); + } + if (retry < 5) + break; + } + if (i == 4) + drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); + + drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); +} + +/* Manual link training for Ivy Bridge A0 parts */ +static void ivb_manual_fdi_link_train(struct intel_crtc *crtc, + const struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp, i, j; + + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + reg = FDI_RX_IMR(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_RX_SYMBOL_LOCK; + temp &= ~FDI_RX_BIT_LOCK; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n", + intel_de_read(dev_priv, FDI_RX_IIR(pipe))); + + /* Try each vswing and preemphasis setting twice before moving on */ + for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) { + /* disable first in case we need to retry */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); + temp &= ~FDI_TX_ENABLE; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_AUTO; + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp &= ~FDI_RX_ENABLE; + intel_de_write(dev_priv, reg, temp); + + /* enable CPU FDI TX and PCH FDI RX */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + temp |= snb_b_fdi_train_param[j/2]; + temp |= FDI_COMPOSITE_SYNC; + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); + + intel_de_write(dev_priv, FDI_RX_MISC(pipe), + FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + temp |= FDI_COMPOSITE_SYNC; + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(1); /* should be 0.5us */ + + for (i = 0; i < 4; i++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if (temp & FDI_RX_BIT_LOCK || + (intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_BIT_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 1 done, level %i.\n", + i); + break; + } + udelay(1); /* should be 0.5us */ + } + if (i == 4) { + drm_dbg_kms(&dev_priv->drm, + "FDI train 1 fail on vswing %d\n", j / 2); + continue; + } + + /* Train 2 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE_IVB; + temp |= FDI_LINK_TRAIN_PATTERN_2_IVB; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(2); /* should be 1.5us */ + + for (i = 0; i < 4; i++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if (temp & FDI_RX_SYMBOL_LOCK || + (intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_SYMBOL_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 2 done, level %i.\n", + i); + goto train_done; + } + udelay(2); /* should be 1.5us */ + } + if (i == 4) + drm_dbg_kms(&dev_priv->drm, + "FDI train 2 fail on vswing %d\n", j / 2); + } + +train_done: + drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); +} + +void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); + enum pipe pipe = intel_crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; + intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(200); + + /* Switch from Rawclk to PCDclk */ + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp | FDI_PCDCLK); + + intel_de_posting_read(dev_priv, reg); + udelay(200); + + /* Enable CPU FDI TX PLL, always on for Ironlake */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(100); + } +} + +void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = intel_crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* Switch from PCDclk to Rawclk */ + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_PCDCLK); + + /* Disable CPU FDI TX PLL */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_TX_PLL_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(100); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_RX_PLL_ENABLE); + + /* Wait for the clocks to turn off. */ + intel_de_posting_read(dev_priv, reg); + udelay(100); +} + +void ilk_fdi_disable(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* disable CPU FDI tx and PCH FDI rx */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_TX_ENABLE); + intel_de_posting_read(dev_priv, reg); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~(0x7 << 16); + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; + intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(100); + + /* Ironlake workaround, disable clock pointer after downing FDI */ + if (HAS_PCH_IBX(dev_priv)) + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), + FDI_RX_PHASE_SYNC_POINTER_OVR); + + /* still set train pattern 1 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + } + /* BPC in FDI rx is consistent with that in PIPECONF */ + temp &= ~(0x07 << 16); + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(100); +} + +void +intel_fdi_init_hook(struct drm_i915_private *dev_priv) +{ + if (IS_GEN(dev_priv, 5)) { + dev_priv->display.fdi_link_train = ilk_fdi_link_train; + } else if (IS_GEN(dev_priv, 6)) { + dev_priv->display.fdi_link_train = gen6_fdi_link_train; + } else if (IS_IVYBRIDGE(dev_priv)) { + /* FIXME: detect B0+ stepping and use auto training */ + dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; + } +} diff --git a/drivers/gpu/drm/i915/display/intel_fdi.h b/drivers/gpu/drm/i915/display/intel_fdi.h new file mode 100644 index 000000000000..a9cd21663eb8 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_fdi.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef _INTEL_FDI_H_ +#define _INTEL_FDI_H_ + +struct drm_i915_private; +struct intel_crtc; +struct intel_crtc_state; + +#define I915_DISPLAY_CONFIG_RETRY 1 +int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, + struct intel_crtc_state *pipe_config); +void intel_fdi_normal_train(struct intel_crtc *crtc); +void ilk_fdi_disable(struct intel_crtc *crtc); +void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc); +void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state); +void intel_fdi_init_hook(struct drm_i915_private *dev_priv); + +#endif diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index b2a4bbcfdcd2..db8dff2eeb0a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -15,6 +15,7 @@ #include <drm/drm_hdcp.h> #include <drm/i915_component.h> +#include "i915_drv.h" #include "i915_reg.h" #include "intel_display_power.h" #include "intel_display_types.h" @@ -23,9 +24,76 @@ #include "intel_connector.h" #define KEY_LOAD_TRIES 5 -#define ENCRYPT_STATUS_CHANGE_TIMEOUT_MS 50 #define HDCP2_LC_RETRY_CNT 3 +static int intel_conn_to_vcpi(struct intel_connector *connector) +{ + /* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */ + return connector->port ? connector->port->vcpi.vcpi : 0; +} + +/* + * intel_hdcp_required_content_stream selects the most highest common possible HDCP + * content_type for all streams in DP MST topology because security f/w doesn't + * have any provision to mark content_type for each stream separately, it marks + * all available streams with the content_type proivided at the time of port + * authentication. This may prohibit the userspace to use type1 content on + * HDCP 2.2 capable sink because of other sink are not capable of HDCP 2.2 in + * DP MST topology. Though it is not compulsory, security fw should change its + * policy to mark different content_types for different streams. + */ +static int +intel_hdcp_required_content_stream(struct intel_digital_port *dig_port) +{ + struct drm_connector_list_iter conn_iter; + struct intel_digital_port *conn_dig_port; + struct intel_connector *connector; + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct hdcp_port_data *data = &dig_port->hdcp_port_data; + bool enforce_type0 = false; + int k; + + if (dig_port->hdcp_auth_status) + return 0; + + drm_connector_list_iter_begin(&i915->drm, &conn_iter); + for_each_intel_connector_iter(connector, &conn_iter) { + if (connector->base.status == connector_status_disconnected) + continue; + + if (!intel_encoder_is_mst(intel_attached_encoder(connector))) + continue; + + conn_dig_port = intel_attached_dig_port(connector); + if (conn_dig_port != dig_port) + continue; + + if (!enforce_type0 && !intel_hdcp2_capable(connector)) + enforce_type0 = true; + + data->streams[data->k].stream_id = intel_conn_to_vcpi(connector); + data->k++; + + /* if there is only one active stream */ + if (dig_port->dp.active_mst_links <= 1) + break; + } + drm_connector_list_iter_end(&conn_iter); + + if (drm_WARN_ON(&i915->drm, data->k > INTEL_NUM_PIPES(i915) || data->k == 0)) + return -EINVAL; + + /* + * Apply common protection level across all streams in DP MST Topology. + * Use highest supported content type for all streams in DP MST Topology. + */ + for (k = 0; k < data->k; k++) + data->streams[k].stream_type = + enforce_type0 ? DRM_MODE_HDCP_CONTENT_TYPE0 : DRM_MODE_HDCP_CONTENT_TYPE1; + + return 0; +} + static bool intel_hdcp_is_ksv_valid(u8 *ksv) { @@ -762,15 +830,22 @@ static int intel_hdcp_auth(struct intel_connector *connector) if (intel_de_wait_for_set(dev_priv, HDCP_STATUS(dev_priv, cpu_transcoder, port), HDCP_STATUS_ENC, - ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { + HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { drm_err(&dev_priv->drm, "Timed out waiting for encryption\n"); return -ETIMEDOUT; } - /* - * XXX: If we have MST-connected devices, we need to enable encryption - * on those as well. - */ + /* DP MST Auth Part 1 Step 2.a and Step 2.b */ + if (shim->stream_encryption) { + ret = shim->stream_encryption(connector, true); + if (ret) { + drm_err(&dev_priv->drm, "[%s:%d] Failed to enable HDCP 1.4 stream enc\n", + connector->base.name, connector->base.base.id); + return ret; + } + drm_dbg_kms(&dev_priv->drm, "HDCP 1.4 transcoder: %s stream encrypted\n", + transcoder_name(hdcp->stream_transcoder)); + } if (repeater_present) return intel_hdcp_auth_downstream(connector); @@ -792,24 +867,29 @@ static int _intel_hdcp_disable(struct intel_connector *connector) drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP is being disabled...\n", connector->base.name, connector->base.base.id); + if (hdcp->shim->stream_encryption) { + ret = hdcp->shim->stream_encryption(connector, false); + if (ret) { + drm_err(&dev_priv->drm, "[%s:%d] Failed to disable HDCP 1.4 stream enc\n", + connector->base.name, connector->base.base.id); + return ret; + } + drm_dbg_kms(&dev_priv->drm, "HDCP 1.4 transcoder: %s stream encryption disabled\n", + transcoder_name(hdcp->stream_transcoder)); + } + /* - * If there are other connectors on this port using HDCP, don't disable - * it. Instead, toggle the HDCP signalling off on that particular - * connector/pipe and exit. + * If there are other connectors on this port using HDCP, don't disable it + * until it disabled HDCP encryption for all connectors in MST topology. */ - if (dig_port->num_hdcp_streams > 0) { - ret = hdcp->shim->toggle_signalling(dig_port, - cpu_transcoder, false); - if (ret) - DRM_ERROR("Failed to disable HDCP signalling\n"); + if (dig_port->num_hdcp_streams > 0) return ret; - } hdcp->hdcp_encrypted = false; intel_de_write(dev_priv, HDCP_CONF(dev_priv, cpu_transcoder, port), 0); if (intel_de_wait_for_clear(dev_priv, HDCP_STATUS(dev_priv, cpu_transcoder, port), - ~0, ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { + ~0, HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { drm_err(&dev_priv->drm, "Failed to disable HDCP, timeout clearing status\n"); return -ETIMEDOUT; @@ -1014,7 +1094,8 @@ static int hdcp2_prepare_ake_init(struct intel_connector *connector, struct hdcp2_ake_init *ake_data) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1043,7 +1124,8 @@ hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector, struct hdcp2_ake_no_stored_km *ek_pub_km, size_t *msg_sz) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1070,7 +1152,8 @@ hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector, static int hdcp2_verify_hprime(struct intel_connector *connector, struct hdcp2_ake_send_hprime *rx_hprime) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1095,7 +1178,8 @@ static int hdcp2_store_pairing_info(struct intel_connector *connector, struct hdcp2_ake_send_pairing_info *pairing_info) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1121,7 +1205,8 @@ static int hdcp2_prepare_lc_init(struct intel_connector *connector, struct hdcp2_lc_init *lc_init) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1147,7 +1232,8 @@ static int hdcp2_verify_lprime(struct intel_connector *connector, struct hdcp2_lc_send_lprime *rx_lprime) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1172,7 +1258,8 @@ hdcp2_verify_lprime(struct intel_connector *connector, static int hdcp2_prepare_skey(struct intel_connector *connector, struct hdcp2_ske_send_eks *ske_data) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1200,7 +1287,8 @@ hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector, *rep_topology, struct hdcp2_rep_send_ack *rep_send_ack) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1228,7 +1316,8 @@ static int hdcp2_verify_mprime(struct intel_connector *connector, struct hdcp2_rep_stream_ready *stream_ready) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1251,7 +1340,8 @@ hdcp2_verify_mprime(struct intel_connector *connector, static int hdcp2_authenticate_port(struct intel_connector *connector) { - struct hdcp_port_data *data = &connector->hdcp.port_data; + 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; int ret; @@ -1275,6 +1365,7 @@ static int hdcp2_authenticate_port(struct intel_connector *connector) static int hdcp2_close_mei_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; int ret; @@ -1288,7 +1379,7 @@ static int hdcp2_close_mei_session(struct intel_connector *connector) } ret = comp->ops->close_hdcp_session(comp->mei_dev, - &connector->hdcp.port_data); + &dig_port->hdcp_port_data); mutex_unlock(&dev_priv->hdcp_comp_mutex); return ret; @@ -1448,13 +1539,14 @@ static int _hdcp2_propagate_stream_management_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 intel_hdcp *hdcp = &connector->hdcp; union { struct hdcp2_rep_stream_manage stream_manage; struct hdcp2_rep_stream_ready stream_ready; } msgs; const struct intel_hdcp_shim *shim = hdcp->shim; - int ret; + int ret, streams_size_delta, i; if (connector->hdcp.seq_num_m > HDCP_2_2_SEQ_NUM_MAX) return -ERANGE; @@ -1463,16 +1555,18 @@ int _hdcp2_propagate_stream_management_info(struct intel_connector *connector) msgs.stream_manage.msg_id = HDCP_2_2_REP_STREAM_MANAGE; drm_hdcp_cpu_to_be24(msgs.stream_manage.seq_num_m, hdcp->seq_num_m); - /* K no of streams is fixed as 1. Stored as big-endian. */ - msgs.stream_manage.k = cpu_to_be16(1); + msgs.stream_manage.k = cpu_to_be16(data->k); - /* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */ - msgs.stream_manage.streams[0].stream_id = 0; - msgs.stream_manage.streams[0].stream_type = hdcp->content_type; + for (i = 0; i < data->k; i++) { + msgs.stream_manage.streams[i].stream_id = data->streams[i].stream_id; + msgs.stream_manage.streams[i].stream_type = data->streams[i].stream_type; + } + streams_size_delta = (HDCP_2_2_MAX_CONTENT_STREAMS_CNT - data->k) * + sizeof(struct hdcp2_streamid_type); /* Send it to Repeater */ ret = shim->write_2_2_msg(dig_port, &msgs.stream_manage, - sizeof(msgs.stream_manage)); + sizeof(msgs.stream_manage) - streams_size_delta); if (ret < 0) goto out; @@ -1481,8 +1575,8 @@ int _hdcp2_propagate_stream_management_info(struct intel_connector *connector) if (ret < 0) goto out; - hdcp->port_data.seq_num_m = hdcp->seq_num_m; - hdcp->port_data.streams[0].stream_type = hdcp->content_type; + data->seq_num_m = hdcp->seq_num_m; + ret = hdcp2_verify_mprime(connector, &msgs.stream_ready); out: @@ -1606,6 +1700,36 @@ static int hdcp2_authenticate_sink(struct intel_connector *connector) return ret; } +static int hdcp2_enable_stream_encryption(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 intel_hdcp *hdcp = &connector->hdcp; + enum transcoder cpu_transcoder = hdcp->cpu_transcoder; + enum port port = dig_port->base.port; + int ret = 0; + + if (!(intel_de_read(dev_priv, HDCP2_STATUS(dev_priv, cpu_transcoder, port)) & + LINK_ENCRYPTION_STATUS)) { + drm_err(&dev_priv->drm, "[%s:%d] HDCP 2.2 Link is not encrypted\n", + connector->base.name, connector->base.base.id); + return -EPERM; + } + + if (hdcp->shim->stream_2_2_encryption) { + ret = hdcp->shim->stream_2_2_encryption(connector, true); + if (ret) { + drm_err(&dev_priv->drm, "[%s:%d] Failed to enable HDCP 2.2 stream enc\n", + connector->base.name, connector->base.base.id); + return ret; + } + drm_dbg_kms(&dev_priv->drm, "HDCP 2.2 transcoder: %s stream encrypted\n", + transcoder_name(hdcp->stream_transcoder)); + } + + return ret; +} + static int hdcp2_enable_encryption(struct intel_connector *connector) { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); @@ -1641,7 +1765,8 @@ static int hdcp2_enable_encryption(struct intel_connector *connector) HDCP2_STATUS(dev_priv, cpu_transcoder, port), LINK_ENCRYPTION_STATUS, - ENCRYPT_STATUS_CHANGE_TIMEOUT_MS); + HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS); + dig_port->hdcp_auth_status = true; return ret; } @@ -1665,7 +1790,7 @@ static int hdcp2_disable_encryption(struct intel_connector *connector) HDCP2_STATUS(dev_priv, cpu_transcoder, port), LINK_ENCRYPTION_STATUS, - ENCRYPT_STATUS_CHANGE_TIMEOUT_MS); + HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS); if (ret == -ETIMEDOUT) drm_dbg_kms(&dev_priv->drm, "Disable Encryption Timedout"); @@ -1714,11 +1839,11 @@ hdcp2_propagate_stream_management_info(struct intel_connector *connector) static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector) { + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct drm_i915_private *i915 = to_i915(connector->base.dev); - struct intel_hdcp *hdcp = &connector->hdcp; - int ret, i, tries = 3; + int ret = 0, i, tries = 3; - for (i = 0; i < tries; i++) { + for (i = 0; i < tries && !dig_port->hdcp_auth_status; i++) { ret = hdcp2_authenticate_sink(connector); if (!ret) { ret = hdcp2_propagate_stream_management_info(connector); @@ -1728,8 +1853,7 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector) ret); break; } - hdcp->port_data.streams[0].stream_type = - hdcp->content_type; + ret = hdcp2_authenticate_port(connector); if (!ret) break; @@ -1744,7 +1868,7 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector) drm_dbg_kms(&i915->drm, "Port deauth failed.\n"); } - if (!ret) { + if (!ret && !dig_port->hdcp_auth_status) { /* * Ensuring the required 200mSec min time interval between * Session Key Exchange and encryption. @@ -1759,12 +1883,16 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector) } } + ret = hdcp2_enable_stream_encryption(connector); + return ret; } static int _intel_hdcp2_enable(struct intel_connector *connector) { + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct hdcp_port_data *data = &dig_port->hdcp_port_data; struct intel_hdcp *hdcp = &connector->hdcp; int ret; @@ -1772,6 +1900,16 @@ static int _intel_hdcp2_enable(struct intel_connector *connector) connector->base.name, connector->base.base.id, hdcp->content_type); + /* Stream which requires encryption */ + if (!intel_encoder_is_mst(intel_attached_encoder(connector))) { + data->k = 1; + data->streams[0].stream_type = hdcp->content_type; + } else { + ret = intel_hdcp_required_content_stream(dig_port); + if (ret) + return ret; + } + ret = hdcp2_authenticate_and_encrypt(connector); if (ret) { drm_dbg_kms(&i915->drm, "HDCP2 Type%d Enabling Failed. (%d)\n", @@ -1789,18 +1927,37 @@ static int _intel_hdcp2_enable(struct intel_connector *connector) static int _intel_hdcp2_disable(struct intel_connector *connector) { + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct hdcp_port_data *data = &dig_port->hdcp_port_data; + struct intel_hdcp *hdcp = &connector->hdcp; int ret; drm_dbg_kms(&i915->drm, "[%s:%d] HDCP2.2 is being Disabled\n", connector->base.name, connector->base.base.id); + if (hdcp->shim->stream_2_2_encryption) { + ret = hdcp->shim->stream_2_2_encryption(connector, false); + if (ret) { + drm_err(&i915->drm, "[%s:%d] Failed to disable HDCP 2.2 stream enc\n", + connector->base.name, connector->base.base.id); + return ret; + } + drm_dbg_kms(&i915->drm, "HDCP 2.2 transcoder: %s stream encryption disabled\n", + transcoder_name(hdcp->stream_transcoder)); + } + + if (dig_port->num_hdcp_streams > 0) + return ret; + ret = hdcp2_disable_encryption(connector); if (hdcp2_deauthenticate_port(connector) < 0) drm_dbg_kms(&i915->drm, "Port deauth failed.\n"); connector->hdcp.hdcp2_encrypted = false; + dig_port->hdcp_auth_status = false; + data->k = 0; return ret; } @@ -1816,6 +1973,7 @@ static int intel_hdcp2_check_link(struct intel_connector *connector) int ret = 0; mutex_lock(&hdcp->mutex); + mutex_lock(&dig_port->hdcp_mutex); cpu_transcoder = hdcp->cpu_transcoder; /* hdcp2_check_link is expected only when HDCP2.2 is Enabled */ @@ -1837,7 +1995,7 @@ static int intel_hdcp2_check_link(struct intel_connector *connector) goto out; } - ret = hdcp->shim->check_2_2_link(dig_port); + ret = hdcp->shim->check_2_2_link(dig_port, connector); if (ret == HDCP_LINK_PROTECTED) { if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { intel_hdcp_update_value(connector, @@ -1893,6 +2051,7 @@ static int intel_hdcp2_check_link(struct intel_connector *connector) } out: + mutex_unlock(&dig_port->hdcp_mutex); mutex_unlock(&hdcp->mutex); return ret; } @@ -1968,12 +2127,13 @@ static enum mei_fw_tc intel_get_mei_fw_tc(enum transcoder cpu_transcoder) } static int initialize_hdcp_port_data(struct intel_connector *connector, - enum port port, + struct intel_digital_port *dig_port, const struct intel_hdcp_shim *shim) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct hdcp_port_data *data = &dig_port->hdcp_port_data; struct intel_hdcp *hdcp = &connector->hdcp; - struct hdcp_port_data *data = &hdcp->port_data; + enum port port = dig_port->base.port; if (INTEL_GEN(dev_priv) < 12) data->fw_ddi = intel_get_mei_fw_ddi_index(port); @@ -1994,16 +2154,15 @@ static int initialize_hdcp_port_data(struct intel_connector *connector, data->port_type = (u8)HDCP_PORT_TYPE_INTEGRATED; data->protocol = (u8)shim->protocol; - data->k = 1; if (!data->streams) - data->streams = kcalloc(data->k, + data->streams = kcalloc(INTEL_NUM_PIPES(dev_priv), sizeof(struct hdcp2_streamid_type), GFP_KERNEL); if (!data->streams) { drm_err(&dev_priv->drm, "Out of Memory\n"); return -ENOMEM; } - + /* For SST */ data->streams[0].stream_id = 0; data->streams[0].stream_type = hdcp->content_type; @@ -2046,14 +2205,15 @@ void intel_hdcp_component_init(struct drm_i915_private *dev_priv) } } -static void intel_hdcp2_init(struct intel_connector *connector, enum port port, +static void intel_hdcp2_init(struct intel_connector *connector, + struct intel_digital_port *dig_port, const struct intel_hdcp_shim *shim) { struct drm_i915_private *i915 = to_i915(connector->base.dev); struct intel_hdcp *hdcp = &connector->hdcp; int ret; - ret = initialize_hdcp_port_data(connector, port, shim); + ret = initialize_hdcp_port_data(connector, dig_port, shim); if (ret) { drm_dbg_kms(&i915->drm, "Mei hdcp data init failed\n"); return; @@ -2063,7 +2223,7 @@ static void intel_hdcp2_init(struct intel_connector *connector, enum port port, } int intel_hdcp_init(struct intel_connector *connector, - enum port port, + struct intel_digital_port *dig_port, const struct intel_hdcp_shim *shim) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); @@ -2073,15 +2233,15 @@ int intel_hdcp_init(struct intel_connector *connector, if (!shim) return -EINVAL; - if (is_hdcp2_supported(dev_priv) && !connector->mst_port) - intel_hdcp2_init(connector, port, shim); + if (is_hdcp2_supported(dev_priv)) + intel_hdcp2_init(connector, dig_port, shim); ret = drm_connector_attach_content_protection_property(&connector->base, hdcp->hdcp2_supported); if (ret) { hdcp->hdcp2_supported = false; - kfree(hdcp->port_data.streams); + kfree(dig_port->hdcp_port_data.streams); return ret; } @@ -2095,7 +2255,7 @@ int intel_hdcp_init(struct intel_connector *connector, } int intel_hdcp_enable(struct intel_connector *connector, - enum transcoder cpu_transcoder, u8 content_type) + const struct intel_crtc_state *pipe_config, u8 content_type) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); struct intel_digital_port *dig_port = intel_attached_dig_port(connector); @@ -2106,15 +2266,28 @@ int intel_hdcp_enable(struct intel_connector *connector, if (!hdcp->shim) return -ENOENT; + if (!connector->encoder) { + drm_err(&dev_priv->drm, "[%s:%d] encoder is not initialized\n", + connector->base.name, connector->base.base.id); + return -ENODEV; + } + mutex_lock(&hdcp->mutex); mutex_lock(&dig_port->hdcp_mutex); drm_WARN_ON(&dev_priv->drm, hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED); hdcp->content_type = content_type; - hdcp->cpu_transcoder = cpu_transcoder; + + if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) { + hdcp->cpu_transcoder = pipe_config->mst_master_transcoder; + hdcp->stream_transcoder = pipe_config->cpu_transcoder; + } else { + hdcp->cpu_transcoder = pipe_config->cpu_transcoder; + hdcp->stream_transcoder = INVALID_TRANSCODER; + } if (INTEL_GEN(dev_priv) >= 12) - hdcp->port_data.fw_tc = intel_get_mei_fw_tc(cpu_transcoder); + dig_port->hdcp_port_data.fw_tc = intel_get_mei_fw_tc(hdcp->cpu_transcoder); /* * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup @@ -2210,6 +2383,7 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state, if (content_protection_type_changed) { mutex_lock(&hdcp->mutex); hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED; + drm_connector_get(&connector->base); schedule_work(&hdcp->prop_work); mutex_unlock(&hdcp->mutex); } @@ -2221,11 +2395,19 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state, desired_and_not_enabled = hdcp->value != DRM_MODE_CONTENT_PROTECTION_ENABLED; mutex_unlock(&hdcp->mutex); + /* + * If HDCP already ENABLED and CP property is DESIRED, schedule + * prop_work to update correct CP property to user space. + */ + if (!desired_and_not_enabled && !content_protection_type_changed) { + drm_connector_get(&connector->base); + schedule_work(&hdcp->prop_work); + } } if (desired_and_not_enabled || content_protection_type_changed) intel_hdcp_enable(connector, - crtc_state->cpu_transcoder, + crtc_state, (u8)conn_state->hdcp_content_type); } @@ -2275,7 +2457,6 @@ void intel_hdcp_cleanup(struct intel_connector *connector) drm_WARN_ON(connector->base.dev, work_pending(&hdcp->prop_work)); mutex_lock(&hdcp->mutex); - kfree(hdcp->port_data.streams); hdcp->shim = NULL; mutex_unlock(&hdcp->mutex); } diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h index 1bbf5b67ed0a..8f53b0c7fe5c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.h +++ b/drivers/gpu/drm/i915/display/intel_hdcp.h @@ -8,6 +8,8 @@ #include <linux/types.h> +#define HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS 50 + struct drm_connector; struct drm_connector_state; struct drm_i915_private; @@ -16,16 +18,18 @@ struct intel_connector; struct intel_crtc_state; struct intel_encoder; struct intel_hdcp_shim; +struct intel_digital_port; enum port; enum transcoder; void intel_hdcp_atomic_check(struct drm_connector *connector, struct drm_connector_state *old_state, struct drm_connector_state *new_state); -int intel_hdcp_init(struct intel_connector *connector, enum port port, +int intel_hdcp_init(struct intel_connector *connector, + struct intel_digital_port *dig_port, const struct intel_hdcp_shim *hdcp_shim); int intel_hdcp_enable(struct intel_connector *connector, - enum transcoder cpu_transcoder, u8 content_type); + const struct intel_crtc_state *pipe_config, u8 content_type); int intel_hdcp_disable(struct intel_connector *connector); void intel_hdcp_update_pipe(struct intel_atomic_state *state, struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index c5959590562b..d5f4b40a8460 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -1494,15 +1494,16 @@ static int kbl_repositioning_enc_en_signal(struct intel_connector *connector, usleep_range(25, 50); } - ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder, - false); + ret = intel_ddi_toggle_hdcp_bits(&dig_port->base, cpu_transcoder, + false, TRANS_DDI_HDCP_SIGNALLING); if (ret) { drm_err(&dev_priv->drm, "Disable HDCP signalling failed (%d)\n", ret); return ret; } - ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder, - true); + + ret = intel_ddi_toggle_hdcp_bits(&dig_port->base, cpu_transcoder, + true, TRANS_DDI_HDCP_SIGNALLING); if (ret) { drm_err(&dev_priv->drm, "Enable HDCP signalling failed (%d)\n", ret); @@ -1525,8 +1526,9 @@ int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *dig_port, if (!enable) usleep_range(6, 60); /* Bspec says >= 6us */ - ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder, - enable); + ret = intel_ddi_toggle_hdcp_bits(&dig_port->base, + cpu_transcoder, enable, + TRANS_DDI_HDCP_SIGNALLING); if (ret) { drm_err(&dev_priv->drm, "%s HDCP signalling failed (%d)\n", enable ? "Enable" : "Disable", ret); @@ -1731,7 +1733,8 @@ int intel_hdmi_hdcp2_read_msg(struct intel_digital_port *dig_port, } static -int intel_hdmi_hdcp2_check_link(struct intel_digital_port *dig_port) +int intel_hdmi_hdcp2_check_link(struct intel_digital_port *dig_port, + struct intel_connector *connector) { u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN]; int ret; @@ -3290,7 +3293,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *dig_port, intel_hdmi->attached_connector = intel_connector; if (is_hdcp_supported(dev_priv, port)) { - int ret = intel_hdcp_init(intel_connector, port, + int ret = intel_hdcp_init(intel_connector, dig_port, &intel_hdmi_hdcp_shim); if (ret) drm_dbg_kms(&dev_priv->drm, diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 7a4239d1c241..7587aaefc7a0 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -530,21 +530,21 @@ static u32 intel_panel_compute_brightness(struct intel_connector *connector, return val; } -static u32 lpt_get_backlight(struct intel_connector *connector) +static u32 lpt_get_backlight(struct intel_connector *connector, enum pipe unused) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); return intel_de_read(dev_priv, BLC_PWM_PCH_CTL2) & BACKLIGHT_DUTY_CYCLE_MASK; } -static u32 pch_get_backlight(struct intel_connector *connector) +static u32 pch_get_backlight(struct intel_connector *connector, enum pipe unused) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); return intel_de_read(dev_priv, BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; } -static u32 i9xx_get_backlight(struct intel_connector *connector) +static u32 i9xx_get_backlight(struct intel_connector *connector, enum pipe unused) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); struct intel_panel *panel = &connector->panel; @@ -564,23 +564,17 @@ static u32 i9xx_get_backlight(struct intel_connector *connector) return val; } -static u32 _vlv_get_backlight(struct drm_i915_private *dev_priv, enum pipe pipe) +static u32 vlv_get_backlight(struct intel_connector *connector, enum pipe pipe) { + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B)) return 0; return intel_de_read(dev_priv, VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK; } -static u32 vlv_get_backlight(struct intel_connector *connector) -{ - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - enum pipe pipe = intel_connector_get_pipe(connector); - - return _vlv_get_backlight(dev_priv, pipe); -} - -static u32 bxt_get_backlight(struct intel_connector *connector) +static u32 bxt_get_backlight(struct intel_connector *connector, enum pipe unused) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); struct intel_panel *panel = &connector->panel; @@ -589,7 +583,7 @@ static u32 bxt_get_backlight(struct intel_connector *connector) BXT_BLC_PWM_DUTY(panel->backlight.controller)); } -static u32 ext_pwm_get_backlight(struct intel_connector *connector) +static u32 ext_pwm_get_backlight(struct intel_connector *connector, enum pipe unused) { struct intel_panel *panel = &connector->panel; struct pwm_state state; @@ -1233,7 +1227,7 @@ static u32 intel_panel_get_backlight(struct intel_connector *connector) mutex_lock(&dev_priv->backlight_lock); if (panel->backlight.enabled) { - val = panel->backlight.funcs->get(connector); + val = panel->backlight.funcs->get(connector, intel_connector_get_pipe(connector)); val = intel_panel_compute_brightness(connector, val); } @@ -1646,9 +1640,9 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus !(pch_ctl1 & BLM_PCH_OVERRIDE_ENABLE) && (cpu_ctl2 & BLM_PWM_ENABLE); if (cpu_mode) - val = pch_get_backlight(connector); + val = pch_get_backlight(connector, unused); else - val = lpt_get_backlight(connector); + val = lpt_get_backlight(connector, unused); if (cpu_mode) { drm_dbg_kms(&dev_priv->drm, @@ -1690,7 +1684,7 @@ static int pch_setup_backlight(struct intel_connector *connector, enum pipe unus panel->backlight.min = get_backlight_min_vbt(connector); - val = pch_get_backlight(connector); + val = pch_get_backlight(connector, unused); val = intel_panel_compute_brightness(connector, val); panel->backlight.level = clamp(val, panel->backlight.min, panel->backlight.max); @@ -1731,7 +1725,7 @@ static int i9xx_setup_backlight(struct intel_connector *connector, enum pipe unu panel->backlight.min = get_backlight_min_vbt(connector); - val = i9xx_get_backlight(connector); + val = i9xx_get_backlight(connector, unused); val = intel_panel_compute_brightness(connector, val); panel->backlight.level = clamp(val, panel->backlight.min, panel->backlight.max); @@ -1765,7 +1759,7 @@ static int i965_setup_backlight(struct intel_connector *connector, enum pipe unu panel->backlight.min = get_backlight_min_vbt(connector); - val = i9xx_get_backlight(connector); + val = i9xx_get_backlight(connector, unused); val = intel_panel_compute_brightness(connector, val); panel->backlight.level = clamp(val, panel->backlight.min, panel->backlight.max); @@ -1798,7 +1792,7 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe panel->backlight.min = get_backlight_min_vbt(connector); - val = _vlv_get_backlight(dev_priv, pipe); + val = vlv_get_backlight(connector, pipe); val = intel_panel_compute_brightness(connector, val); panel->backlight.level = clamp(val, panel->backlight.min, panel->backlight.max); @@ -1840,7 +1834,7 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused) panel->backlight.min = get_backlight_min_vbt(connector); - val = bxt_get_backlight(connector); + val = bxt_get_backlight(connector, unused); val = intel_panel_compute_brightness(connector, val); panel->backlight.level = clamp(val, panel->backlight.min, panel->backlight.max); @@ -1880,7 +1874,7 @@ cnp_setup_backlight(struct intel_connector *connector, enum pipe unused) panel->backlight.min = get_backlight_min_vbt(connector); - val = bxt_get_backlight(connector); + val = bxt_get_backlight(connector, unused); val = intel_panel_compute_brightness(connector, val); panel->backlight.level = clamp(val, panel->backlight.min, panel->backlight.max); diff --git a/drivers/gpu/drm/i915/display/intel_pps.c b/drivers/gpu/drm/i915/display/intel_pps.c new file mode 100644 index 000000000000..58eff6289d12 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_pps.c @@ -0,0 +1,1385 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ + +#include "i915_drv.h" +#include "intel_display_types.h" +#include "intel_dp.h" +#include "intel_pps.h" + +static void vlv_steal_power_sequencer(struct drm_i915_private *dev_priv, + enum pipe pipe); + +static void pps_init_delays(struct intel_dp *intel_dp); +static void pps_init_registers(struct intel_dp *intel_dp, bool force_disable_vdd); + +intel_wakeref_t intel_pps_lock(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + intel_wakeref_t wakeref; + + /* + * See intel_pps_reset_all() why we need a power domain reference here. + */ + wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_DISPLAY_CORE); + mutex_lock(&dev_priv->pps_mutex); + + return wakeref; +} + +intel_wakeref_t intel_pps_unlock(struct intel_dp *intel_dp, + intel_wakeref_t wakeref) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + mutex_unlock(&dev_priv->pps_mutex); + intel_display_power_put(dev_priv, POWER_DOMAIN_DISPLAY_CORE, wakeref); + + return 0; +} + +static void +vlv_power_sequencer_kick(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum pipe pipe = intel_dp->pps_pipe; + bool pll_enabled, release_cl_override = false; + enum dpio_phy phy = DPIO_PHY(pipe); + enum dpio_channel ch = vlv_pipe_to_channel(pipe); + u32 DP; + + if (drm_WARN(&dev_priv->drm, + intel_de_read(dev_priv, intel_dp->output_reg) & DP_PORT_EN, + "skipping pipe %c power sequencer kick due to [ENCODER:%d:%s] being active\n", + pipe_name(pipe), dig_port->base.base.base.id, + dig_port->base.base.name)) + return; + + drm_dbg_kms(&dev_priv->drm, + "kicking pipe %c power sequencer for [ENCODER:%d:%s]\n", + pipe_name(pipe), dig_port->base.base.base.id, + dig_port->base.base.name); + + /* Preserve the BIOS-computed detected bit. This is + * supposed to be read-only. + */ + DP = intel_de_read(dev_priv, intel_dp->output_reg) & DP_DETECTED; + DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; + DP |= DP_PORT_WIDTH(1); + DP |= DP_LINK_TRAIN_PAT_1; + + if (IS_CHERRYVIEW(dev_priv)) + DP |= DP_PIPE_SEL_CHV(pipe); + else + DP |= DP_PIPE_SEL(pipe); + + pll_enabled = intel_de_read(dev_priv, DPLL(pipe)) & DPLL_VCO_ENABLE; + + /* + * The DPLL for the pipe must be enabled for this to work. + * So enable temporarily it if it's not already enabled. + */ + if (!pll_enabled) { + release_cl_override = IS_CHERRYVIEW(dev_priv) && + !chv_phy_powergate_ch(dev_priv, phy, ch, true); + + if (vlv_force_pll_on(dev_priv, pipe, vlv_get_dpll(dev_priv))) { + drm_err(&dev_priv->drm, + "Failed to force on pll for pipe %c!\n", + pipe_name(pipe)); + return; + } + } + + /* + * Similar magic as in intel_dp_enable_port(). + * We _must_ do this port enable + disable trick + * to make this power sequencer lock onto the port. + * Otherwise even VDD force bit won't work. + */ + intel_de_write(dev_priv, intel_dp->output_reg, DP); + intel_de_posting_read(dev_priv, intel_dp->output_reg); + + intel_de_write(dev_priv, intel_dp->output_reg, DP | DP_PORT_EN); + intel_de_posting_read(dev_priv, intel_dp->output_reg); + + intel_de_write(dev_priv, intel_dp->output_reg, DP & ~DP_PORT_EN); + intel_de_posting_read(dev_priv, intel_dp->output_reg); + + if (!pll_enabled) { + vlv_force_pll_off(dev_priv, pipe); + + if (release_cl_override) + chv_phy_powergate_ch(dev_priv, phy, ch, false); + } +} + +static enum pipe vlv_find_free_pps(struct drm_i915_private *dev_priv) +{ + struct intel_encoder *encoder; + unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B); + + /* + * We don't have power sequencer currently. + * Pick one that's not used by other ports. + */ + for_each_intel_dp(&dev_priv->drm, encoder) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + if (encoder->type == INTEL_OUTPUT_EDP) { + drm_WARN_ON(&dev_priv->drm, + intel_dp->active_pipe != INVALID_PIPE && + intel_dp->active_pipe != + intel_dp->pps_pipe); + + if (intel_dp->pps_pipe != INVALID_PIPE) + pipes &= ~(1 << intel_dp->pps_pipe); + } else { + drm_WARN_ON(&dev_priv->drm, + intel_dp->pps_pipe != INVALID_PIPE); + + if (intel_dp->active_pipe != INVALID_PIPE) + pipes &= ~(1 << intel_dp->active_pipe); + } + } + + if (pipes == 0) + return INVALID_PIPE; + + return ffs(pipes) - 1; +} + +static enum pipe +vlv_power_sequencer_pipe(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum pipe pipe; + + lockdep_assert_held(&dev_priv->pps_mutex); + + /* We should never land here with regular DP ports */ + drm_WARN_ON(&dev_priv->drm, !intel_dp_is_edp(intel_dp)); + + drm_WARN_ON(&dev_priv->drm, intel_dp->active_pipe != INVALID_PIPE && + intel_dp->active_pipe != intel_dp->pps_pipe); + + if (intel_dp->pps_pipe != INVALID_PIPE) + return intel_dp->pps_pipe; + + pipe = vlv_find_free_pps(dev_priv); + + /* + * Didn't find one. This should not happen since there + * are two power sequencers and up to two eDP ports. + */ + if (drm_WARN_ON(&dev_priv->drm, pipe == INVALID_PIPE)) + pipe = PIPE_A; + + vlv_steal_power_sequencer(dev_priv, pipe); + intel_dp->pps_pipe = pipe; + + drm_dbg_kms(&dev_priv->drm, + "picked pipe %c power sequencer for [ENCODER:%d:%s]\n", + pipe_name(intel_dp->pps_pipe), + dig_port->base.base.base.id, + dig_port->base.base.name); + + /* init power sequencer on this pipe and port */ + pps_init_delays(intel_dp); + pps_init_registers(intel_dp, true); + + /* + * Even vdd force doesn't work until we've made + * the power sequencer lock in on the port. + */ + vlv_power_sequencer_kick(intel_dp); + + return intel_dp->pps_pipe; +} + +static int +bxt_power_sequencer_idx(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + int backlight_controller = dev_priv->vbt.backlight.controller; + + lockdep_assert_held(&dev_priv->pps_mutex); + + /* We should never land here with regular DP ports */ + drm_WARN_ON(&dev_priv->drm, !intel_dp_is_edp(intel_dp)); + + if (!intel_dp->pps_reset) + return backlight_controller; + + intel_dp->pps_reset = false; + + /* + * Only the HW needs to be reprogrammed, the SW state is fixed and + * has been setup during connector init. + */ + pps_init_registers(intel_dp, false); + + return backlight_controller; +} + +typedef bool (*vlv_pipe_check)(struct drm_i915_private *dev_priv, + enum pipe pipe); + +static bool vlv_pipe_has_pp_on(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return intel_de_read(dev_priv, PP_STATUS(pipe)) & PP_ON; +} + +static bool vlv_pipe_has_vdd_on(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return intel_de_read(dev_priv, PP_CONTROL(pipe)) & EDP_FORCE_VDD; +} + +static bool vlv_pipe_any(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return true; +} + +static enum pipe +vlv_initial_pps_pipe(struct drm_i915_private *dev_priv, + enum port port, + vlv_pipe_check pipe_check) +{ + enum pipe pipe; + + for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) { + u32 port_sel = intel_de_read(dev_priv, PP_ON_DELAYS(pipe)) & + PANEL_PORT_SELECT_MASK; + + if (port_sel != PANEL_PORT_SELECT_VLV(port)) + continue; + + if (!pipe_check(dev_priv, pipe)) + continue; + + return pipe; + } + + return INVALID_PIPE; +} + +static void +vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum port port = dig_port->base.port; + + lockdep_assert_held(&dev_priv->pps_mutex); + + /* try to find a pipe with this port selected */ + /* first pick one where the panel is on */ + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_has_pp_on); + /* didn't find one? pick one where vdd is on */ + if (intel_dp->pps_pipe == INVALID_PIPE) + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_has_vdd_on); + /* didn't find one? pick one with just the correct port */ + if (intel_dp->pps_pipe == INVALID_PIPE) + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_any); + + /* didn't find one? just let vlv_power_sequencer_pipe() pick one when needed */ + if (intel_dp->pps_pipe == INVALID_PIPE) { + drm_dbg_kms(&dev_priv->drm, + "no initial power sequencer for [ENCODER:%d:%s]\n", + dig_port->base.base.base.id, + dig_port->base.base.name); + return; + } + + drm_dbg_kms(&dev_priv->drm, + "initial power sequencer for [ENCODER:%d:%s]: pipe %c\n", + dig_port->base.base.base.id, + dig_port->base.base.name, + pipe_name(intel_dp->pps_pipe)); + + pps_init_delays(intel_dp); + pps_init_registers(intel_dp, false); +} + +void intel_pps_reset_all(struct drm_i915_private *dev_priv) +{ + struct intel_encoder *encoder; + + if (drm_WARN_ON(&dev_priv->drm, + !(IS_VALLEYVIEW(dev_priv) || + IS_CHERRYVIEW(dev_priv) || + IS_GEN9_LP(dev_priv)))) + return; + + /* + * We can't grab pps_mutex here due to deadlock with power_domain + * mutex when power_domain functions are called while holding pps_mutex. + * That also means that in order to use pps_pipe the code needs to + * hold both a power domain reference and pps_mutex, and the power domain + * reference get/put must be done while _not_ holding pps_mutex. + * pps_{lock,unlock}() do these steps in the correct order, so one + * should use them always. + */ + + for_each_intel_dp(&dev_priv->drm, encoder) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + drm_WARN_ON(&dev_priv->drm, + intel_dp->active_pipe != INVALID_PIPE); + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + if (IS_GEN9_LP(dev_priv)) + intel_dp->pps_reset = true; + else + intel_dp->pps_pipe = INVALID_PIPE; + } +} + +struct pps_registers { + i915_reg_t pp_ctrl; + i915_reg_t pp_stat; + i915_reg_t pp_on; + i915_reg_t pp_off; + i915_reg_t pp_div; +}; + +static void intel_pps_get_registers(struct intel_dp *intel_dp, + struct pps_registers *regs) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + int pps_idx = 0; + + memset(regs, 0, sizeof(*regs)); + + if (IS_GEN9_LP(dev_priv)) + pps_idx = bxt_power_sequencer_idx(intel_dp); + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + pps_idx = vlv_power_sequencer_pipe(intel_dp); + + regs->pp_ctrl = PP_CONTROL(pps_idx); + regs->pp_stat = PP_STATUS(pps_idx); + regs->pp_on = PP_ON_DELAYS(pps_idx); + regs->pp_off = PP_OFF_DELAYS(pps_idx); + + /* Cycle delay moved from PP_DIVISOR to PP_CONTROL */ + if (IS_GEN9_LP(dev_priv) || INTEL_PCH_TYPE(dev_priv) >= PCH_CNP) + regs->pp_div = INVALID_MMIO_REG; + else + regs->pp_div = PP_DIVISOR(pps_idx); +} + +static i915_reg_t +_pp_ctrl_reg(struct intel_dp *intel_dp) +{ + struct pps_registers regs; + + intel_pps_get_registers(intel_dp, ®s); + + return regs.pp_ctrl; +} + +static i915_reg_t +_pp_stat_reg(struct intel_dp *intel_dp) +{ + struct pps_registers regs; + + intel_pps_get_registers(intel_dp, ®s); + + return regs.pp_stat; +} + +static bool edp_have_panel_power(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + lockdep_assert_held(&dev_priv->pps_mutex); + + if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && + intel_dp->pps_pipe == INVALID_PIPE) + return false; + + return (intel_de_read(dev_priv, _pp_stat_reg(intel_dp)) & PP_ON) != 0; +} + +static bool edp_have_panel_vdd(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + lockdep_assert_held(&dev_priv->pps_mutex); + + if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && + intel_dp->pps_pipe == INVALID_PIPE) + return false; + + return intel_de_read(dev_priv, _pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD; +} + +void intel_pps_check_power_unlocked(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + if (!intel_dp_is_edp(intel_dp)) + return; + + if (!edp_have_panel_power(intel_dp) && !edp_have_panel_vdd(intel_dp)) { + drm_WARN(&dev_priv->drm, 1, + "eDP powered off while attempting aux channel communication.\n"); + drm_dbg_kms(&dev_priv->drm, "Status 0x%08x Control 0x%08x\n", + intel_de_read(dev_priv, _pp_stat_reg(intel_dp)), + intel_de_read(dev_priv, _pp_ctrl_reg(intel_dp))); + } +} + +#define IDLE_ON_MASK (PP_ON | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) +#define IDLE_ON_VALUE (PP_ON | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE) + +#define IDLE_OFF_MASK (PP_ON | PP_SEQUENCE_MASK | 0 | 0) +#define IDLE_OFF_VALUE (0 | PP_SEQUENCE_NONE | 0 | 0) + +#define IDLE_CYCLE_MASK (PP_ON | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK) +#define IDLE_CYCLE_VALUE (0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE) + +static void intel_pps_verify_state(struct intel_dp *intel_dp); + +static void wait_panel_status(struct intel_dp *intel_dp, + u32 mask, + u32 value) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + i915_reg_t pp_stat_reg, pp_ctrl_reg; + + lockdep_assert_held(&dev_priv->pps_mutex); + + intel_pps_verify_state(intel_dp); + + pp_stat_reg = _pp_stat_reg(intel_dp); + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + + drm_dbg_kms(&dev_priv->drm, + "mask %08x value %08x status %08x control %08x\n", + mask, value, + intel_de_read(dev_priv, pp_stat_reg), + intel_de_read(dev_priv, pp_ctrl_reg)); + + if (intel_de_wait_for_register(dev_priv, pp_stat_reg, + mask, value, 5000)) + drm_err(&dev_priv->drm, + "Panel status timeout: status %08x control %08x\n", + intel_de_read(dev_priv, pp_stat_reg), + intel_de_read(dev_priv, pp_ctrl_reg)); + + drm_dbg_kms(&dev_priv->drm, "Wait complete\n"); +} + +static void wait_panel_on(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + + drm_dbg_kms(&i915->drm, "Wait for panel power on\n"); + wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE); +} + +static void wait_panel_off(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + + drm_dbg_kms(&i915->drm, "Wait for panel power off time\n"); + wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE); +} + +static void wait_panel_power_cycle(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + ktime_t panel_power_on_time; + s64 panel_power_off_duration; + + drm_dbg_kms(&i915->drm, "Wait for panel power cycle\n"); + + /* take the difference of currrent time and panel power off time + * and then make panel wait for t11_t12 if needed. */ + panel_power_on_time = ktime_get_boottime(); + panel_power_off_duration = ktime_ms_delta(panel_power_on_time, intel_dp->panel_power_off_time); + + /* When we disable the VDD override bit last we have to do the manual + * wait. */ + if (panel_power_off_duration < (s64)intel_dp->panel_power_cycle_delay) + wait_remaining_ms_from_jiffies(jiffies, + intel_dp->panel_power_cycle_delay - panel_power_off_duration); + + wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE); +} + +void intel_pps_wait_power_cycle(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + + if (!intel_dp_is_edp(intel_dp)) + return; + + with_intel_pps_lock(intel_dp, wakeref) + wait_panel_power_cycle(intel_dp); +} + +static void wait_backlight_on(struct intel_dp *intel_dp) +{ + wait_remaining_ms_from_jiffies(intel_dp->last_power_on, + intel_dp->backlight_on_delay); +} + +static void edp_wait_backlight_off(struct intel_dp *intel_dp) +{ + wait_remaining_ms_from_jiffies(intel_dp->last_backlight_off, + intel_dp->backlight_off_delay); +} + +/* Read the current pp_control value, unlocking the register if it + * is locked + */ + +static u32 ilk_get_pp_control(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + u32 control; + + lockdep_assert_held(&dev_priv->pps_mutex); + + control = intel_de_read(dev_priv, _pp_ctrl_reg(intel_dp)); + if (drm_WARN_ON(&dev_priv->drm, !HAS_DDI(dev_priv) && + (control & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS)) { + control &= ~PANEL_UNLOCK_MASK; + control |= PANEL_UNLOCK_REGS; + } + return control; +} + +/* + * Must be paired with intel_pps_vdd_off_unlocked(). + * Must hold pps_mutex around the whole on/off sequence. + * Can be nested with intel_pps_vdd_{on,off}() calls. + */ +bool intel_pps_vdd_on_unlocked(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + u32 pp; + i915_reg_t pp_stat_reg, pp_ctrl_reg; + bool need_to_disable = !intel_dp->want_panel_vdd; + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (!intel_dp_is_edp(intel_dp)) + return false; + + cancel_delayed_work(&intel_dp->panel_vdd_work); + intel_dp->want_panel_vdd = true; + + if (edp_have_panel_vdd(intel_dp)) + return need_to_disable; + + drm_WARN_ON(&dev_priv->drm, intel_dp->vdd_wakeref); + intel_dp->vdd_wakeref = intel_display_power_get(dev_priv, + intel_aux_power_domain(dig_port)); + + drm_dbg_kms(&dev_priv->drm, "Turning [ENCODER:%d:%s] VDD on\n", + dig_port->base.base.base.id, + dig_port->base.base.name); + + if (!edp_have_panel_power(intel_dp)) + wait_panel_power_cycle(intel_dp); + + pp = ilk_get_pp_control(intel_dp); + pp |= EDP_FORCE_VDD; + + pp_stat_reg = _pp_stat_reg(intel_dp); + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + drm_dbg_kms(&dev_priv->drm, "PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", + intel_de_read(dev_priv, pp_stat_reg), + intel_de_read(dev_priv, pp_ctrl_reg)); + /* + * If the panel wasn't on, delay before accessing aux channel + */ + if (!edp_have_panel_power(intel_dp)) { + drm_dbg_kms(&dev_priv->drm, + "[ENCODER:%d:%s] panel power wasn't enabled\n", + dig_port->base.base.base.id, + dig_port->base.base.name); + msleep(intel_dp->panel_power_up_delay); + } + + return need_to_disable; +} + +/* + * Must be paired with intel_pps_off(). + * Nested calls to these functions are not allowed since + * we drop the lock. Caller must use some higher level + * locking to prevent nested calls from other threads. + */ +void intel_pps_vdd_on(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + bool vdd; + + if (!intel_dp_is_edp(intel_dp)) + return; + + vdd = false; + with_intel_pps_lock(intel_dp, wakeref) + vdd = intel_pps_vdd_on_unlocked(intel_dp); + I915_STATE_WARN(!vdd, "[ENCODER:%d:%s] VDD already requested on\n", + dp_to_dig_port(intel_dp)->base.base.base.id, + dp_to_dig_port(intel_dp)->base.base.name); +} + +static void intel_pps_vdd_off_sync_unlocked(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_digital_port *dig_port = + dp_to_dig_port(intel_dp); + u32 pp; + i915_reg_t pp_stat_reg, pp_ctrl_reg; + + lockdep_assert_held(&dev_priv->pps_mutex); + + drm_WARN_ON(&dev_priv->drm, intel_dp->want_panel_vdd); + + if (!edp_have_panel_vdd(intel_dp)) + return; + + drm_dbg_kms(&dev_priv->drm, "Turning [ENCODER:%d:%s] VDD off\n", + dig_port->base.base.base.id, + dig_port->base.base.name); + + pp = ilk_get_pp_control(intel_dp); + pp &= ~EDP_FORCE_VDD; + + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + pp_stat_reg = _pp_stat_reg(intel_dp); + + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + + /* Make sure sequencer is idle before allowing subsequent activity */ + drm_dbg_kms(&dev_priv->drm, "PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", + intel_de_read(dev_priv, pp_stat_reg), + intel_de_read(dev_priv, pp_ctrl_reg)); + + if ((pp & PANEL_POWER_ON) == 0) + intel_dp->panel_power_off_time = ktime_get_boottime(); + + intel_display_power_put(dev_priv, + intel_aux_power_domain(dig_port), + fetch_and_zero(&intel_dp->vdd_wakeref)); +} + +void intel_pps_vdd_off_sync(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + + if (!intel_dp_is_edp(intel_dp)) + return; + + cancel_delayed_work_sync(&intel_dp->panel_vdd_work); + /* + * vdd might still be enabled due to the delayed vdd off. + * Make sure vdd is actually turned off here. + */ + with_intel_pps_lock(intel_dp, wakeref) + intel_pps_vdd_off_sync_unlocked(intel_dp); +} + +static void edp_panel_vdd_work(struct work_struct *__work) +{ + struct intel_dp *intel_dp = + container_of(to_delayed_work(__work), + struct intel_dp, panel_vdd_work); + intel_wakeref_t wakeref; + + with_intel_pps_lock(intel_dp, wakeref) { + if (!intel_dp->want_panel_vdd) + intel_pps_vdd_off_sync_unlocked(intel_dp); + } +} + +static void edp_panel_vdd_schedule_off(struct intel_dp *intel_dp) +{ + unsigned long delay; + + /* + * Queue the timer to fire a long time from now (relative to the power + * down delay) to keep the panel power up across a sequence of + * operations. + */ + delay = msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5); + schedule_delayed_work(&intel_dp->panel_vdd_work, delay); +} + +/* + * Must be paired with edp_panel_vdd_on(). + * Must hold pps_mutex around the whole on/off sequence. + * Can be nested with intel_pps_vdd_{on,off}() calls. + */ +void intel_pps_vdd_off_unlocked(struct intel_dp *intel_dp, bool sync) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (!intel_dp_is_edp(intel_dp)) + return; + + I915_STATE_WARN(!intel_dp->want_panel_vdd, "[ENCODER:%d:%s] VDD not forced on", + dp_to_dig_port(intel_dp)->base.base.base.id, + dp_to_dig_port(intel_dp)->base.base.name); + + intel_dp->want_panel_vdd = false; + + if (sync) + intel_pps_vdd_off_sync_unlocked(intel_dp); + else + edp_panel_vdd_schedule_off(intel_dp); +} + +void intel_pps_on_unlocked(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + u32 pp; + i915_reg_t pp_ctrl_reg; + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (!intel_dp_is_edp(intel_dp)) + return; + + drm_dbg_kms(&dev_priv->drm, "Turn [ENCODER:%d:%s] panel power on\n", + dp_to_dig_port(intel_dp)->base.base.base.id, + dp_to_dig_port(intel_dp)->base.base.name); + + if (drm_WARN(&dev_priv->drm, edp_have_panel_power(intel_dp), + "[ENCODER:%d:%s] panel power already on\n", + dp_to_dig_port(intel_dp)->base.base.base.id, + dp_to_dig_port(intel_dp)->base.base.name)) + return; + + wait_panel_power_cycle(intel_dp); + + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + pp = ilk_get_pp_control(intel_dp); + if (IS_GEN(dev_priv, 5)) { + /* ILK workaround: disable reset around power sequence */ + pp &= ~PANEL_POWER_RESET; + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + } + + pp |= PANEL_POWER_ON; + if (!IS_GEN(dev_priv, 5)) + pp |= PANEL_POWER_RESET; + + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + + wait_panel_on(intel_dp); + intel_dp->last_power_on = jiffies; + + if (IS_GEN(dev_priv, 5)) { + pp |= PANEL_POWER_RESET; /* restore panel reset bit */ + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + } +} + +void intel_pps_on(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + + if (!intel_dp_is_edp(intel_dp)) + return; + + with_intel_pps_lock(intel_dp, wakeref) + intel_pps_on_unlocked(intel_dp); +} + +void intel_pps_off_unlocked(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + u32 pp; + i915_reg_t pp_ctrl_reg; + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (!intel_dp_is_edp(intel_dp)) + return; + + drm_dbg_kms(&dev_priv->drm, "Turn [ENCODER:%d:%s] panel power off\n", + dig_port->base.base.base.id, dig_port->base.base.name); + + drm_WARN(&dev_priv->drm, !intel_dp->want_panel_vdd, + "Need [ENCODER:%d:%s] VDD to turn off panel\n", + dig_port->base.base.base.id, dig_port->base.base.name); + + pp = ilk_get_pp_control(intel_dp); + /* We need to switch off panel power _and_ force vdd, for otherwise some + * panels get very unhappy and cease to work. */ + pp &= ~(PANEL_POWER_ON | PANEL_POWER_RESET | EDP_FORCE_VDD | + EDP_BLC_ENABLE); + + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + + intel_dp->want_panel_vdd = false; + + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + + wait_panel_off(intel_dp); + intel_dp->panel_power_off_time = ktime_get_boottime(); + + /* We got a reference when we enabled the VDD. */ + intel_display_power_put(dev_priv, + intel_aux_power_domain(dig_port), + fetch_and_zero(&intel_dp->vdd_wakeref)); +} + +void intel_pps_off(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + + if (!intel_dp_is_edp(intel_dp)) + return; + + with_intel_pps_lock(intel_dp, wakeref) + intel_pps_off_unlocked(intel_dp); +} + +/* Enable backlight in the panel power control. */ +void intel_pps_backlight_on(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + intel_wakeref_t wakeref; + + /* + * If we enable the backlight right away following a panel power + * on, we may see slight flicker as the panel syncs with the eDP + * link. So delay a bit to make sure the image is solid before + * allowing it to appear. + */ + wait_backlight_on(intel_dp); + + with_intel_pps_lock(intel_dp, wakeref) { + i915_reg_t pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + u32 pp; + + pp = ilk_get_pp_control(intel_dp); + pp |= EDP_BLC_ENABLE; + + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + } +} + +/* Disable backlight in the panel power control. */ +void intel_pps_backlight_off(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + intel_wakeref_t wakeref; + + if (!intel_dp_is_edp(intel_dp)) + return; + + with_intel_pps_lock(intel_dp, wakeref) { + i915_reg_t pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + u32 pp; + + pp = ilk_get_pp_control(intel_dp); + pp &= ~EDP_BLC_ENABLE; + + intel_de_write(dev_priv, pp_ctrl_reg, pp); + intel_de_posting_read(dev_priv, pp_ctrl_reg); + } + + intel_dp->last_backlight_off = jiffies; + edp_wait_backlight_off(intel_dp); +} + +/* + * Hook for controlling the panel power control backlight through the bl_power + * sysfs attribute. Take care to handle multiple calls. + */ +void intel_pps_backlight_power(struct intel_connector *connector, bool enable) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct intel_dp *intel_dp = intel_attached_dp(connector); + intel_wakeref_t wakeref; + bool is_enabled; + + is_enabled = false; + with_intel_pps_lock(intel_dp, wakeref) + is_enabled = ilk_get_pp_control(intel_dp) & EDP_BLC_ENABLE; + if (is_enabled == enable) + return; + + drm_dbg_kms(&i915->drm, "panel power control backlight %s\n", + enable ? "enable" : "disable"); + + if (enable) + intel_pps_backlight_on(intel_dp); + else + intel_pps_backlight_off(intel_dp); +} + +static void vlv_detach_power_sequencer(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev); + enum pipe pipe = intel_dp->pps_pipe; + i915_reg_t pp_on_reg = PP_ON_DELAYS(pipe); + + drm_WARN_ON(&dev_priv->drm, intel_dp->active_pipe != INVALID_PIPE); + + if (drm_WARN_ON(&dev_priv->drm, pipe != PIPE_A && pipe != PIPE_B)) + return; + + intel_pps_vdd_off_sync_unlocked(intel_dp); + + /* + * VLV seems to get confused when multiple power sequencers + * have the same port selected (even if only one has power/vdd + * enabled). The failure manifests as vlv_wait_port_ready() failing + * CHV on the other hand doesn't seem to mind having the same port + * selected in multiple power sequencers, but let's clear the + * port select always when logically disconnecting a power sequencer + * from a port. + */ + drm_dbg_kms(&dev_priv->drm, + "detaching pipe %c power sequencer from [ENCODER:%d:%s]\n", + pipe_name(pipe), dig_port->base.base.base.id, + dig_port->base.base.name); + intel_de_write(dev_priv, pp_on_reg, 0); + intel_de_posting_read(dev_priv, pp_on_reg); + + intel_dp->pps_pipe = INVALID_PIPE; +} + +static void vlv_steal_power_sequencer(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + struct intel_encoder *encoder; + + lockdep_assert_held(&dev_priv->pps_mutex); + + for_each_intel_dp(&dev_priv->drm, encoder) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + drm_WARN(&dev_priv->drm, intel_dp->active_pipe == pipe, + "stealing pipe %c power sequencer from active [ENCODER:%d:%s]\n", + pipe_name(pipe), encoder->base.base.id, + encoder->base.name); + + if (intel_dp->pps_pipe != pipe) + continue; + + drm_dbg_kms(&dev_priv->drm, + "stealing pipe %c power sequencer from [ENCODER:%d:%s]\n", + pipe_name(pipe), encoder->base.base.id, + encoder->base.name); + + /* make sure vdd is off before we steal it */ + vlv_detach_power_sequencer(intel_dp); + } +} + +void vlv_pps_init(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + + lockdep_assert_held(&dev_priv->pps_mutex); + + drm_WARN_ON(&dev_priv->drm, intel_dp->active_pipe != INVALID_PIPE); + + if (intel_dp->pps_pipe != INVALID_PIPE && + intel_dp->pps_pipe != crtc->pipe) { + /* + * If another power sequencer was being used on this + * port previously make sure to turn off vdd there while + * we still have control of it. + */ + vlv_detach_power_sequencer(intel_dp); + } + + /* + * We may be stealing the power + * sequencer from another port. + */ + vlv_steal_power_sequencer(dev_priv, crtc->pipe); + + intel_dp->active_pipe = crtc->pipe; + + if (!intel_dp_is_edp(intel_dp)) + return; + + /* now it's all ours */ + intel_dp->pps_pipe = crtc->pipe; + + drm_dbg_kms(&dev_priv->drm, + "initializing pipe %c power sequencer for [ENCODER:%d:%s]\n", + pipe_name(intel_dp->pps_pipe), encoder->base.base.id, + encoder->base.name); + + /* init power sequencer on this pipe and port */ + pps_init_delays(intel_dp); + pps_init_registers(intel_dp, true); +} + +static void intel_pps_vdd_sanitize(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (!edp_have_panel_vdd(intel_dp)) + return; + + /* + * The VDD bit needs a power domain reference, so if the bit is + * already enabled when we boot or resume, grab this reference and + * schedule a vdd off, so we don't hold on to the reference + * indefinitely. + */ + drm_dbg_kms(&dev_priv->drm, + "VDD left on by BIOS, adjusting state tracking\n"); + drm_WARN_ON(&dev_priv->drm, intel_dp->vdd_wakeref); + intel_dp->vdd_wakeref = intel_display_power_get(dev_priv, + intel_aux_power_domain(dig_port)); + + edp_panel_vdd_schedule_off(intel_dp); +} + +bool intel_pps_have_power(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + bool have_power = false; + + with_intel_pps_lock(intel_dp, wakeref) { + have_power = edp_have_panel_power(intel_dp) && + edp_have_panel_vdd(intel_dp); + } + + return have_power; +} + +static void pps_init_timestamps(struct intel_dp *intel_dp) +{ + intel_dp->panel_power_off_time = ktime_get_boottime(); + intel_dp->last_power_on = jiffies; + intel_dp->last_backlight_off = jiffies; +} + +static void +intel_pps_readout_hw_state(struct intel_dp *intel_dp, struct edp_power_seq *seq) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + u32 pp_on, pp_off, pp_ctl; + struct pps_registers regs; + + intel_pps_get_registers(intel_dp, ®s); + + pp_ctl = ilk_get_pp_control(intel_dp); + + /* Ensure PPS is unlocked */ + if (!HAS_DDI(dev_priv)) + intel_de_write(dev_priv, regs.pp_ctrl, pp_ctl); + + pp_on = intel_de_read(dev_priv, regs.pp_on); + pp_off = intel_de_read(dev_priv, regs.pp_off); + + /* Pull timing values out of registers */ + seq->t1_t3 = REG_FIELD_GET(PANEL_POWER_UP_DELAY_MASK, pp_on); + seq->t8 = REG_FIELD_GET(PANEL_LIGHT_ON_DELAY_MASK, pp_on); + seq->t9 = REG_FIELD_GET(PANEL_LIGHT_OFF_DELAY_MASK, pp_off); + seq->t10 = REG_FIELD_GET(PANEL_POWER_DOWN_DELAY_MASK, pp_off); + + if (i915_mmio_reg_valid(regs.pp_div)) { + u32 pp_div; + + pp_div = intel_de_read(dev_priv, regs.pp_div); + + seq->t11_t12 = REG_FIELD_GET(PANEL_POWER_CYCLE_DELAY_MASK, pp_div) * 1000; + } else { + seq->t11_t12 = REG_FIELD_GET(BXT_POWER_CYCLE_DELAY_MASK, pp_ctl) * 1000; + } +} + +static void +intel_pps_dump_state(const char *state_name, const struct edp_power_seq *seq) +{ + DRM_DEBUG_KMS("%s t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + state_name, + seq->t1_t3, seq->t8, seq->t9, seq->t10, seq->t11_t12); +} + +static void +intel_pps_verify_state(struct intel_dp *intel_dp) +{ + struct edp_power_seq hw; + struct edp_power_seq *sw = &intel_dp->pps_delays; + + intel_pps_readout_hw_state(intel_dp, &hw); + + if (hw.t1_t3 != sw->t1_t3 || hw.t8 != sw->t8 || hw.t9 != sw->t9 || + hw.t10 != sw->t10 || hw.t11_t12 != sw->t11_t12) { + DRM_ERROR("PPS state mismatch\n"); + intel_pps_dump_state("sw", sw); + intel_pps_dump_state("hw", &hw); + } +} + +static void pps_init_delays(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct edp_power_seq cur, vbt, spec, + *final = &intel_dp->pps_delays; + + lockdep_assert_held(&dev_priv->pps_mutex); + + /* already initialized? */ + if (final->t11_t12 != 0) + return; + + intel_pps_readout_hw_state(intel_dp, &cur); + + intel_pps_dump_state("cur", &cur); + + vbt = dev_priv->vbt.edp.pps; + /* On Toshiba Satellite P50-C-18C system the VBT T12 delay + * of 500ms appears to be too short. Ocassionally the panel + * just fails to power back on. Increasing the delay to 800ms + * seems sufficient to avoid this problem. + */ + if (dev_priv->quirks & QUIRK_INCREASE_T12_DELAY) { + vbt.t11_t12 = max_t(u16, vbt.t11_t12, 1300 * 10); + drm_dbg_kms(&dev_priv->drm, + "Increasing T12 panel delay as per the quirk to %d\n", + vbt.t11_t12); + } + /* T11_T12 delay is special and actually in units of 100ms, but zero + * based in the hw (so we need to add 100 ms). But the sw vbt + * table multiplies it with 1000 to make it in units of 100usec, + * too. */ + vbt.t11_t12 += 100 * 10; + + /* Upper limits from eDP 1.3 spec. Note that we use the clunky units of + * our hw here, which are all in 100usec. */ + spec.t1_t3 = 210 * 10; + spec.t8 = 50 * 10; /* no limit for t8, use t7 instead */ + spec.t9 = 50 * 10; /* no limit for t9, make it symmetric with t8 */ + spec.t10 = 500 * 10; + /* This one is special and actually in units of 100ms, but zero + * based in the hw (so we need to add 100 ms). But the sw vbt + * table multiplies it with 1000 to make it in units of 100usec, + * too. */ + spec.t11_t12 = (510 + 100) * 10; + + intel_pps_dump_state("vbt", &vbt); + + /* Use the max of the register settings and vbt. If both are + * unset, fall back to the spec limits. */ +#define assign_final(field) final->field = (max(cur.field, vbt.field) == 0 ? \ + spec.field : \ + max(cur.field, vbt.field)) + assign_final(t1_t3); + assign_final(t8); + assign_final(t9); + assign_final(t10); + assign_final(t11_t12); +#undef assign_final + +#define get_delay(field) (DIV_ROUND_UP(final->field, 10)) + intel_dp->panel_power_up_delay = get_delay(t1_t3); + intel_dp->backlight_on_delay = get_delay(t8); + intel_dp->backlight_off_delay = get_delay(t9); + intel_dp->panel_power_down_delay = get_delay(t10); + intel_dp->panel_power_cycle_delay = get_delay(t11_t12); +#undef get_delay + + drm_dbg_kms(&dev_priv->drm, + "panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, + intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); + + drm_dbg_kms(&dev_priv->drm, "backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, + intel_dp->backlight_off_delay); + + /* + * We override the HW backlight delays to 1 because we do manual waits + * on them. For T8, even BSpec recommends doing it. For T9, if we + * don't do this, we'll end up waiting for the backlight off delay + * twice: once when we do the manual sleep, and once when we disable + * the panel and wait for the PP_STATUS bit to become zero. + */ + final->t8 = 1; + final->t9 = 1; + + /* + * HW has only a 100msec granularity for t11_t12 so round it up + * accordingly. + */ + final->t11_t12 = roundup(final->t11_t12, 100 * 10); +} + +static void pps_init_registers(struct intel_dp *intel_dp, bool force_disable_vdd) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + u32 pp_on, pp_off, port_sel = 0; + int div = RUNTIME_INFO(dev_priv)->rawclk_freq / 1000; + struct pps_registers regs; + enum port port = dp_to_dig_port(intel_dp)->base.port; + const struct edp_power_seq *seq = &intel_dp->pps_delays; + + lockdep_assert_held(&dev_priv->pps_mutex); + + intel_pps_get_registers(intel_dp, ®s); + + /* + * On some VLV machines the BIOS can leave the VDD + * enabled even on power sequencers which aren't + * hooked up to any port. This would mess up the + * power domain tracking the first time we pick + * one of these power sequencers for use since + * intel_pps_vdd_on_unlocked() would notice that the VDD was + * already on and therefore wouldn't grab the power + * domain reference. Disable VDD first to avoid this. + * This also avoids spuriously turning the VDD on as + * soon as the new power sequencer gets initialized. + */ + if (force_disable_vdd) { + u32 pp = ilk_get_pp_control(intel_dp); + + drm_WARN(&dev_priv->drm, pp & PANEL_POWER_ON, + "Panel power already on\n"); + + if (pp & EDP_FORCE_VDD) + drm_dbg_kms(&dev_priv->drm, + "VDD already on, disabling first\n"); + + pp &= ~EDP_FORCE_VDD; + + intel_de_write(dev_priv, regs.pp_ctrl, pp); + } + + pp_on = REG_FIELD_PREP(PANEL_POWER_UP_DELAY_MASK, seq->t1_t3) | + REG_FIELD_PREP(PANEL_LIGHT_ON_DELAY_MASK, seq->t8); + pp_off = REG_FIELD_PREP(PANEL_LIGHT_OFF_DELAY_MASK, seq->t9) | + REG_FIELD_PREP(PANEL_POWER_DOWN_DELAY_MASK, seq->t10); + + /* Haswell doesn't have any port selection bits for the panel + * power sequencer any more. */ + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + port_sel = PANEL_PORT_SELECT_VLV(port); + } else if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv)) { + switch (port) { + case PORT_A: + port_sel = PANEL_PORT_SELECT_DPA; + break; + case PORT_C: + port_sel = PANEL_PORT_SELECT_DPC; + break; + case PORT_D: + port_sel = PANEL_PORT_SELECT_DPD; + break; + default: + MISSING_CASE(port); + break; + } + } + + pp_on |= port_sel; + + intel_de_write(dev_priv, regs.pp_on, pp_on); + intel_de_write(dev_priv, regs.pp_off, pp_off); + + /* + * Compute the divisor for the pp clock, simply match the Bspec formula. + */ + if (i915_mmio_reg_valid(regs.pp_div)) { + intel_de_write(dev_priv, regs.pp_div, + REG_FIELD_PREP(PP_REFERENCE_DIVIDER_MASK, (100 * div) / 2 - 1) | REG_FIELD_PREP(PANEL_POWER_CYCLE_DELAY_MASK, DIV_ROUND_UP(seq->t11_t12, 1000))); + } else { + u32 pp_ctl; + + pp_ctl = intel_de_read(dev_priv, regs.pp_ctrl); + pp_ctl &= ~BXT_POWER_CYCLE_DELAY_MASK; + pp_ctl |= REG_FIELD_PREP(BXT_POWER_CYCLE_DELAY_MASK, DIV_ROUND_UP(seq->t11_t12, 1000)); + intel_de_write(dev_priv, regs.pp_ctrl, pp_ctl); + } + + drm_dbg_kms(&dev_priv->drm, + "panel power sequencer register settings: PP_ON %#x, PP_OFF %#x, PP_DIV %#x\n", + intel_de_read(dev_priv, regs.pp_on), + intel_de_read(dev_priv, regs.pp_off), + i915_mmio_reg_valid(regs.pp_div) ? + intel_de_read(dev_priv, regs.pp_div) : + (intel_de_read(dev_priv, regs.pp_ctrl) & BXT_POWER_CYCLE_DELAY_MASK)); +} + +static void intel_dp_pps_init(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + vlv_initial_power_sequencer_setup(intel_dp); + } else { + pps_init_delays(intel_dp); + pps_init_registers(intel_dp, false); + } +} + +void intel_pps_encoder_reset(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + + if (!intel_dp_is_edp(intel_dp)) + return; + + with_intel_pps_lock(intel_dp, wakeref) { + /* + * Reinit the power sequencer, in case BIOS did something nasty + * with it. + */ + intel_dp_pps_init(intel_dp); + intel_pps_vdd_sanitize(intel_dp); + } +} + +void intel_pps_init(struct intel_dp *intel_dp) +{ + intel_wakeref_t wakeref; + + INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, edp_panel_vdd_work); + + with_intel_pps_lock(intel_dp, wakeref) { + pps_init_timestamps(intel_dp); + intel_dp_pps_init(intel_dp); + intel_pps_vdd_sanitize(intel_dp); + } +} diff --git a/drivers/gpu/drm/i915/display/intel_pps.h b/drivers/gpu/drm/i915/display/intel_pps.h new file mode 100644 index 000000000000..22045c5cdc86 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_pps.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef __INTEL_PPS_H__ +#define __INTEL_PPS_H__ + +#include <linux/types.h> + +#include "intel_wakeref.h" + +struct drm_i915_private; +struct intel_connector; +struct intel_crtc_state; +struct intel_dp; +struct intel_encoder; + +intel_wakeref_t intel_pps_lock(struct intel_dp *intel_dp); +intel_wakeref_t intel_pps_unlock(struct intel_dp *intel_dp, intel_wakeref_t wakeref); + +#define with_intel_pps_lock(dp, wf) \ + for ((wf) = intel_pps_lock(dp); (wf); (wf) = intel_pps_unlock((dp), (wf))) + +void intel_pps_backlight_on(struct intel_dp *intel_dp); +void intel_pps_backlight_off(struct intel_dp *intel_dp); +void intel_pps_backlight_power(struct intel_connector *connector, bool enable); + +bool intel_pps_vdd_on_unlocked(struct intel_dp *intel_dp); +void intel_pps_vdd_off_unlocked(struct intel_dp *intel_dp, bool sync); +void intel_pps_on_unlocked(struct intel_dp *intel_dp); +void intel_pps_off_unlocked(struct intel_dp *intel_dp); +void intel_pps_check_power_unlocked(struct intel_dp *intel_dp); + +void intel_pps_vdd_on(struct intel_dp *intel_dp); +void intel_pps_on(struct intel_dp *intel_dp); +void intel_pps_off(struct intel_dp *intel_dp); +void intel_pps_vdd_off_sync(struct intel_dp *intel_dp); +bool intel_pps_have_power(struct intel_dp *intel_dp); +void intel_pps_wait_power_cycle(struct intel_dp *intel_dp); + +void intel_pps_init(struct intel_dp *intel_dp); +void intel_pps_encoder_reset(struct intel_dp *intel_dp); +void intel_pps_reset_all(struct drm_i915_private *i915); + +void vlv_pps_init(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state); + +#endif /* __INTEL_PPS_H__ */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e3d58299b323..8376cff5ba86 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1762,6 +1762,11 @@ tgl_revids_get(struct drm_i915_private *dev_priv) #define INTEL_DISPLAY_ENABLED(dev_priv) \ (drm_WARN_ON(&(dev_priv)->drm, !HAS_DISPLAY(dev_priv)), !(dev_priv)->params.disable_display) +static inline bool run_as_guest(void) +{ + return !hypervisor_is_type(X86_HYPER_NATIVE); +} + static inline bool intel_vtd_active(void) { #ifdef CONFIG_INTEL_IOMMU @@ -1770,7 +1775,7 @@ static inline bool intel_vtd_active(void) #endif /* Running as a guest, we assume the host is enforcing VT'd */ - return !hypervisor_is_type(X86_HYPER_NATIVE); + return run_as_guest(); } static inline bool intel_scanout_needs_vtd_wa(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 1d8ba10847ca..249a81575b9d 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -9872,6 +9872,7 @@ enum skl_power_gate { _PORTD_HDCP2_BASE, \ _PORTE_HDCP2_BASE, \ _PORTF_HDCP2_BASE) + (x)) + #define PORT_HDCP2_AUTH(port) _PORT_HDCP2_BASE(port, 0x98) #define _TRANSA_HDCP2_AUTH 0x66498 #define _TRANSB_HDCP2_AUTH 0x66598 @@ -9911,6 +9912,44 @@ enum skl_power_gate { TRANS_HDCP2_STATUS(trans) : \ PORT_HDCP2_STATUS(port)) +#define _PIPEA_HDCP2_STREAM_STATUS 0x668C0 +#define _PIPEB_HDCP2_STREAM_STATUS 0x665C0 +#define _PIPEC_HDCP2_STREAM_STATUS 0x666C0 +#define _PIPED_HDCP2_STREAM_STATUS 0x667C0 +#define PIPE_HDCP2_STREAM_STATUS(pipe) _MMIO(_PICK((pipe), \ + _PIPEA_HDCP2_STREAM_STATUS, \ + _PIPEB_HDCP2_STREAM_STATUS, \ + _PIPEC_HDCP2_STREAM_STATUS, \ + _PIPED_HDCP2_STREAM_STATUS)) + +#define _TRANSA_HDCP2_STREAM_STATUS 0x664C0 +#define _TRANSB_HDCP2_STREAM_STATUS 0x665C0 +#define TRANS_HDCP2_STREAM_STATUS(trans) _MMIO_TRANS(trans, \ + _TRANSA_HDCP2_STREAM_STATUS, \ + _TRANSB_HDCP2_STREAM_STATUS) +#define STREAM_ENCRYPTION_STATUS BIT(31) +#define STREAM_TYPE_STATUS BIT(30) +#define HDCP2_STREAM_STATUS(dev_priv, trans, port) \ + (INTEL_GEN(dev_priv) >= 12 ? \ + TRANS_HDCP2_STREAM_STATUS(trans) : \ + PIPE_HDCP2_STREAM_STATUS(pipe)) + +#define _PORTA_HDCP2_AUTH_STREAM 0x66F00 +#define _PORTB_HDCP2_AUTH_STREAM 0x66F04 +#define PORT_HDCP2_AUTH_STREAM(port) _MMIO_PORT(port, \ + _PORTA_HDCP2_AUTH_STREAM, \ + _PORTB_HDCP2_AUTH_STREAM) +#define _TRANSA_HDCP2_AUTH_STREAM 0x66F00 +#define _TRANSB_HDCP2_AUTH_STREAM 0x66F04 +#define TRANS_HDCP2_AUTH_STREAM(trans) _MMIO_TRANS(trans, \ + _TRANSA_HDCP2_AUTH_STREAM, \ + _TRANSB_HDCP2_AUTH_STREAM) +#define AUTH_STREAM_TYPE BIT(31) +#define HDCP2_AUTH_STREAM(dev_priv, trans, port) \ + (INTEL_GEN(dev_priv) >= 12 ? \ + TRANS_HDCP2_AUTH_STREAM(trans) : \ + PORT_HDCP2_AUTH_STREAM(port)) + /* Per-pipe DDI Function Control */ #define _TRANS_DDI_FUNC_CTL_A 0x60400 #define _TRANS_DDI_FUNC_CTL_B 0x61400 @@ -9960,6 +9999,7 @@ enum skl_power_gate { #define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1 << 8) #define TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1 << 7) #define TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1 << 6) +#define TRANS_DDI_HDCP_SELECT REG_BIT(5) #define TRANS_DDI_BFI_ENABLE (1 << 4) #define TRANS_DDI_HIGH_TMDS_CHAR_RATE (1 << 4) #define TRANS_DDI_HDMI_SCRAMBLING (1 << 0) diff --git a/drivers/gpu/drm/i915/intel_pch.c b/drivers/gpu/drm/i915/intel_pch.c index f31c0dabd0cc..ecaf314d60b6 100644 --- a/drivers/gpu/drm/i915/intel_pch.c +++ b/drivers/gpu/drm/i915/intel_pch.c @@ -143,8 +143,9 @@ static bool intel_is_virt_pch(unsigned short id, sdevice == PCI_SUBDEVICE_ID_QEMU)); } -static unsigned short -intel_virt_detect_pch(const struct drm_i915_private *dev_priv) +static void +intel_virt_detect_pch(const struct drm_i915_private *dev_priv, + unsigned short *pch_id, enum intel_pch *pch_type) { unsigned short id = 0; @@ -181,12 +182,21 @@ intel_virt_detect_pch(const struct drm_i915_private *dev_priv) else drm_dbg_kms(&dev_priv->drm, "Assuming no PCH\n"); - return id; + *pch_type = intel_pch_type(dev_priv, id); + + /* Sanity check virtual PCH id */ + if (drm_WARN_ON(&dev_priv->drm, + id && *pch_type == PCH_NONE)) + id = 0; + + *pch_id = id; } void intel_detect_pch(struct drm_i915_private *dev_priv) { struct pci_dev *pch = NULL; + unsigned short id; + enum intel_pch pch_type; /* DG1 has south engine display on the same PCI device */ if (IS_DG1(dev_priv)) { @@ -206,9 +216,6 @@ void intel_detect_pch(struct drm_i915_private *dev_priv) * of only checking the first one. */ while ((pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, pch))) { - unsigned short id; - enum intel_pch pch_type; - if (pch->vendor != PCI_VENDOR_ID_INTEL) continue; @@ -221,14 +228,7 @@ void intel_detect_pch(struct drm_i915_private *dev_priv) break; } else if (intel_is_virt_pch(id, pch->subsystem_vendor, pch->subsystem_device)) { - id = intel_virt_detect_pch(dev_priv); - pch_type = intel_pch_type(dev_priv, id); - - /* Sanity check virtual PCH id */ - if (drm_WARN_ON(&dev_priv->drm, - id && pch_type == PCH_NONE)) - id = 0; - + intel_virt_detect_pch(dev_priv, &id, &pch_type); dev_priv->pch_type = pch_type; dev_priv->pch_id = id; break; @@ -244,10 +244,15 @@ void intel_detect_pch(struct drm_i915_private *dev_priv) "Display disabled, reverting to NOP PCH\n"); dev_priv->pch_type = PCH_NOP; dev_priv->pch_id = 0; + } else if (!pch) { + if (run_as_guest() && HAS_DISPLAY(dev_priv)) { + intel_virt_detect_pch(dev_priv, &id, &pch_type); + dev_priv->pch_type = pch_type; + dev_priv->pch_id = id; + } else { + drm_dbg_kms(&dev_priv->drm, "No PCH found.\n"); + } } - if (!pch) - drm_dbg_kms(&dev_priv->drm, "No PCH found.\n"); - pci_dev_put(pch); } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index bbc73df7f753..992fce8b8d13 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -7103,24 +7103,26 @@ static void icl_init_clock_gating(struct drm_i915_private *dev_priv) 0, CNL_DELAY_PMRSP); } -static void tgl_init_clock_gating(struct drm_i915_private *dev_priv) +static void gen12lp_init_clock_gating(struct drm_i915_private *dev_priv) { - /* Wa_1409120013:tgl */ + /* Wa_1409120013:tgl,rkl,adl_s,dg1 */ intel_uncore_write(&dev_priv->uncore, ILK_DPFC_CHICKEN, - ILK_DPFC_CHICKEN_COMP_DUMMY_PIXEL); + ILK_DPFC_CHICKEN_COMP_DUMMY_PIXEL); /* Wa_1409825376:tgl (pre-prod)*/ if (IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_B1)) intel_uncore_write(&dev_priv->uncore, GEN9_CLKGATE_DIS_3, intel_uncore_read(&dev_priv->uncore, GEN9_CLKGATE_DIS_3) | TGL_VRH_GATING_DIS); - /* Wa_14011059788:tgl */ + /* Wa_14011059788:tgl,rkl,adl_s,dg1 */ intel_uncore_rmw(&dev_priv->uncore, GEN10_DFR_RATIO_EN_AND_CHICKEN, 0, DFR_DISABLE); } static void dg1_init_clock_gating(struct drm_i915_private *dev_priv) { + gen12lp_init_clock_gating(dev_priv); + /* Wa_1409836686:dg1[a0] */ if (IS_DG1_REVID(dev_priv, DG1_REVID_A0, DG1_REVID_A0)) intel_uncore_write(&dev_priv->uncore, GEN9_CLKGATE_DIS_3, intel_uncore_read(&dev_priv->uncore, GEN9_CLKGATE_DIS_3) | @@ -7583,7 +7585,7 @@ void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv) if (IS_DG1(dev_priv)) dev_priv->display.init_clock_gating = dg1_init_clock_gating; else if (IS_GEN(dev_priv, 12)) - dev_priv->display.init_clock_gating = tgl_init_clock_gating; + dev_priv->display.init_clock_gating = gen12lp_init_clock_gating; else if (IS_GEN(dev_priv, 11)) dev_priv->display.init_clock_gating = icl_init_clock_gating; else if (IS_CANNONLAKE(dev_priv)) diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 9ae9669e46ea..3506a3534294 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -569,8 +569,7 @@ static int mei_hdcp_verify_mprime(struct device *dev, 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 = ME_HDCP_STATUS_SUCCESS; - verify_mprime_in->header.buffer_len = - WIRED_CMD_BUF_LEN_REPEATER_AUTH_STREAM_REQ_MIN_IN; + 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->fw_ddi; diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index fe58dbb46962..ac22c246542a 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -101,11 +101,11 @@ /* Following Macros take a byte at a time for bit(s) masking */ /* - * TODO: This has to be changed for DP MST, as multiple stream on - * same port is possible. - * For HDCP2.2 on HDMI and DP SST this value is always 1. + * TODO: HDCP_2_2_MAX_CONTENT_STREAMS_CNT is based upon actual + * H/W MST streams capacity. + * This required to be moved out to platform specific header. */ -#define HDCP_2_2_MAX_CONTENT_STREAMS_CNT 1 +#define HDCP_2_2_MAX_CONTENT_STREAMS_CNT 4 #define HDCP_2_2_TXCAP_MASK_LEN 2 #define HDCP_2_2_RXCAPS_LEN 3 #define HDCP_2_2_RX_REPEATER(x) ((x) & BIT(0)) |