if (display->platform.valleyview || display->platform.cherryview) { switch (pps->vlv_pps_pipe) { case INVALID_PIPE: /* * FIXME would be nice if we can guarantee * to always have a valid PPS when calling this.
*/ return"PPS "; case PIPE_A: return"PPS A"; case PIPE_B: return"PPS B"; default:
MISSING_CASE(pps->vlv_pps_pipe); break;
}
} else { switch (pps->pps_idx) { case 0: return"PPS 0"; case 1: return"PPS 1"; default:
MISSING_CASE(pps->pps_idx); break;
}
}
/* * See vlv_pps_reset_all() why we need a power domain reference here.
*/
wakeref = intel_display_power_get(display, POWER_DOMAIN_DISPLAY_CORE);
mutex_lock(&display->pps.mutex);
if (drm_WARN(display->drm,
intel_de_read(display, intel_dp->output_reg) & DP_PORT_EN, "skipping %s kick due to [ENCODER:%d:%s] being active\n",
pps_name(intel_dp),
dig_port->base.base.base.id, dig_port->base.base.name)) return;
drm_dbg_kms(display->drm, "kicking %s for [ENCODER:%d:%s]\n",
pps_name(intel_dp),
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(display, 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 (display->platform.cherryview)
DP |= DP_PIPE_SEL_CHV(pipe); else
DP |= DP_PIPE_SEL(pipe);
/* * 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 = display->platform.cherryview &&
!chv_phy_powergate_ch(display, phy, ch, true);
if (vlv_force_pll_on(display, pipe, vlv_get_dpll(display))) {
drm_err(display->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(display, intel_dp->output_reg, DP);
intel_de_posting_read(display, intel_dp->output_reg);
/* * We don't have power sequencer currently. * Pick one that's not used by other ports.
*/
for_each_intel_dp(display->drm, encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
if (intel_dp->pps.vlv_pps_pipe != INVALID_PIPE) return intel_dp->pps.vlv_pps_pipe;
pipe = vlv_find_free_pps(display);
/* * Didn't find one. This should not happen since there * are two power sequencers and up to two eDP ports.
*/ if (drm_WARN_ON(display->drm, pipe == INVALID_PIPE))
pipe = PIPE_A;
if (port_sel != PANEL_PORT_SELECT_VLV(port)) continue;
if (!check(display, pipe)) continue;
return pipe;
}
return INVALID_PIPE;
}
staticvoid
vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp)
{ struct intel_display *display = to_intel_display(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum port port = dig_port->base.port;
lockdep_assert_held(&display->pps.mutex);
/* try to find a pipe with this port selected */ /* first pick one where the panel is on */
intel_dp->pps.vlv_pps_pipe = vlv_initial_pps_pipe(display, port,
pps_has_pp_on); /* didn't find one? pick one where vdd is on */ if (intel_dp->pps.vlv_pps_pipe == INVALID_PIPE)
intel_dp->pps.vlv_pps_pipe = vlv_initial_pps_pipe(display, port,
pps_has_vdd_on); /* didn't find one? pick one with just the correct port */ if (intel_dp->pps.vlv_pps_pipe == INVALID_PIPE)
intel_dp->pps.vlv_pps_pipe = vlv_initial_pps_pipe(display, port,
pps_any);
/* didn't find one? just let vlv_power_sequencer_pipe() pick one when needed */ if (intel_dp->pps.vlv_pps_pipe == INVALID_PIPE) {
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] no initial power sequencer\n",
dig_port->base.base.base.id, dig_port->base.base.name); return;
}
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] initial power sequencer: %s\n",
dig_port->base.base.base.id, dig_port->base.base.name,
pps_name(intel_dp));
}
if (display->platform.valleyview || display->platform.cherryview) {
vlv_initial_power_sequencer_setup(intel_dp); returntrue;
}
/* first ask the VBT */ if (intel_num_pps(display) > 1)
intel_dp->pps.pps_idx = connector->panel.vbt.backlight.controller; else
intel_dp->pps.pps_idx = 0;
if (drm_WARN_ON(display->drm, intel_dp->pps.pps_idx >= intel_num_pps(display)))
intel_dp->pps.pps_idx = -1;
/* VBT wasn't parsed yet? pick one where the panel is on */ if (intel_dp->pps.pps_idx < 0)
intel_dp->pps.pps_idx = bxt_initial_pps_idx(display, pps_has_pp_on); /* didn't find one? pick one where vdd is on */ if (intel_dp->pps.pps_idx < 0)
intel_dp->pps.pps_idx = bxt_initial_pps_idx(display, pps_has_vdd_on); /* didn't find one? pick any */ if (intel_dp->pps.pps_idx < 0) {
intel_dp->pps.pps_idx = bxt_initial_pps_idx(display, pps_any);
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] no initial power sequencer, assuming %s\n",
encoder->base.base.id, encoder->base.name,
pps_name(intel_dp));
} else {
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] initial power sequencer: %s\n",
encoder->base.base.id, encoder->base.name,
pps_name(intel_dp));
}
/* * 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 vlv_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.
*/
/* take the difference of current time and panel power off time
* and then make panel wait for power_cycle if needed. */
panel_power_on_time = ktime_get_boottime();
panel_power_off_duration = ktime_ms_delta(panel_power_on_time, intel_dp->pps.panel_power_off_time);
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] %s wait for panel power cycle (%lld ms remaining)\n",
dig_port->base.base.base.id, dig_port->base.base.name,
pps_name(intel_dp), remaining);
/* When we disable the VDD override bit last we have to do the manual
* wait. */ if (remaining)
wait_remaining_ms_from_jiffies(jiffies, remaining);
control = intel_de_read(display, _pp_ctrl_reg(intel_dp)); if (drm_WARN_ON(display->drm, !HAS_DDI(display) &&
(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 intel_display *display = to_intel_display(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->pps.want_panel_vdd;
intel_de_write(display, pp_ctrl_reg, pp);
intel_de_posting_read(display, pp_ctrl_reg);
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] %s PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n",
dig_port->base.base.base.id, dig_port->base.base.name,
pps_name(intel_dp),
intel_de_read(display, pp_stat_reg),
intel_de_read(display, pp_ctrl_reg)); /* * If the panel wasn't on, delay before accessing aux channel
*/ if (!edp_have_panel_power(intel_dp)) {
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] %s panel power wasn't enabled\n",
dig_port->base.base.base.id, dig_port->base.base.name,
pps_name(intel_dp));
msleep(intel_dp->pps.panel_power_up_delay);
}
return need_to_disable;
}
/* * Must be paired with intel_pps_vdd_off() or - to disable * both VDD and panel power - 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)
{ struct intel_display *display = to_intel_display(intel_dp);
intel_wakeref_t wakeref; bool vdd;
cancel_delayed_work_sync(&intel_dp->pps.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);
}
/* * We may not yet know the real power sequencing delays, * so keep VDD enabled until we're done with init.
*/ if (intel_dp->pps.initializing) return;
/* * 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->pps.panel_power_cycle_delay * 5);
queue_delayed_work(display->wq.unordered,
&intel_dp->pps.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 intel_display *display = to_intel_display(intel_dp);
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] %s turn panel power off\n",
dig_port->base.base.base.id, dig_port->base.base.name,
pps_name(intel_dp));
drm_WARN(display->drm, !intel_dp->pps.want_panel_vdd, "[ENCODER:%d:%s] %s need VDD to turn off panel\n",
dig_port->base.base.base.id, dig_port->base.base.name,
pps_name(intel_dp));
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);
/* We got a reference when we enabled the VDD. */
intel_display_power_put(display,
intel_aux_power_domain(dig_port),
fetch_and_zero(&intel_dp->pps.vdd_wakeref));
}
/* Enable backlight in the panel power control. */ void intel_pps_backlight_on(struct intel_dp *intel_dp)
{ struct intel_display *display = to_intel_display(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);
/* * 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 intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector);
intel_wakeref_t wakeref; bool is_enabled;
if (drm_WARN_ON(display->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(display->drm, "detaching %s from [ENCODER:%d:%s]\n",
pps_name(intel_dp),
dig_port->base.base.base.id, dig_port->base.base.name);
intel_de_write(display, pp_on_reg, 0);
intel_de_posting_read(display, pp_on_reg);
/* * Figure out the current pipe for the initial backlight setup. If the * current pipe isn't valid, try the PPS pipe, and if that fails just * assume pipe A.
*/
pipe = vlv_active_pipe(intel_dp);
if (intel_dp->pps.vlv_pps_pipe != INVALID_PIPE &&
intel_dp->pps.vlv_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(display, crtc->pipe);
intel_dp->pps.vlv_active_pipe = crtc->pipe;
if (!intel_dp_is_edp(intel_dp)) return;
/* now it's all ours */
intel_dp->pps.vlv_pps_pipe = crtc->pipe;
drm_dbg_kms(display->drm, "initializing %s for [ENCODER:%d:%s]\n",
pps_name(intel_dp),
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);
}
/* Call on all DP, not just eDP */ void vlv_pps_port_disable(struct intel_encoder *encoder, conststruct intel_crtc_state *crtc_state)
{ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
/* * 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(display->drm, "[ENCODER:%d:%s] %s VDD left on by BIOS, adjusting state tracking\n",
dig_port->base.base.base.id, dig_port->base.base.name,
pps_name(intel_dp));
drm_WARN_ON(display->drm, intel_dp->pps.vdd_wakeref);
intel_dp->pps.vdd_wakeref = intel_display_power_get(display,
intel_aux_power_domain(dig_port));
}
staticvoid pps_init_timestamps(struct intel_dp *intel_dp)
{ /* * Initialize panel power off time to 0, assuming panel power could have * been toggled between kernel boot and now only by a previously loaded * and removed i915, which has already ensured sufficient power off * delay at module remove.
*/
intel_dp->pps.panel_power_off_time = 0;
intel_dp->pps.last_power_on = jiffies;
intel_dp->pps.last_backlight_off = jiffies;
}
/* * On Toshiba Satellite P50-C-18C system the VBT T12 delay * of 500ms appears to be too short. Occasionally the panel * just fails to power back on. Increasing the delay to 800ms * seems sufficient to avoid this problem.
*/ if (intel_has_quirk(display, QUIRK_INCREASE_T12_DELAY)) {
vbt->power_cycle = max_t(u16, vbt->power_cycle, msecs_to_pps_units(1300));
drm_dbg_kms(display->drm, "Increasing T12 panel delay as per the quirk to %d\n",
vbt->power_cycle);
}
/* 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(power_up);
assign_final(backlight_on);
assign_final(backlight_off);
assign_final(power_down);
assign_final(power_cycle); #undef assign_final
drm_dbg_kms(display->drm, "panel power up delay %d, power down delay %d, power cycle delay %d\n",
intel_dp->pps.panel_power_up_delay,
intel_dp->pps.panel_power_down_delay,
intel_dp->pps.panel_power_cycle_delay);
drm_dbg_kms(display->drm, "backlight on delay %d, off delay %d\n",
intel_dp->pps.backlight_on_delay,
intel_dp->pps.backlight_off_delay);
/* * We override the HW backlight delays to 1 because we do manual waits * on them. For backlight_on, even BSpec recommends doing it. For * backlight_off, 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->backlight_on = 1;
final->backlight_off = 1;
/* * HW has only a 100msec granularity for power_cycle so round it up * accordingly.
*/
final->power_cycle = roundup(final->power_cycle, msecs_to_pps_units(100));
}
staticvoid pps_init_registers(struct intel_dp *intel_dp, bool force_disable_vdd)
{ struct intel_display *display = to_intel_display(intel_dp);
u32 pp_on, pp_off, port_sel = 0; int div = DISPLAY_RUNTIME_INFO(display)->rawclk_freq / 1000; struct pps_registers regs; enum port port = dp_to_dig_port(intel_dp)->base.port; conststruct intel_pps_delays *seq = &intel_dp->pps.pps_delays;
lockdep_assert_held(&display->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(display->drm, pp & PANEL_POWER_ON, "Panel power already on\n");
if (pp & EDP_FORCE_VDD)
drm_dbg_kms(display->drm, "VDD already on, disabling first\n");
/* Haswell doesn't have any port selection bits for the panel
* power sequencer any more. */ if (display->platform.valleyview || display->platform.cherryview) {
port_sel = PANEL_PORT_SELECT_VLV(port);
} elseif (HAS_PCH_IBX(display) || HAS_PCH_CPT(display)) { 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;
}
}
with_intel_pps_lock(intel_dp, wakeref) { /* * Reinit the power sequencer also on the resume path, in case * BIOS did something nasty with it.
*/ if (display->platform.valleyview || display->platform.cherryview)
vlv_initial_power_sequencer_setup(intel_dp);
if (edp_have_panel_vdd(intel_dp))
edp_panel_vdd_schedule_off(intel_dp);
}
}
void intel_pps_unlock_regs_wa(struct intel_display *display)
{ int pps_num; int pps_idx;
if (!HAS_DISPLAY(display) || HAS_DDI(display)) return; /* * This w/a is needed at least on CPT/PPT, but to be sure apply it * everywhere where registers can be write protected.
*/
pps_num = intel_num_pps(display);
if (connector->base.status != connector_status_connected) return -ENODEV;
seq_printf(m, "Panel power up delay: %d\n",
intel_dp->pps.panel_power_up_delay);
seq_printf(m, "Panel power down delay: %d\n",
intel_dp->pps.panel_power_down_delay);
seq_printf(m, "Panel power cycle delay: %d\n",
intel_dp->pps.panel_power_cycle_delay);
seq_printf(m, "Backlight on delay: %d\n",
intel_dp->pps.backlight_on_delay);
seq_printf(m, "Backlight off delay: %d\n",
intel_dp->pps.backlight_off_delay);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.