From: Jani Nikula Date: Fri, 6 Sep 2013 04:40:05 +0000 (+0300) Subject: drm/i915: add support for per-pipe power sequencing on vlv X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=bf13e81b904a37d94d83dd6c3b53a147719a3ead;p=linux-beck.git drm/i915: add support for per-pipe power sequencing on vlv VLV has per-pipe PP registers. Set up power sequencing on mode set. The connector init time setup is problematic, since we don't have a pipe at that time. Cook up something. v2: - use vlv_power_sequencer_pipe() also in _pp_{ctrl,stat}_reg() - use PANEL_PORT_SELECT_DPC_VLV (Ville) v3: make checkpatch happier Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä [danvet: Make checkpatch a bit more happier still ...] Signed-off-by: Daniel Vetter --- diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d7d12c3d1986..8c70a833950d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -237,24 +237,77 @@ intel_hrawclk(struct drm_device *dev) } } +static void +intel_dp_init_panel_power_sequencer(struct drm_device *dev, + struct intel_dp *intel_dp, + struct edp_power_seq *out); +static void +intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, + struct intel_dp *intel_dp, + struct edp_power_seq *out); + +static enum pipe +vlv_power_sequencer_pipe(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_crtc *crtc = intel_dig_port->base.base.crtc; + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_dig_port->port; + enum pipe pipe; + + /* modeset should have pipe */ + if (crtc) + return to_intel_crtc(crtc)->pipe; + + /* init time, try to find a pipe with this port selected */ + for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) { + u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) & + PANEL_PORT_SELECT_MASK; + if (port_sel == PANEL_PORT_SELECT_DPB_VLV && port == PORT_B) + return pipe; + if (port_sel == PANEL_PORT_SELECT_DPC_VLV && port == PORT_C) + return pipe; + } + + /* shrug */ + return PIPE_A; +} + +static u32 _pp_ctrl_reg(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + + if (HAS_PCH_SPLIT(dev)) + return PCH_PP_CONTROL; + else + return VLV_PIPE_PP_CONTROL(vlv_power_sequencer_pipe(intel_dp)); +} + +static u32 _pp_stat_reg(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + + if (HAS_PCH_SPLIT(dev)) + return PCH_PP_STATUS; + else + return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp)); +} + static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp_stat_reg; - pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; - return (I915_READ(pp_stat_reg) & PP_ON) != 0; + return (I915_READ(_pp_stat_reg(intel_dp)) & PP_ON) != 0; } static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp_ctrl_reg; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; - return (I915_READ(pp_ctrl_reg) & EDP_FORCE_VDD) != 0; + return (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0; } static void @@ -262,19 +315,15 @@ intel_dp_check_edp(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp_stat_reg, pp_ctrl_reg; if (!is_edp(intel_dp)) return; - pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; - if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) { WARN(1, "eDP powered off while attempting aux channel communication.\n"); DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n", - I915_READ(pp_stat_reg), - I915_READ(pp_ctrl_reg)); + I915_READ(_pp_stat_reg(intel_dp)), + I915_READ(_pp_ctrl_reg(intel_dp))); } } @@ -948,8 +997,8 @@ static void ironlake_wait_panel_status(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; u32 pp_stat_reg, pp_ctrl_reg; - pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + pp_stat_reg = _pp_stat_reg(intel_dp); + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); DRM_DEBUG_KMS("mask %08x value %08x status %08x control %08x\n", mask, value, @@ -991,11 +1040,8 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; u32 control; - u32 pp_ctrl_reg; - - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; - control = I915_READ(pp_ctrl_reg); + control = I915_READ(_pp_ctrl_reg(intel_dp)); control &= ~PANEL_UNLOCK_MASK; control |= PANEL_UNLOCK_REGS; return control; @@ -1028,8 +1074,8 @@ void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) pp = ironlake_get_pp_control(intel_dp); pp |= EDP_FORCE_VDD; - pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + pp_stat_reg = _pp_stat_reg(intel_dp); + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); @@ -1057,8 +1103,8 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) pp = ironlake_get_pp_control(intel_dp); pp &= ~EDP_FORCE_VDD; - pp_stat_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_STATUS : PCH_PP_STATUS; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + pp_stat_reg = _pp_ctrl_reg(intel_dp); + pp_ctrl_reg = _pp_stat_reg(intel_dp); I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); @@ -1123,20 +1169,19 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp) ironlake_wait_panel_power_cycle(intel_dp); + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); pp = ironlake_get_pp_control(intel_dp); if (IS_GEN5(dev)) { /* ILK workaround: disable reset around power sequence */ pp &= ~PANEL_POWER_RESET; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); } pp |= POWER_TARGET_ON; if (!IS_GEN5(dev)) pp |= PANEL_POWER_RESET; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; - I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); @@ -1144,8 +1189,8 @@ void ironlake_edp_panel_on(struct intel_dp *intel_dp) if (IS_GEN5(dev)) { pp |= PANEL_POWER_RESET; /* restore panel reset bit */ - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); } } @@ -1168,7 +1213,7 @@ void ironlake_edp_panel_off(struct intel_dp *intel_dp) * panels get very unhappy and cease to work. */ pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE); - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); @@ -1201,7 +1246,7 @@ void ironlake_edp_backlight_on(struct intel_dp *intel_dp) pp = ironlake_get_pp_control(intel_dp); pp |= EDP_BLC_ENABLE; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); @@ -1225,7 +1270,7 @@ void ironlake_edp_backlight_off(struct intel_dp *intel_dp) pp = ironlake_get_pp_control(intel_dp); pp &= ~EDP_BLC_ENABLE; - pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); @@ -1752,6 +1797,7 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); int port = vlv_dport_to_channel(dport); int pipe = intel_crtc->pipe; + struct edp_power_seq power_seq; u32 val; mutex_lock(&dev_priv->dpio_lock); @@ -1769,6 +1815,11 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); + /* init power sequencer on this pipe and port */ + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); + intel_enable_dp(encoder); vlv_wait_port_ready(dev_priv, port); @@ -3161,24 +3212,26 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; struct edp_power_seq cur, vbt, spec, final; u32 pp_on, pp_off, pp_div, pp; - int pp_control_reg, pp_on_reg, pp_off_reg, pp_div_reg; + int pp_ctrl_reg, pp_on_reg, pp_off_reg, pp_div_reg; if (HAS_PCH_SPLIT(dev)) { - pp_control_reg = PCH_PP_CONTROL; + pp_ctrl_reg = PCH_PP_CONTROL; pp_on_reg = PCH_PP_ON_DELAYS; pp_off_reg = PCH_PP_OFF_DELAYS; pp_div_reg = PCH_PP_DIVISOR; } else { - pp_control_reg = PIPEA_PP_CONTROL; - pp_on_reg = PIPEA_PP_ON_DELAYS; - pp_off_reg = PIPEA_PP_OFF_DELAYS; - pp_div_reg = PIPEA_PP_DIVISOR; + enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); + + pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe); + pp_on_reg = VLV_PIPE_PP_ON_DELAYS(pipe); + pp_off_reg = VLV_PIPE_PP_OFF_DELAYS(pipe); + pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe); } /* Workaround: Need to write PP_CONTROL with the unlock key as * the very first thing. */ pp = ironlake_get_pp_control(intel_dp); - I915_WRITE(pp_control_reg, pp); + I915_WRITE(pp_ctrl_reg, pp); pp_on = I915_READ(pp_on_reg); pp_off = I915_READ(pp_off_reg); @@ -3266,9 +3319,11 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, pp_off_reg = PCH_PP_OFF_DELAYS; pp_div_reg = PCH_PP_DIVISOR; } else { - pp_on_reg = PIPEA_PP_ON_DELAYS; - pp_off_reg = PIPEA_PP_OFF_DELAYS; - pp_div_reg = PIPEA_PP_DIVISOR; + enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); + + pp_on_reg = VLV_PIPE_PP_ON_DELAYS(pipe); + pp_off_reg = VLV_PIPE_PP_OFF_DELAYS(pipe); + pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe); } /* And finally store the new values in the power sequencer. */ @@ -3285,7 +3340,10 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, /* Haswell doesn't have any port selection bits for the panel * power sequencer any more. */ if (IS_VALLEYVIEW(dev)) { - port_sel = I915_READ(pp_on_reg) & 0xc0000000; + if (dp_to_dig_port(intel_dp)->port == PORT_B) + port_sel = PANEL_PORT_SELECT_DPB_VLV; + else + port_sel = PANEL_PORT_SELECT_DPC_VLV; } else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { if (dp_to_dig_port(intel_dp)->port == PORT_A) port_sel = PANEL_PORT_SELECT_DPA;