drm/i915: Add infrastructure for choosing DPLLs before disabling crtcs

It is possible for a mode set to fail if there aren't shared DPLLS that
match the new configuration requirement or other errors in clock
computation. If that step is executed after disabling crtcs, in the
failure case the hardware configuration is changed and needs to be
restored. Doing those things early will allow the mode set to fail
before actually touching the hardware.

Follow up patches will convert different platforms to use the new
infrastructure.

v2: Keep pll->new_config valid only during mode set (Ville)
    Use kmemdup() in i915_shared_dpll_start_config() (Ville)
    Restore old pll config if something fails before commit (Ville)
    Don't set compute_clock hooks since dev_priv is kzalloc()'d (Ville)

Signed-off-by: Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
Ander Conselvan de Oliveira 2014-10-29 11:32:33 +02:00 committed by Daniel Vetter
parent 3e369b76ce
commit 8bd31e67c9
2 changed files with 117 additions and 28 deletions

View File

@ -236,6 +236,8 @@ struct intel_shared_dpll_config {
struct intel_shared_dpll { struct intel_shared_dpll {
struct intel_shared_dpll_config config; struct intel_shared_dpll_config config;
struct intel_shared_dpll_config *new_config;
int active; /* count of number of active CRTCs (i.e. DPMS on) */ int active; /* count of number of active CRTCs (i.e. DPMS on) */
bool on; /* is the PLL actually active? Disabled during modeset */ bool on; /* is the PLL actually active? Disabled during modeset */
const char *name; const char *name;
@ -484,6 +486,7 @@ struct drm_i915_display_funcs {
struct intel_crtc_config *); struct intel_crtc_config *);
void (*get_plane_config)(struct intel_crtc *, void (*get_plane_config)(struct intel_crtc *,
struct intel_plane_config *); struct intel_plane_config *);
int (*crtc_compute_clock)(struct intel_crtc *crtc);
int (*crtc_mode_set)(struct intel_crtc *crtc, int (*crtc_mode_set)(struct intel_crtc *crtc,
int x, int y, int x, int y,
struct drm_framebuffer *old_fb); struct drm_framebuffer *old_fb);

View File

@ -3863,15 +3863,9 @@ void intel_put_shared_dpll(struct intel_crtc *crtc)
struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc) struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
{ {
struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); struct intel_shared_dpll *pll;
enum intel_dpll_id i; enum intel_dpll_id i;
if (pll) {
DRM_DEBUG_KMS("CRTC:%d dropping existing %s\n",
crtc->base.base.id, pll->name);
intel_put_shared_dpll(crtc);
}
if (HAS_PCH_IBX(dev_priv->dev)) { if (HAS_PCH_IBX(dev_priv->dev)) {
/* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */
i = (enum intel_dpll_id) crtc->pipe; i = (enum intel_dpll_id) crtc->pipe;
@ -3880,7 +3874,7 @@ struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n", DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n",
crtc->base.base.id, pll->name); crtc->base.base.id, pll->name);
WARN_ON(pll->config.crtc_mask); WARN_ON(pll->new_config->crtc_mask);
goto found; goto found;
} }
@ -3889,17 +3883,16 @@ struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
pll = &dev_priv->shared_dplls[i]; pll = &dev_priv->shared_dplls[i];
/* Only want to check enabled timings first */ /* Only want to check enabled timings first */
if (pll->config.crtc_mask == 0) if (pll->new_config->crtc_mask == 0)
continue; continue;
if (memcmp(&crtc->config.dpll_hw_state, if (memcmp(&crtc->new_config->dpll_hw_state,
&pll->config.hw_state, &pll->new_config->hw_state,
sizeof(pll->config.hw_state)) == 0) { sizeof(pll->new_config->hw_state)) == 0) {
DRM_DEBUG_KMS("CRTC:%d sharing existing %s " DRM_DEBUG_KMS("CRTC:%d sharing existing %s (crtc mask 0x%08x, ative %d)\n",
"(crtc_mask 0x%08x, active %d)\n",
crtc->base.base.id, pll->name, crtc->base.base.id, pll->name,
pll->config.crtc_mask, pll->active); pll->new_config->crtc_mask,
pll->active);
goto found; goto found;
} }
} }
@ -3907,7 +3900,7 @@ struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
/* Ok no matching timings, maybe there's a free one? */ /* Ok no matching timings, maybe there's a free one? */
for (i = 0; i < dev_priv->num_shared_dpll; i++) { for (i = 0; i < dev_priv->num_shared_dpll; i++) {
pll = &dev_priv->shared_dplls[i]; pll = &dev_priv->shared_dplls[i];
if (pll->config.crtc_mask == 0) { if (pll->new_config->crtc_mask == 0) {
DRM_DEBUG_KMS("CRTC:%d allocated %s\n", DRM_DEBUG_KMS("CRTC:%d allocated %s\n",
crtc->base.base.id, pll->name); crtc->base.base.id, pll->name);
goto found; goto found;
@ -3917,18 +3910,85 @@ struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
return NULL; return NULL;
found: found:
if (pll->config.crtc_mask == 0) if (pll->new_config->crtc_mask == 0)
pll->config.hw_state = crtc->config.dpll_hw_state; pll->new_config->hw_state = crtc->new_config->dpll_hw_state;
crtc->config.shared_dpll = i; crtc->new_config->shared_dpll = i;
DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name, DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name,
pipe_name(crtc->pipe)); pipe_name(crtc->pipe));
pll->config.crtc_mask |= 1 << crtc->pipe; pll->new_config->crtc_mask |= 1 << crtc->pipe;
return pll; return pll;
} }
/**
* intel_shared_dpll_start_config - start a new PLL staged config
* @dev_priv: DRM device
* @clear_pipes: mask of pipes that will have their PLLs freed
*
* Starts a new PLL staged config, copying the current config but
* releasing the references of pipes specified in clear_pipes.
*/
static int intel_shared_dpll_start_config(struct drm_i915_private *dev_priv,
unsigned clear_pipes)
{
struct intel_shared_dpll *pll;
enum intel_dpll_id i;
for (i = 0; i < dev_priv->num_shared_dpll; i++) {
pll = &dev_priv->shared_dplls[i];
pll->new_config = kmemdup(&pll->config, sizeof pll->config,
GFP_KERNEL);
if (!pll->new_config)
goto cleanup;
pll->new_config->crtc_mask &= ~clear_pipes;
}
return 0;
cleanup:
while (--i >= 0) {
pll = &dev_priv->shared_dplls[i];
pll->new_config = NULL;
}
return -ENOMEM;
}
static void intel_shared_dpll_commit(struct drm_i915_private *dev_priv)
{
struct intel_shared_dpll *pll;
enum intel_dpll_id i;
for (i = 0; i < dev_priv->num_shared_dpll; i++) {
pll = &dev_priv->shared_dplls[i];
WARN_ON(pll->new_config == &pll->config);
pll->config = *pll->new_config;
kfree(pll->new_config);
pll->new_config = NULL;
}
}
static void intel_shared_dpll_abort_config(struct drm_i915_private *dev_priv)
{
struct intel_shared_dpll *pll;
enum intel_dpll_id i;
for (i = 0; i < dev_priv->num_shared_dpll; i++) {
pll = &dev_priv->shared_dplls[i];
WARN_ON(pll->new_config == &pll->config);
kfree(pll->new_config);
pll->new_config = NULL;
}
}
static void cpt_verify_modeset(struct drm_device *dev, int pipe) static void cpt_verify_modeset(struct drm_device *dev, int pipe)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
@ -5420,11 +5480,11 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config) struct intel_crtc_config *pipe_config)
{ {
struct drm_device *dev = crtc->base.dev; struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
/* FIXME should check pixel clock limits on all platforms */ /* FIXME should check pixel clock limits on all platforms */
if (INTEL_INFO(dev)->gen < 4) { if (INTEL_INFO(dev)->gen < 4) {
struct drm_i915_private *dev_priv = dev->dev_private;
int clock_limit = int clock_limit =
dev_priv->display.get_display_clock_speed(dev); dev_priv->display.get_display_clock_speed(dev);
@ -5474,10 +5534,11 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc,
hsw_compute_ips_config(crtc, pipe_config); hsw_compute_ips_config(crtc, pipe_config);
/* /*
* XXX: PCH/WRPLL clock sharing is done in ->mode_set, so make sure the * XXX: PCH/WRPLL clock sharing is done in ->mode_set if ->compute_clock is not
* old clock survives for now. * set, so make sure the old clock survives for now.
*/ */
if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev) || HAS_DDI(dev)) if (dev_priv->display.crtc_compute_clock == NULL &&
(HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev) || HAS_DDI(dev)))
pipe_config->shared_dpll = crtc->config.shared_dpll; pipe_config->shared_dpll = crtc->config.shared_dpll;
if (pipe_config->has_pch_encoder) if (pipe_config->has_pch_encoder)
@ -7405,6 +7466,9 @@ static int ironlake_crtc_mode_set(struct intel_crtc *crtc,
else else
crtc->new_config->dpll_hw_state.fp1 = fp; crtc->new_config->dpll_hw_state.fp1 = fp;
if (intel_crtc_to_shared_dpll(crtc))
intel_put_shared_dpll(crtc);
pll = intel_get_shared_dpll(crtc); pll = intel_get_shared_dpll(crtc);
if (pll == NULL) { if (pll == NULL) {
DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n", DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n",
@ -10749,6 +10813,22 @@ static int __intel_set_mode(struct drm_crtc *crtc,
prepare_pipes &= ~disable_pipes; prepare_pipes &= ~disable_pipes;
} }
if (dev_priv->display.crtc_compute_clock) {
unsigned clear_pipes = modeset_pipes | disable_pipes;
ret = intel_shared_dpll_start_config(dev_priv, clear_pipes);
if (ret)
goto done;
for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) {
ret = dev_priv->display.crtc_compute_clock(intel_crtc);
if (ret) {
intel_shared_dpll_abort_config(dev_priv);
goto done;
}
}
}
for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc)
intel_crtc_disable(&intel_crtc->base); intel_crtc_disable(&intel_crtc->base);
@ -10776,6 +10856,9 @@ static int __intel_set_mode(struct drm_crtc *crtc,
&pipe_config->adjusted_mode); &pipe_config->adjusted_mode);
} }
if (dev_priv->display.crtc_compute_clock)
intel_shared_dpll_commit(dev_priv);
/* Only after disabling all output pipelines that will be changed can we /* Only after disabling all output pipelines that will be changed can we
* update the the output configuration. */ * update the the output configuration. */
intel_modeset_update_state(dev, prepare_pipes); intel_modeset_update_state(dev, prepare_pipes);
@ -10810,10 +10893,13 @@ static int __intel_set_mode(struct drm_crtc *crtc,
crtc->x = x; crtc->x = x;
crtc->y = y; crtc->y = y;
ret = dev_priv->display.crtc_mode_set(intel_crtc, x, y, fb); if (dev_priv->display.crtc_mode_set) {
ret = dev_priv->display.crtc_mode_set(intel_crtc,
x, y, fb);
if (ret) if (ret)
goto done; goto done;
} }
}
/* Now enable the clocks, plane, pipe, and connectors that we set up. */ /* Now enable the clocks, plane, pipe, and connectors that we set up. */
for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) { for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {