drm/i915/lvds: Restore initial HW state during encoder enabling
Atm the LVDS encoder depends on the PPS HW context being saved/restored from generic suspend/resume code. Since the PPS is specific to the LVDS and eDP encoders a cleaner way is to reinitialize it during encoder enabling, so do this here for LVDS. Follow-up patches will init the PPS for the eDP encoder similarly and remove the suspend/resume time save / restore. v2: - Apply BSpec +1 offset and use DIV_ROUND_UP() when programming the power cycle delay. (Ville) v3: (Ville) - Fix +1 vs. round-up order. - s/reset_on_powerdown/powerdown_on_reset/ Signed-off-by: Imre Deak <imre.deak@intel.com> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Link: http://patchwork.freedesktop.org/patch/msgid/1470827254-21954-3-git-send-email-imre.deak@intel.com
This commit is contained in:
Родитель
5a162e229a
Коммит
ed6143b8f7
|
@ -3710,6 +3710,7 @@ enum {
|
|||
|
||||
#define _PP_ON_DELAYS 0x61208
|
||||
#define PP_ON_DELAYS(pps_idx) _MMIO_PPS(pps_idx, _PP_ON_DELAYS)
|
||||
#define PANEL_PORT_SELECT_SHIFT 30
|
||||
#define PANEL_PORT_SELECT_MASK (3 << 30)
|
||||
#define PANEL_PORT_SELECT_LVDS (0 << 30)
|
||||
#define PANEL_PORT_SELECT_DPA (1 << 30)
|
||||
|
|
|
@ -48,6 +48,20 @@ struct intel_lvds_connector {
|
|||
struct notifier_block lid_notifier;
|
||||
};
|
||||
|
||||
struct intel_lvds_pps {
|
||||
/* 100us units */
|
||||
int t1_t2;
|
||||
int t3;
|
||||
int t4;
|
||||
int t5;
|
||||
int tx;
|
||||
|
||||
int divider;
|
||||
|
||||
int port;
|
||||
bool powerdown_on_reset;
|
||||
};
|
||||
|
||||
struct intel_lvds_encoder {
|
||||
struct intel_encoder base;
|
||||
|
||||
|
@ -55,6 +69,9 @@ struct intel_lvds_encoder {
|
|||
i915_reg_t reg;
|
||||
u32 a3_power;
|
||||
|
||||
struct intel_lvds_pps init_pps;
|
||||
u32 init_lvds_val;
|
||||
|
||||
struct intel_lvds_connector *attached_connector;
|
||||
};
|
||||
|
||||
|
@ -136,6 +153,83 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
|
|||
pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
|
||||
}
|
||||
|
||||
static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv,
|
||||
struct intel_lvds_pps *pps)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
pps->powerdown_on_reset = I915_READ(PP_CONTROL(0)) & PANEL_POWER_RESET;
|
||||
|
||||
val = I915_READ(PP_ON_DELAYS(0));
|
||||
pps->port = (val & PANEL_PORT_SELECT_MASK) >>
|
||||
PANEL_PORT_SELECT_SHIFT;
|
||||
pps->t1_t2 = (val & PANEL_POWER_UP_DELAY_MASK) >>
|
||||
PANEL_POWER_UP_DELAY_SHIFT;
|
||||
pps->t5 = (val & PANEL_LIGHT_ON_DELAY_MASK) >>
|
||||
PANEL_LIGHT_ON_DELAY_SHIFT;
|
||||
|
||||
val = I915_READ(PP_OFF_DELAYS(0));
|
||||
pps->t3 = (val & PANEL_POWER_DOWN_DELAY_MASK) >>
|
||||
PANEL_POWER_DOWN_DELAY_SHIFT;
|
||||
pps->tx = (val & PANEL_LIGHT_OFF_DELAY_MASK) >>
|
||||
PANEL_LIGHT_OFF_DELAY_SHIFT;
|
||||
|
||||
val = I915_READ(PP_DIVISOR(0));
|
||||
pps->divider = (val & PP_REFERENCE_DIVIDER_MASK) >>
|
||||
PP_REFERENCE_DIVIDER_SHIFT;
|
||||
val = (val & PANEL_POWER_CYCLE_DELAY_MASK) >>
|
||||
PANEL_POWER_CYCLE_DELAY_SHIFT;
|
||||
/*
|
||||
* Remove the BSpec specified +1 (100ms) offset that accounts for a
|
||||
* too short power-cycle delay due to the asynchronous programming of
|
||||
* the register.
|
||||
*/
|
||||
if (val)
|
||||
val--;
|
||||
/* Convert from 100ms to 100us units */
|
||||
pps->t4 = val * 1000;
|
||||
|
||||
if (INTEL_INFO(dev_priv)->gen <= 4 &&
|
||||
pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) {
|
||||
DRM_DEBUG_KMS("Panel power timings uninitialized, "
|
||||
"setting defaults\n");
|
||||
/* Set T2 to 40ms and T5 to 200ms in 100 usec units */
|
||||
pps->t1_t2 = 40 * 10;
|
||||
pps->t5 = 200 * 10;
|
||||
/* Set T3 to 35ms and Tx to 200ms in 100 usec units */
|
||||
pps->t3 = 35 * 10;
|
||||
pps->tx = 200 * 10;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d "
|
||||
"divider %d port %d powerdown_on_reset %d\n",
|
||||
pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx,
|
||||
pps->divider, pps->port, pps->powerdown_on_reset);
|
||||
}
|
||||
|
||||
static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv,
|
||||
struct intel_lvds_pps *pps)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = I915_READ(PP_CONTROL(0));
|
||||
WARN_ON((val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS);
|
||||
if (pps->powerdown_on_reset)
|
||||
val |= PANEL_POWER_RESET;
|
||||
I915_WRITE(PP_CONTROL(0), val);
|
||||
|
||||
I915_WRITE(PP_ON_DELAYS(0), (pps->port << PANEL_PORT_SELECT_SHIFT) |
|
||||
(pps->t1_t2 << PANEL_POWER_UP_DELAY_SHIFT) |
|
||||
(pps->t5 << PANEL_LIGHT_ON_DELAY_SHIFT));
|
||||
I915_WRITE(PP_OFF_DELAYS(0), (pps->t3 << PANEL_POWER_DOWN_DELAY_SHIFT) |
|
||||
(pps->tx << PANEL_LIGHT_OFF_DELAY_SHIFT));
|
||||
|
||||
val = pps->divider << PP_REFERENCE_DIVIDER_SHIFT;
|
||||
val |= (DIV_ROUND_UP(pps->t4, 1000) + 1) <<
|
||||
PANEL_POWER_CYCLE_DELAY_SHIFT;
|
||||
I915_WRITE(PP_DIVISOR(0), val);
|
||||
}
|
||||
|
||||
static void intel_pre_enable_lvds(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
|
||||
|
@ -154,7 +248,9 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
|
|||
assert_pll_disabled(dev_priv, pipe);
|
||||
}
|
||||
|
||||
temp = I915_READ(lvds_encoder->reg);
|
||||
intel_lvds_pps_init_hw(dev_priv, &lvds_encoder->init_pps);
|
||||
|
||||
temp = lvds_encoder->init_lvds_val;
|
||||
temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
|
||||
|
||||
if (HAS_PCH_CPT(dev)) {
|
||||
|
@ -922,18 +1018,6 @@ void intel_lvds_init(struct drm_device *dev)
|
|||
DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n");
|
||||
}
|
||||
|
||||
/* Set the Panel Power On/Off timings if uninitialized. */
|
||||
if (INTEL_INFO(dev_priv)->gen < 5 &&
|
||||
I915_READ(PP_ON_DELAYS(0)) == 0 && I915_READ(PP_OFF_DELAYS(0)) == 0) {
|
||||
/* Set T2 to 40ms and T5 to 200ms */
|
||||
I915_WRITE(PP_ON_DELAYS(0), 0x019007d0);
|
||||
|
||||
/* Set T3 to 35ms and Tx to 200ms */
|
||||
I915_WRITE(PP_OFF_DELAYS(0), 0x015e07d0);
|
||||
|
||||
DRM_DEBUG_KMS("Panel power timings uninitialized, setting defaults\n");
|
||||
}
|
||||
|
||||
lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
|
||||
if (!lvds_encoder)
|
||||
return;
|
||||
|
@ -999,6 +1083,10 @@ void intel_lvds_init(struct drm_device *dev)
|
|||
dev->mode_config.scaling_mode_property,
|
||||
DRM_MODE_SCALE_ASPECT);
|
||||
intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT;
|
||||
|
||||
intel_lvds_pps_get_hw_state(dev_priv, &lvds_encoder->init_pps);
|
||||
lvds_encoder->init_lvds_val = lvds;
|
||||
|
||||
/*
|
||||
* LVDS discovery:
|
||||
* 1) check for EDID on DDC
|
||||
|
|
Загрузка…
Ссылка в новой задаче