Commit bb6ae9e6 authored by Ville Syrjälä's avatar Ville Syrjälä

drm/i915: Allow planes to declare their minimum acceptable cdclk

Various pixel formats and plane scaling impose additional constraints
on the cdclk frequency. Provide a new plane->min_cdclk() hook that
will be used to compute the minimum acceptable cdclk frequency for
each plane.

Annoyingly on some platforms the numer of active planes affects
this calculation so we must also toss in more planes into the
state when the number of active planes changes.

The sequence of state computation must also be changed:
1. check_plane() (updates plane's visibility etc.)
2. figure out if more planes now require update min_cdclk
   computaion
3. calculate the new min cdclk for each plane in the state
4. if the minimum of any plane now exceeds the current
   logical cdclk we recompute the cdclk
4. during cdclk computation take the planes' min_cdclk into
   accoutn
5. follow the normal cdclk programming to change the
   cdclk frequency. This may now require a modeset (except
   on bxt/glk in some cases), which either succeeds or
   fails depending on whether userspace has given
   us permission to perform a modeset or not.

v2: Fix plane id check in intel_crtc_add_planes_to_state()
    Only print the debug message when cdclk needs bumping
    Use dev_priv->cdclk... as the old state explicitly
Reviewed-by: default avatarJuha-Pekka Heikkila <juhapekka.heikkila@gmail.com>
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191015193035.25982-5-ville.syrjala@linux.intel.com
parent bf5da83e
......@@ -138,6 +138,44 @@ unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
return cpp * crtc_state->pixel_rate;
}
bool intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
struct intel_plane *plane)
{
struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
const struct intel_plane_state *plane_state =
intel_atomic_get_new_plane_state(state, plane);
struct intel_crtc *crtc = to_intel_crtc(plane_state->base.crtc);
struct intel_crtc_state *crtc_state;
if (!plane_state->base.visible || !plane->min_cdclk)
return false;
crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
crtc_state->min_cdclk[plane->id] =
plane->min_cdclk(crtc_state, plane_state);
/*
* Does the cdclk need to be bumbed up?
*
* Note: we obviously need to be called before the new
* cdclk frequency is calculated so state->cdclk.logical
* hasn't been populated yet. Hence we look at the old
* cdclk state under dev_priv->cdclk.logical. This is
* safe as long we hold at least one crtc mutex (which
* must be true since we have crtc_state).
*/
if (crtc_state->min_cdclk[plane->id] > dev_priv->cdclk.logical.cdclk) {
DRM_DEBUG_KMS("[PLANE:%d:%s] min_cdclk (%d kHz) > logical cdclk (%d kHz)\n",
plane->base.base.id, plane->base.name,
crtc_state->min_cdclk[plane->id],
dev_priv->cdclk.logical.cdclk);
return true;
}
return false;
}
int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_state,
struct intel_crtc_state *new_crtc_state,
const struct intel_plane_state *old_plane_state,
......@@ -151,6 +189,7 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
new_crtc_state->nv12_planes &= ~BIT(plane->id);
new_crtc_state->c8_planes &= ~BIT(plane->id);
new_crtc_state->data_rate[plane->id] = 0;
new_crtc_state->min_cdclk[plane->id] = 0;
new_plane_state->base.visible = false;
if (!new_plane_state->base.crtc && !old_plane_state->base.crtc)
......
......@@ -47,5 +47,7 @@ int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
struct intel_crtc_state *crtc_state,
const struct intel_plane_state *old_plane_state,
struct intel_plane_state *plane_state);
bool intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
struct intel_plane *plane);
#endif /* __INTEL_ATOMIC_PLANE_H__ */
......@@ -1918,6 +1918,19 @@ static int intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state)
return DIV_ROUND_UP(pixel_rate * 100, 90);
}
static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state)
{
struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
struct intel_plane *plane;
int min_cdclk = 0;
for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane)
min_cdclk = max(crtc_state->min_cdclk[plane->id], min_cdclk);
return min_cdclk;
}
int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state)
{
struct drm_i915_private *dev_priv =
......@@ -1986,6 +1999,9 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state)
IS_GEMINILAKE(dev_priv))
min_cdclk = max(158400, min_cdclk);
/* Account for additional needs from the planes */
min_cdclk = max(intel_planes_min_cdclk(crtc_state), min_cdclk);
if (min_cdclk > dev_priv->max_cdclk_freq) {
DRM_DEBUG_KMS("required cdclk (%d kHz) exceeds max (%d kHz)\n",
min_cdclk, dev_priv->max_cdclk_freq);
......
......@@ -3154,6 +3154,7 @@ static void intel_plane_disable_noatomic(struct intel_crtc *crtc,
intel_set_plane_visible(crtc_state, plane_state, false);
fixup_active_planes(crtc_state);
crtc_state->data_rate[plane->id] = 0;
crtc_state->min_cdclk[plane->id] = 0;
if (plane->id == PLANE_PRIMARY)
intel_pre_disable_primary_noatomic(&crtc->base);
......@@ -3577,6 +3578,53 @@ int skl_check_plane_surface(struct intel_plane_state *plane_state)
return 0;
}
static void i9xx_plane_ratio(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state,
unsigned int *num, unsigned int *den)
{
const struct drm_framebuffer *fb = plane_state->base.fb;
unsigned int cpp = fb->format->cpp[0];
/*
* g4x bspec says 64bpp pixel rate can't exceed 80%
* of cdclk when the sprite plane is enabled on the
* same pipe. ilk/snb bspec says 64bpp pixel rate is
* never allowed to exceed 80% of cdclk. Let's just go
* with the ilk/snb limit always.
*/
if (cpp == 8) {
*num = 10;
*den = 8;
} else {
*num = 1;
*den = 1;
}
}
static int i9xx_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state)
{
unsigned int pixel_rate;
unsigned int num, den;
/*
* Note that crtc_state->pixel_rate accounts for both
* horizontal and vertical panel fitter downscaling factors.
* Pre-HSW bspec tells us to only consider the horizontal
* downscaling factor here. We ignore that and just consider
* both for simplicity.
*/
pixel_rate = crtc_state->pixel_rate;
i9xx_plane_ratio(crtc_state, plane_state, &num, &den);
/* two pixels per clock with double wide pipe */
if (crtc_state->double_wide)
den *= 2;
return DIV_ROUND_UP(pixel_rate * num, den);
}
unsigned int
i9xx_plane_max_stride(struct intel_plane *plane,
u32 pixel_format, u64 modifier,
......@@ -11706,6 +11754,7 @@ int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
plane_state->base.visible = visible = false;
crtc_state->active_planes &= ~BIT(plane->id);
crtc_state->data_rate[plane->id] = 0;
crtc_state->min_cdclk[plane->id] = 0;
}
if (!was_visible && !visible)
......@@ -12072,9 +12121,6 @@ static int intel_crtc_atomic_check(struct intel_atomic_state *state,
if (INTEL_GEN(dev_priv) >= 9) {
if (mode_changed || crtc_state->update_pipe)
ret = skl_update_scaler_crtc(crtc_state);
if (!ret)
ret = icl_check_nv12_planes(crtc_state);
if (!ret)
ret = skl_check_pipe_max_pixel_rate(crtc, crtc_state);
if (!ret)
......@@ -13796,12 +13842,49 @@ static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_sta
new_crtc_state->has_drrs = old_crtc_state->has_drrs;
}
static int intel_atomic_check_planes(struct intel_atomic_state *state)
static int intel_crtc_add_planes_to_state(struct intel_atomic_state *state,
struct intel_crtc *crtc,
u8 plane_ids_mask)
{
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
struct intel_plane *plane;
for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
struct intel_plane_state *plane_state;
if ((plane_ids_mask & BIT(plane->id)) == 0)
continue;
plane_state = intel_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state))
return PTR_ERR(plane_state);
}
return 0;
}
static bool active_planes_affects_min_cdclk(struct drm_i915_private *dev_priv)
{
/* See {hsw,vlv,ivb}_plane_ratio() */
return IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv) ||
IS_CHERRYVIEW(dev_priv) || IS_VALLEYVIEW(dev_priv) ||
IS_IVYBRIDGE(dev_priv);
}
static int intel_atomic_check_planes(struct intel_atomic_state *state,
bool *need_modeset)
{
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
struct intel_crtc_state *old_crtc_state, *new_crtc_state;
struct intel_plane_state *plane_state;
struct intel_plane *plane;
struct intel_crtc *crtc;
int i, ret;
ret = icl_add_linked_planes(state);
if (ret)
return ret;
for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
ret = intel_plane_atomic_check(state, plane);
if (ret) {
......@@ -13811,6 +13894,41 @@ static int intel_atomic_check_planes(struct intel_atomic_state *state)
}
}
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
u8 old_active_planes, new_active_planes;
ret = icl_check_nv12_planes(new_crtc_state);
if (ret)
return ret;
/*
* On some platforms the number of active planes affects
* the planes' minimum cdclk calculation. Add such planes
* to the state before we compute the minimum cdclk.
*/
if (!active_planes_affects_min_cdclk(dev_priv))
continue;
old_active_planes = old_crtc_state->active_planes & ~BIT(PLANE_CURSOR);
new_active_planes = new_crtc_state->active_planes & ~BIT(PLANE_CURSOR);
if (hweight8(old_active_planes) == hweight8(new_active_planes))
continue;
ret = intel_crtc_add_planes_to_state(state, crtc, new_active_planes);
if (ret)
return ret;
}
/*
* active_planes bitmask has been updated, and potentially
* affected planes are part of the state. We can now
* compute the minimum cdclk for each plane.
*/
for_each_new_intel_plane_in_state(state, plane, plane_state, i)
*need_modeset |= intel_plane_calc_min_cdclk(state, plane);
return 0;
}
......@@ -13891,6 +14009,10 @@ static int intel_atomic_check(struct drm_device *dev,
any_ms |= state->cdclk.force_min_cdclk_changed;
ret = intel_atomic_check_planes(state, &any_ms);
if (ret)
goto fail;
if (any_ms) {
ret = intel_modeset_checks(state);
if (ret)
......@@ -13899,14 +14021,6 @@ static int intel_atomic_check(struct drm_device *dev,
state->cdclk.logical = dev_priv->cdclk.logical;
}
ret = icl_add_linked_planes(state);
if (ret)
goto fail;
ret = intel_atomic_check_planes(state);
if (ret)
goto fail;
ret = intel_atomic_check_crtcs(state);
if (ret)
goto fail;
......@@ -15355,6 +15469,15 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe)
plane->get_hw_state = i9xx_plane_get_hw_state;
plane->check_plane = i9xx_plane_check;
if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv))
plane->min_cdclk = hsw_plane_min_cdclk;
else if (IS_IVYBRIDGE(dev_priv))
plane->min_cdclk = ivb_plane_min_cdclk;
else if (IS_CHERRYVIEW(dev_priv) || IS_VALLEYVIEW(dev_priv))
plane->min_cdclk = vlv_plane_min_cdclk;
else
plane->min_cdclk = i9xx_plane_min_cdclk;
plane_funcs = &i965_plane_funcs;
} else {
formats = i8xx_primary_formats;
......@@ -15366,6 +15489,7 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe)
plane->disable_plane = i9xx_disable_plane;
plane->get_hw_state = i9xx_plane_get_hw_state;
plane->check_plane = i9xx_plane_check;
plane->min_cdclk = i9xx_plane_min_cdclk;
plane_funcs = &i8xx_plane_funcs;
}
......@@ -17284,17 +17408,9 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
intel_crtc_compute_pixel_rate(crtc_state);
min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
if (WARN_ON(min_cdclk < 0))
min_cdclk = 0;
intel_crtc_update_active_timings(crtc_state);
}
dev_priv->min_cdclk[crtc->pipe] = min_cdclk;
dev_priv->min_voltage_level[crtc->pipe] =
crtc_state->min_voltage_level;
for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
const struct intel_plane_state *plane_state =
to_intel_plane_state(plane->base.state);
......@@ -17306,8 +17422,34 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
if (plane_state->base.visible)
crtc_state->data_rate[plane->id] =
4 * crtc_state->pixel_rate;
/*
* FIXME don't have the fb yet, so can't
* use plane->min_cdclk() :(
*/
if (plane_state->base.visible && plane->min_cdclk) {
if (crtc_state->double_wide ||
INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
crtc_state->min_cdclk[plane->id] =
DIV_ROUND_UP(crtc_state->pixel_rate, 2);
else
crtc_state->min_cdclk[plane->id] =
crtc_state->pixel_rate;
}
DRM_DEBUG_KMS("[PLANE:%d:%s] min_cdclk %d kHz\n",
plane->base.base.id, plane->base.name,
crtc_state->min_cdclk[plane->id]);
}
if (crtc_state->base.active) {
min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
if (WARN_ON(min_cdclk < 0))
min_cdclk = 0;
}
dev_priv->min_cdclk[crtc->pipe] = min_cdclk;
dev_priv->min_voltage_level[crtc->pipe] =
crtc_state->min_voltage_level;
intel_bw_crtc_update(bw_state, crtc_state);
intel_pipe_config_sanity_check(dev_priv, crtc_state);
......
......@@ -940,6 +940,8 @@ struct intel_crtc_state {
struct intel_crtc_wm_state wm;
int min_cdclk[I915_MAX_PLANES];
u32 data_rate[I915_MAX_PLANES];
/* Gamma mode programmed on the pipe */
......@@ -1085,6 +1087,8 @@ struct intel_plane {
bool (*get_hw_state)(struct intel_plane *plane, enum pipe *pipe);
int (*check_plane)(struct intel_crtc_state *crtc_state,
struct intel_plane_state *plane_state);
int (*min_cdclk)(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state);
};
struct intel_watermark_params {
......
This diff is collapsed.
......@@ -49,4 +49,11 @@ static inline u8 icl_hdr_plane_mask(void)
bool icl_is_hdr_plane(struct drm_i915_private *dev_priv, enum plane_id plane_id);
int ivb_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state);
int hsw_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state);
int vlv_plane_min_cdclk(const struct intel_crtc_state *crtc_state,
const struct intel_plane_state *plane_state);
#endif /* __INTEL_SPRITE_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment