@@ -12632,6 +12632,7 @@ static void intel_init_display(struct drm_device *dev)
}
intel_panel_init_backlight_funcs(dev);
+ intel_panel_init_pps_funcs(dev);
mutex_init(&dev_priv->pps_mutex);
}
@@ -322,7 +322,7 @@ static void pps_unlock(struct intel_dp *intel_dp)
intel_display_power_put(dev_priv, power_domain);
}
-static enum pipe
+enum pipe
vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -419,13 +419,12 @@ vlv_initial_pps_pipe(struct drm_i915_private *dev_priv,
return INVALID_PIPE;
}
-static void
-vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp)
+void
+vlv_initial_power_sequencer_setup(struct intel_digital_port *intel_dig_port)
{
- struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
struct drm_device *dev = intel_dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct edp_power_seq power_seq;
enum port port = intel_dig_port->port;
lockdep_assert_held(&dev_priv->pps_mutex);
@@ -452,10 +451,6 @@ vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp)
DRM_DEBUG_KMS("initial power sequencer for port %c: pipe %c\n",
port_name(port), pipe_name(intel_dp->pps_pipe));
-
- intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
- &power_seq);
}
void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv)
@@ -1366,7 +1361,7 @@ static void edp_wait_backlight_off(struct intel_dp *intel_dp)
* is locked
*/
-static u32 ironlake_get_pp_control(struct intel_dp *intel_dp)
+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;
@@ -5034,9 +5029,9 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
return false;
}
- /* We now know it's not a ghost, init power sequence regs. */
pps_lock(intel_dp);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq);
+ intel_dp_init_panel_power_timestamps(intel_dp);
+ intel_panel_setup_panel_power_sequencer(intel_connector);
pps_unlock(intel_dp);
mutex_lock(&dev->mode_config.mutex);
@@ -5176,18 +5171,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
BUG();
}
- if (is_edp(intel_dp)) {
- pps_lock(intel_dp);
- if (IS_VALLEYVIEW(dev)) {
- vlv_initial_power_sequencer_setup(intel_dp);
- } else {
- intel_dp_init_panel_power_timestamps(intel_dp);
- intel_dp_init_panel_power_sequencer(dev, intel_dp,
- &power_seq);
- }
- pps_unlock(intel_dp);
- }
-
intel_dp_aux_init(intel_dp, intel_connector);
/* init MST on ports that can support it */
@@ -989,7 +989,12 @@ void intel_dp_mst_suspend(struct drm_device *dev);
void intel_dp_mst_resume(struct drm_device *dev);
int intel_dp_max_link_bw(struct intel_dp *intel_dp);
void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
+enum pipe vlv_power_sequencer_pipe(struct intel_dp *intel_dp);
+void vlv_initial_power_sequencer_setup(
+ struct intel_digital_port *intel_dig_port);
void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv);
+u32 ironlake_get_pp_control(struct intel_dp *intel_dp);
+
/* intel_dp_mst.c */
int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
@@ -1089,6 +1094,9 @@ extern struct drm_display_mode *intel_find_panel_downclock(
struct drm_device *dev,
struct drm_display_mode *fixed_mode,
struct drm_connector *connector);
+void intel_panel_setup_panel_power_sequencer(
+ struct intel_connector *connector);
+void intel_panel_init_pps_funcs(struct drm_device *dev);
/* intel_pm.c */
void intel_init_clock_gating(struct drm_device *dev);
@@ -1344,6 +1344,159 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev)
}
}
+
+static struct edp_power_seq pch_get_pps_registers(
+ struct intel_connector *connector,
+ u32 pp_ctrl_reg, u32 pp_on_reg,
+ u32 pp_off_reg, u32 pp_div_reg)
+{
+ struct drm_device *dev = connector->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_encoder *encoder = connector->base.encoder;
+ struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
+ struct edp_power_seq cur;
+ u32 pp_on, pp_off, pp_div, pp;
+
+ pp_on = I915_READ(pp_on_reg);
+ pp_off = I915_READ(pp_off_reg);
+ pp_div = I915_READ(pp_div_reg);
+
+ /* Workaround: Need to write PP_CONTROL with the unlock key as
+ * the very first thing. */
+ pp = ironlake_get_pp_control(&intel_dig_port->dp);
+ I915_WRITE(pp_ctrl_reg, pp);
+
+ /* Pull timing values out of registers */
+ cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >>
+ PANEL_POWER_UP_DELAY_SHIFT;
+
+ cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >>
+ PANEL_LIGHT_ON_DELAY_SHIFT;
+
+ cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >>
+ PANEL_LIGHT_OFF_DELAY_SHIFT;
+
+ cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >>
+ PANEL_POWER_DOWN_DELAY_SHIFT;
+
+ cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >>
+ PANEL_POWER_CYCLE_DELAY_SHIFT) * 1000;
+
+ DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+ cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
+
+ return cur;
+}
+
+static struct edp_power_seq pch_setup_pps(struct intel_connector *connector)
+{
+ struct drm_device *dev = connector->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int pp_ctrl_reg, pp_on_reg, pp_off_reg, pp_div_reg;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ 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;
+
+ return pch_get_pps_registers(connector, pp_ctrl_reg, pp_on_reg,
+ pp_off_reg, pp_div_reg);
+}
+
+static struct edp_power_seq vlv_setup_pps(struct intel_connector *connector)
+{
+ struct drm_device *dev = connector->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_encoder *encoder = connector->base.encoder;
+ struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
+ enum pipe pipe = vlv_power_sequencer_pipe(&intel_dig_port->dp);
+ int pp_ctrl_reg, pp_on_reg, pp_off_reg, pp_div_reg;
+
+ vlv_initial_power_sequencer_setup(intel_dig_port);
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ 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);
+
+ return pch_get_pps_registers(connector, pp_ctrl_reg, pp_on_reg,
+ pp_off_reg, pp_div_reg);
+}
+
+void intel_panel_setup_panel_power_sequencer(struct intel_connector *connector)
+{
+ struct drm_device *dev = connector->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_panel *panel = &connector->panel;
+ struct edp_power_seq cur, spec, vbt, final;
+
+ /* Get chip specific register values */
+ cur = dev_priv->display.setup_panel_power_seq(connector);
+
+ vbt = dev_priv->vbt.edp_pps;
+
+ /* 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;
+
+ DRM_DEBUG_KMS("vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
+ vbt.t1_t3, vbt.t8, vbt.t9, vbt.t10, vbt.t11_t12);
+
+ /* 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))
+ panel->pps.panel_power_up_delay = get_delay(t1_t3);
+ panel->pps.backlight_on_delay = get_delay(t8);
+ panel->pps.backlight_off_delay = get_delay(t9);
+ panel->pps.panel_power_down_delay = get_delay(t10);
+ panel->pps.panel_power_cycle_delay = get_delay(t11_t12);
+#undef get_delay
+
+ DRM_DEBUG_KMS("panel power up delay %d, power down delay %d,"
+ "power cycle delay %d\n",
+ panel->pps.panel_power_up_delay,
+ panel->pps.panel_power_down_delay,
+ panel->pps.panel_power_cycle_delay);
+
+ DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
+ panel->pps.backlight_on_delay,
+ panel->pps.backlight_off_delay);
+
+}
+
+/* Setup chip specific PPS functions */
+void intel_panel_init_pps_funcs(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (IS_VALLEYVIEW(dev))
+ dev_priv->display.setup_panel_power_seq = vlv_setup_pps;
+ else
+ dev_priv->display.setup_panel_power_seq = pch_setup_pps;
+}
+
int intel_panel_init(struct intel_panel *panel,
struct drm_display_mode *fixed_mode,
struct drm_display_mode *downclock_mode)
Defining functions equivalent to intel_dp_panel_power_sequencer. In the setup part, the differnce between vlv and other platforms is only w.r.t registers. Other parts like reading VBT are common. Signed-off-by: Vandana Kannan <vandana.kannan@intel.com> --- drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/i915/intel_dp.c | 31 ++----- drivers/gpu/drm/i915/intel_drv.h | 8 ++ drivers/gpu/drm/i915/intel_panel.c | 153 +++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 24 deletions(-)