Op 03-05-2019 om 21:08 schreef Ville Syrjala:
> From: Ville Syrjälä <ville.syrj...@linux.intel.com>
>
> ICL has so many planes that it can easily exceed the maximum
> effective memory bandwidth of the system. We must therefore check
> that we don't exceed that limit.
>
> The algorithm is very magic number heavy and lacks sufficient
> explanation for now. We also have no sane way to query the
> memory clock and timings, so we must rely on a combination of
> raw readout from the memory controller and hardcoded assumptions.
> The memory controller values obviously change as the system
> jumps between the different SAGV points, so we try to stabilize
> it first by disabling SAGV for the duration of the readout.
>
> The utilized bandwidth is tracked via a device wide atomic
> private object. That is actually not robust because we can't
> afford to enforce strict global ordering between the pipes.
> Thus I think I'll need to change this to simply chop up the
> available bandwidth between all the active pipes. Each pipe
> can then do whatever it wants as long as it doesn't exceed
> its budget. That scheme will also require that we assume that
> any number of planes could be active at any time.
>
> TODO: make it robust and deal with all the open questions
>
> v2: Sleep longer after disabling SAGV
> v3: Poll for the dclk to get raised (seen it take 250ms!)
>     If the system has 2133MT/s memory then we pointlessly
>     wait one full second :(
> v4: Use the new pcode interface to get the qgv points rather
>     that using hardcoded numbers
>
> Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
> ---
Splitting out the HW readout would be nice, it would make it easier to review 
the separate parts that this patch tries to accomplish. :)
>  drivers/gpu/drm/i915/Makefile             |   1 +
>  drivers/gpu/drm/i915/i915_drv.c           | 229 ++++++++++++++++++++++
>  drivers/gpu/drm/i915/i915_drv.h           |  10 +
>  drivers/gpu/drm/i915/i915_reg.h           |   3 +
>  drivers/gpu/drm/i915/intel_atomic_plane.c |  20 ++
>  drivers/gpu/drm/i915/intel_atomic_plane.h |   2 +
>  drivers/gpu/drm/i915/intel_bw.c           | 181 +++++++++++++++++
>  drivers/gpu/drm/i915/intel_bw.h           |  46 +++++
>  drivers/gpu/drm/i915/intel_display.c      |  40 +++-
>  drivers/gpu/drm/i915/intel_drv.h          |   2 +
>  10 files changed, 533 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/i915/intel_bw.c
>  create mode 100644 drivers/gpu/drm/i915/intel_bw.h
>
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 68106fe35a04..139a0fc19390 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -138,6 +138,7 @@ i915-y += intel_audio.o \
>         intel_atomic.o \
>         intel_atomic_plane.o \
>         intel_bios.o \
> +       intel_bw.o \
>         intel_cdclk.o \
>         intel_color.o \
>         intel_combo_phy.o \
> diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> index 5ed864752c7b..b7fa7b51c2e2 100644
> --- a/drivers/gpu/drm/i915/i915_drv.c
> +++ b/drivers/gpu/drm/i915/i915_drv.c
> @@ -70,6 +70,7 @@
>  #include "intel_overlay.h"
>  #include "intel_pipe_crc.h"
>  #include "intel_pm.h"
> +#include "intel_sideband.h"
>  #include "intel_sprite.h"
>  #include "intel_uc.h"
>  
> @@ -1435,6 +1436,232 @@ bxt_get_dram_info(struct drm_i915_private *dev_priv)
>       return 0;
>  }
>  
> +struct intel_qgv_point {
> +     u16 dclk, t_rp, t_rdpre, t_rc, t_ras, t_rcd;
> +};
> +
> +struct intel_sagv_info {
> +     struct intel_qgv_point points[3];
> +     u8 num_points;
> +     u8 num_channels;
> +     u8 t_bl;
> +     enum intel_dram_type dram_type;
> +};
> +
> +static int icl_pcode_read_mem_global_info(struct drm_i915_private *dev_priv,
> +                                       struct intel_sagv_info *si)
> +{
> +     u32 val = 0;
> +     int ret;
> +
> +     ret = sandybridge_pcode_read(dev_priv,
> +                                  ICL_PCODE_MEM_SUBSYSYSTEM_INFO |
> +                                  ICL_PCODE_MEM_SS_READ_GLOBAL_INFO,
> +                                  &val, NULL);
> +     if (ret)
> +             return ret;
> +
> +     switch (val & 0xf) {
> +     case 0:
> +             si->dram_type = INTEL_DRAM_DDR4;
> +             break;
> +     case 1:
> +             si->dram_type = INTEL_DRAM_DDR3;
> +             break;
> +     case 2:
> +             si->dram_type = INTEL_DRAM_LPDDR3;
> +             break;
> +     case 3:
> +             si->dram_type = INTEL_DRAM_LPDDR3;
> +             break;
> +     default:
> +             MISSING_CASE(val & 0xf);
> +             break;
> +     }
> +
> +     si->num_channels = (val & 0xf0) >> 4;
> +     si->num_points = (val & 0xf00) >> 8;
> +
> +     si->t_bl = si->dram_type == INTEL_DRAM_DDR4 ? 4 : 8;
> +
> +     return 0;
> +}
> +
> +static int icl_pcode_read_qgv_point_info(struct drm_i915_private *dev_priv,
> +                                      struct intel_qgv_point *sp,
> +                                      int point)
> +{
> +     u32 val = 0, val2;
> +     int ret;
> +
> +     ret = sandybridge_pcode_read(dev_priv,
> +                                  ICL_PCODE_MEM_SUBSYSYSTEM_INFO |
> +                                  
> ICL_PCODE_MEM_SS_READ_QGV_POINT_INFO(point),
> +                                  &val, &val2);
> +     if (ret)
> +             return ret;
> +
> +     sp->dclk = val & 0xffff;
> +     sp->t_rp = (val & 0xff0000) >> 16;
> +     sp->t_rcd = (val & 0xff000000) >> 24;
> +
> +     sp->t_rdpre = val2 & 0xff;
> +     sp->t_ras = (val2 & 0xff00) >> 8;
> +
> +     sp->t_rc = sp->t_rp + sp->t_ras;
> +
> +     return 0;
> +}
> +
> +static int icl_get_qgv_points(struct drm_i915_private *dev_priv,
> +                           struct intel_sagv_info *si)
> +{
> +     int i, ret;
> +
> +     ret = icl_pcode_read_mem_global_info(dev_priv, si);
> +     if (ret)
> +             return ret;
> +
> +     if (WARN_ON(si->num_points > ARRAY_SIZE(si->points)))
> +             si->num_points = ARRAY_SIZE(si->points);
> +
> +     for (i = 0; i < si->num_points; i++) {
> +             struct intel_qgv_point *sp = &si->points[i];
> +
> +             ret = icl_pcode_read_qgv_point_info(dev_priv, sp, i);
> +             if (ret)
> +                     return ret;
> +
> +             DRM_DEBUG_KMS("QGV %d: DCLK=%d tRP=%d tRDPRE=%d tRAS=%d tRCD=%d 
> tRC=%d\n",
> +                           i, sp->dclk, sp->t_rp, sp->t_rdpre, sp->t_ras,
> +                           sp->t_rcd, sp->t_rc);
> +     }
> +
> +     return 0;
> +}
> +
> +static int icl_calc_bw(int dclk, int num, int den)
> +{
> +     /* multiples of 16.666MHz (100/6) */
> +     return DIV_ROUND_CLOSEST(num * dclk * 100, den * 6);
> +}
> +
> +static int icl_sagv_max_dclk(const struct intel_sagv_info *si)
> +{
> +     u16 dclk = 0;
> +     int i;
> +
> +     for (i = 0; i < si->num_points; i++)
> +             dclk = max(dclk, si->points[i].dclk);
> +
> +     return dclk;
> +}
> +
> +struct intel_sa_info {
> +     u8 deburst, mpagesize, deprogbwlimit, displayrtids;
> +};
> +
> +static const struct intel_sa_info icl_sa_info = {
> +     .deburst = 8,
> +     .mpagesize = 16,
> +     .deprogbwlimit = 25, /* GB/s */
> +     .displayrtids = 128,
> +};
> +
> +static int icl_get_bw_info(struct drm_i915_private *dev_priv)
> +{
> +     struct intel_sagv_info si = {};
> +     const struct intel_sa_info *sa = &icl_sa_info;
> +     bool is_y_tile = true; /* assume y tile may be used */
> +     int num_channels;
> +     int deinterleave;
> +     int ipqdepth, ipqdepthpch;
> +     int dclk_max;
> +     int maxdebw;
> +     int i, ret;
> +
> +     ret = icl_get_qgv_points(dev_priv, &si);
> +     if (ret)
> +             return ret;
> +     num_channels = si.num_channels;
> +
> +     deinterleave = DIV_ROUND_UP(num_channels, is_y_tile ? 4 : 2);
> +     dclk_max = icl_sagv_max_dclk(&si);
> +
> +     ipqdepthpch = 16;
> +
> +     maxdebw = min(sa->deprogbwlimit * 1000,
> +                   icl_calc_bw(dclk_max, 16, 1) * 6 / 10); /* 60% */
> +     ipqdepth = min(ipqdepthpch, sa->displayrtids / num_channels);
> +
> +     for (i = 0; i < ARRAY_SIZE(dev_priv->max_bw); i++) {
> +             struct intel_bw_info *bi = &dev_priv->max_bw[i];
> +             int clpchgroup;
> +             int j;
> +
> +             clpchgroup = (sa->deburst * deinterleave / num_channels) << i;
> +             bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1;
> +
> +             for (j = 0; j < si.num_points; j++) {
> +                     const struct intel_qgv_point *sp = &si.points[j];
> +                     int ct, bw;
> +
> +                     /*
> +                      * Max row cycle time
> +                      *
> +                      * FIXME what is the logic behind the
> +                      * assumed burst length?
> +                      */
> +                     ct = max_t(int, sp->t_rc, sp->t_rp + sp->t_rcd +
> +                                (clpchgroup - 1) * si.t_bl + sp->t_rdpre);
> +                     bw = icl_calc_bw(sp->dclk, clpchgroup * 32 * 
> num_channels, ct);
> +
> +                     bi->deratedbw[j] = min(maxdebw,
> +                                            bw * 9 / 10); /* 90% */
> +
> +                     DRM_DEBUG_KMS("BW%d / QGV %d: num_planes=%d 
> deratedbw=%d\n",
> +                                   i, j, bi->num_planes, bi->deratedbw[j]);
> +             }
> +
> +             if (bi->num_planes == 1)
> +                     break;
> +     }
> +
> +     return 0;
> +}
> +
> +static unsigned int icl_max_bw(struct drm_i915_private *dev_priv,
> +                            int num_planes, int qgv_point)
> +{
> +     int i;
> +
> +     for (i = 0; i < ARRAY_SIZE(dev_priv->max_bw); i++) {
> +             const struct intel_bw_info *bi =
> +                     &dev_priv->max_bw[i];
> +
> +             if (num_planes >= bi->num_planes)
> +                     return bi->deratedbw[qgv_point];
> +     }
> +
> +     return 0;
> +}
> +
> +unsigned int intel_max_data_rate(struct drm_i915_private *dev_priv,
> +                              int num_planes)
> +{
> +     if (IS_ICELAKE(dev_priv))
> +             /*
> +              * FIXME with SAGV disabled maybe we can assume
> +              * point 1 will always be used? Seems to match
> +              * the behaviour observed in the wild.
> +              */
> +             return min3(icl_max_bw(dev_priv, num_planes, 0),
> +                         icl_max_bw(dev_priv, num_planes, 1),
> +                         icl_max_bw(dev_priv, num_planes, 2));
> +     else
> +             return UINT_MAX;
> +}
> +
>  static void
>  intel_get_dram_info(struct drm_i915_private *dev_priv)
>  {
> @@ -1655,6 +1882,8 @@ static int i915_driver_init_hw(struct drm_i915_private 
> *dev_priv)
>        */
>       intel_get_dram_info(dev_priv);
>  
> +     if (INTEL_GEN(dev_priv) >= 11)
> +             icl_get_bw_info(dev_priv);
>  
>       return 0;
>  
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 64fa353a62bb..d1b9c3fe5802 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -54,6 +54,7 @@
>  #include <drm/drm_cache.h>
>  #include <drm/drm_util.h>
>  #include <drm/drm_dsc.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_connector.h>
>  #include <drm/i915_mei_hdcp_interface.h>
>  
> @@ -1837,6 +1838,13 @@ struct drm_i915_private {
>               } type;
>       } dram_info;
>  
> +     struct intel_bw_info {
> +             int num_planes;
> +             int deratedbw[3];
> +     } max_bw[6];
> +
> +     struct drm_private_obj bw_obj;
> +
>       struct i915_runtime_pm runtime_pm;
>  
>       struct {
> @@ -2706,6 +2714,8 @@ extern unsigned long i915_mch_val(struct 
> drm_i915_private *dev_priv);
>  extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
>  extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
>  int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool on);
> +unsigned int intel_max_data_rate(struct drm_i915_private *dev_priv,
> +                              int num_planes);
>  
>  u32 intel_calculate_mcr_s_ss_select(struct drm_i915_private *dev_priv);
>  
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index e97c47fca645..399366a41524 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -8774,6 +8774,9 @@ enum {
>  #define   GEN6_PCODE_WRITE_MIN_FREQ_TABLE    0x8
>  #define   GEN6_PCODE_READ_MIN_FREQ_TABLE     0x9
>  #define   GEN6_READ_OC_PARAMS                        0xc
> +#define   ICL_PCODE_MEM_SUBSYSYSTEM_INFO     0xd
> +#define     ICL_PCODE_MEM_SS_READ_GLOBAL_INFO        (0x0 << 8)
> +#define     ICL_PCODE_MEM_SS_READ_QGV_POINT_INFO(point)      (((point) << 
> 16) | (0x1 << 8))
>  #define   GEN6_PCODE_READ_D_COMP             0x10
>  #define   GEN6_PCODE_WRITE_D_COMP            0x11
>  #define   HSW_PCODE_DE_WRITE_FREQ_REQ                0x17
> diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c 
> b/drivers/gpu/drm/i915/intel_atomic_plane.c
> index d11681d71add..f142c5c22d7e 100644
> --- a/drivers/gpu/drm/i915/intel_atomic_plane.c
> +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c
> @@ -114,6 +114,22 @@ intel_plane_destroy_state(struct drm_plane *plane,
>       drm_atomic_helper_plane_destroy_state(plane, state);
>  }
>  
> +unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
> +                                const struct intel_plane_state *plane_state)
> +{
> +     const struct drm_framebuffer *fb = plane_state->base.fb;
> +     unsigned int cpp = 0;
> +     int i;
> +
> +     if (!plane_state->base.visible)
> +             return 0;
> +
> +     for (i = 0; i < fb->format->num_planes; i++)
> +             cpp += fb->format->cpp[i];
> +
> +     return cpp * crtc_state->pixel_rate;
> +}
> +
>  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,
> @@ -125,6 +141,7 @@ int intel_plane_atomic_check_with_state(const struct 
> intel_crtc_state *old_crtc_
>       new_crtc_state->active_planes &= ~BIT(plane->id);
>       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_plane_state->base.visible = false;
>  
>       if (!new_plane_state->base.crtc && !old_plane_state->base.crtc)
> @@ -149,6 +166,9 @@ int intel_plane_atomic_check_with_state(const struct 
> intel_crtc_state *old_crtc_
>       if (new_plane_state->base.visible || old_plane_state->base.visible)
>               new_crtc_state->update_planes |= BIT(plane->id);
>  
> +     new_crtc_state->data_rate[plane->id] =
> +             intel_plane_data_rate(new_crtc_state, new_plane_state);
> +
>       return intel_plane_atomic_calc_changes(old_crtc_state,
>                                              &new_crtc_state->base,
>                                              old_plane_state,
> diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.h 
> b/drivers/gpu/drm/i915/intel_atomic_plane.h
> index 14678620440f..0a9651376d0e 100644
> --- a/drivers/gpu/drm/i915/intel_atomic_plane.h
> +++ b/drivers/gpu/drm/i915/intel_atomic_plane.h
> @@ -15,6 +15,8 @@ struct intel_plane_state;
>  
>  extern const struct drm_plane_helper_funcs intel_plane_helper_funcs;
>  
> +unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
> +                                const struct intel_plane_state *plane_state);
>  void intel_update_plane(struct intel_plane *plane,
>                       const struct intel_crtc_state *crtc_state,
>                       const struct intel_plane_state *plane_state);
> diff --git a/drivers/gpu/drm/i915/intel_bw.c b/drivers/gpu/drm/i915/intel_bw.c
> new file mode 100644
> index 000000000000..304bf87f0a2e
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_bw.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2019 Intel Corporation
> + */
> +
> +#include <drm/drm_atomic_state_helper.h>
> +
> +#include "intel_bw.h"
> +#include "intel_drv.h"
> +
> +static unsigned int intel_bw_crtc_num_active_planes(const struct 
> intel_crtc_state *crtc_state)
> +{
> +     /*
> +      * We assume cursors are small enough
> +      * to not not cause bandwidth problems.
> +      */
> +     return hweight8(crtc_state->active_planes & ~BIT(PLANE_CURSOR));
> +}
> +
> +static unsigned int intel_bw_crtc_data_rate(const struct intel_crtc_state 
> *crtc_state)
> +{
> +     struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> +     unsigned int data_rate = 0;
> +     enum plane_id plane_id;
> +
> +     for_each_plane_id_on_crtc(crtc, plane_id) {
> +             /*
> +              * We assume cursors are small enough
> +              * to not not cause bandwidth problems.
> +              */
> +             if (plane_id == PLANE_CURSOR)
> +                     continue;
> +
> +             data_rate += crtc_state->data_rate[plane_id];
> +     }
> +
> +     return data_rate;
> +}
> +
> +void intel_bw_crtc_update(struct intel_bw_state *bw_state,
> +                       const struct intel_crtc_state *crtc_state)
> +{
> +     struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> +
> +     bw_state->data_rate[crtc->pipe] =
> +             intel_bw_crtc_data_rate(crtc_state);
> +     bw_state->num_active_planes[crtc->pipe] =
> +             intel_bw_crtc_num_active_planes(crtc_state);
> +
> +     DRM_DEBUG_KMS("pipe %c data rate %u num active planes %u\n",
> +                   pipe_name(crtc->pipe),
> +                   bw_state->data_rate[crtc->pipe],
> +                   bw_state->num_active_planes[crtc->pipe]);
> +}
> +
> +static unsigned int intel_bw_num_active_planes(struct drm_i915_private 
> *dev_priv,
> +                                            const struct intel_bw_state 
> *bw_state)
> +{
> +     unsigned int num_active_planes = 0;
> +     enum pipe pipe;
> +
> +     for_each_pipe(dev_priv, pipe)
> +             num_active_planes += bw_state->num_active_planes[pipe];
> +
> +     return num_active_planes;
> +}
> +
> +static unsigned int intel_bw_data_rate(struct drm_i915_private *dev_priv,
> +                                    const struct intel_bw_state *bw_state)
> +{
> +     unsigned int data_rate = 0;
> +     enum pipe pipe;
> +
> +     for_each_pipe(dev_priv, pipe)
> +             data_rate += bw_state->data_rate[pipe];
> +
> +     return data_rate;
> +}
> +
> +int intel_bw_atomic_check(struct intel_atomic_state *state)
> +{
> +     struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> +     struct intel_crtc_state *new_crtc_state, *old_crtc_state;
> +     struct intel_bw_state *bw_state = NULL;
> +     unsigned int data_rate, max_data_rate;
> +     unsigned int num_active_planes;
> +     struct intel_crtc *crtc;
> +     int i;
> +
> +     /* FIXME earlier gens need some checks too */
> +     if (INTEL_GEN(dev_priv) < 11)
> +             return 0;
> +
> +     for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
> +                                         new_crtc_state, i) {
> +             unsigned int old_data_rate =
> +                     intel_bw_crtc_data_rate(old_crtc_state);
> +             unsigned int new_data_rate =
> +                     intel_bw_crtc_data_rate(new_crtc_state);
> +             unsigned int old_active_planes =
> +                     intel_bw_crtc_num_active_planes(old_crtc_state);
> +             unsigned int new_active_planes =
> +                     intel_bw_crtc_num_active_planes(new_crtc_state);
> +
> +             /*
> +              * Avoid locking the bw state when
> +              * nothing significant has changed.
> +              */
> +             if (old_data_rate == new_data_rate &&
> +                 old_active_planes == new_active_planes)
> +                     continue;
> +
> +             bw_state  = intel_atomic_get_bw_state(state);
> +             if (IS_ERR(bw_state))
> +                     return PTR_ERR(bw_state);
> +
> +             bw_state->data_rate[crtc->pipe] = new_data_rate;
> +             bw_state->num_active_planes[crtc->pipe] = new_active_planes;
> +
> +             DRM_DEBUG_KMS("pipe %c data rate %u num active planes %u\n",
> +                           pipe_name(crtc->pipe),
> +                           bw_state->data_rate[crtc->pipe],
> +                           bw_state->num_active_planes[crtc->pipe]);
> +     }
> +
> +     if (!bw_state)
> +             return 0;
> +
> +     data_rate = intel_bw_data_rate(dev_priv, bw_state);
> +     num_active_planes = intel_bw_num_active_planes(dev_priv, bw_state);
> +
> +     max_data_rate = intel_max_data_rate(dev_priv, num_active_planes);
> +
> +     data_rate = DIV_ROUND_UP(data_rate, 1000);
> +
> +     if (data_rate > max_data_rate) {
> +             DRM_DEBUG_KMS("Bandwidth %u MB/s exceeds max available %d MB/s 
> (%d active planes)\n",
> +                           data_rate, max_data_rate, num_active_planes);
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static struct drm_private_state *intel_bw_duplicate_state(struct 
> drm_private_obj *obj)
> +{
> +     struct intel_bw_state *state;
> +
> +     state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
> +     if (!state)
> +             return NULL;
> +
> +     __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
> +
> +     return &state->base;
> +}
> +
> +static void intel_bw_destroy_state(struct drm_private_obj *obj,
> +                                struct drm_private_state *state)
> +{
> +     kfree(state);
> +}
> +
> +static const struct drm_private_state_funcs intel_bw_funcs = {
> +     .atomic_duplicate_state = intel_bw_duplicate_state,
> +     .atomic_destroy_state = intel_bw_destroy_state,
> +};
> +
> +int intel_bw_init(struct drm_i915_private *dev_priv)
> +{
> +     struct intel_bw_state *state;
> +
> +     state = kzalloc(sizeof(*state), GFP_KERNEL);
> +     if (!state)
> +             return -ENOMEM;
> +
> +     drm_atomic_private_obj_init(&dev_priv->drm, &dev_priv->bw_obj,
> +                                 &state->base, &intel_bw_funcs);
> +
> +     return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/intel_bw.h b/drivers/gpu/drm/i915/intel_bw.h
> new file mode 100644
> index 000000000000..c14272ca5b59
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_bw.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2019 Intel Corporation
> + */
> +
> +#ifndef __INTEL_BW_H__
> +#define __INTEL_BW_H__
> +
> +#include <drm/drm_atomic.h>
> +
> +#include "i915_drv.h"
> +#include "intel_display.h"
> +
> +struct drm_i915_private;
> +struct intel_atomic_state;
> +struct intel_crtc_state;
> +
> +struct intel_bw_state {
> +     struct drm_private_state base;
> +
> +     unsigned int data_rate[I915_MAX_PIPES];
> +     u8 num_active_planes[I915_MAX_PIPES];
> +};
> +
> +#define to_intel_bw_state(x) container_of((x), struct intel_bw_state, base)
> +
> +static inline struct intel_bw_state *
> +intel_atomic_get_bw_state(struct intel_atomic_state *state)
> +{
> +     struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> +     struct drm_private_state *bw_state;
> +
> +     bw_state = drm_atomic_get_private_obj_state(&state->base,
> +                                                 &dev_priv->bw_obj);
> +     if (IS_ERR(bw_state))
> +             return ERR_CAST(bw_state);
> +
> +     return to_intel_bw_state(bw_state);
> +}
> +
> +int intel_bw_init(struct drm_i915_private *dev_priv);
> +int intel_bw_atomic_check(struct intel_atomic_state *state);
> +void intel_bw_crtc_update(struct intel_bw_state *bw_state,
> +                       const struct intel_crtc_state *crtc_state);
> +
> +#endif /* __INTEL_BW_H__ */
> diff --git a/drivers/gpu/drm/i915/intel_display.c 
> b/drivers/gpu/drm/i915/intel_display.c
> index d81ec80e34f6..a955840b73cb 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -50,6 +50,7 @@
>  #include "intel_acpi.h"
>  #include "intel_atomic.h"
>  #include "intel_atomic_plane.h"
> +#include "intel_bw.h"
>  #include "intel_color.h"
>  #include "intel_cdclk.h"
>  #include "intel_crt.h"
> @@ -2863,6 +2864,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;
>  
>       if (plane->id == PLANE_PRIMARY)
>               intel_pre_disable_primary_noatomic(&crtc->base);
> @@ -6590,6 +6592,8 @@ static void intel_crtc_disable_noatomic(struct drm_crtc 
> *crtc,
>       struct intel_encoder *encoder;
>       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>       struct drm_i915_private *dev_priv = to_i915(crtc->dev);
> +     struct intel_bw_state *bw_state =
> +             to_intel_bw_state(dev_priv->bw_obj.state);
>       enum intel_display_power_domain domain;
>       struct intel_plane *plane;
>       u64 domains;
> @@ -6652,6 +6656,9 @@ static void intel_crtc_disable_noatomic(struct drm_crtc 
> *crtc,
>       dev_priv->active_crtcs &= ~(1 << intel_crtc->pipe);
>       dev_priv->min_cdclk[intel_crtc->pipe] = 0;
>       dev_priv->min_voltage_level[intel_crtc->pipe] = 0;
> +
> +     bw_state->data_rate[intel_crtc->pipe] = 0;
> +     bw_state->num_active_planes[intel_crtc->pipe] = 0;
>  }
>  
>  /*
> @@ -11176,6 +11183,7 @@ int intel_plane_atomic_calc_changes(const struct 
> intel_crtc_state *old_crtc_stat
>       if (!is_crtc_enabled) {
>               plane_state->visible = visible = false;
>               to_intel_crtc_state(crtc_state)->active_planes &= 
> ~BIT(plane->id);
> +             to_intel_crtc_state(crtc_state)->data_rate[plane->id] = 0;
>       }
>  
>       if (!was_visible && !visible)
> @@ -13296,7 +13304,15 @@ static int intel_atomic_check(struct drm_device *dev,
>               return ret;
>  
>       intel_fbc_choose_crtc(dev_priv, intel_state);
> -     return calc_watermark_data(intel_state);
> +     ret = calc_watermark_data(intel_state);
> +     if (ret)
> +             return ret;
> +
> +     ret = intel_bw_atomic_check(intel_state);
> +     if (ret)
> +             return ret;
> +
> +     return 0;
>  }
>  
>  static int intel_atomic_prepare_commit(struct drm_device *dev,
> @@ -15696,6 +15712,10 @@ int intel_modeset_init(struct drm_device *dev)
>  
>       drm_mode_config_init(dev);
>  
> +     ret = intel_bw_init(dev_priv);
> +     if (ret)
> +             return ret;
> +
>       dev->mode_config.min_width = 0;
>       dev->mode_config.min_height = 0;
>  
> @@ -16318,8 +16338,11 @@ static void intel_modeset_readout_hw_state(struct 
> drm_device *dev)
>       drm_connector_list_iter_end(&conn_iter);
>  
>       for_each_intel_crtc(dev, crtc) {
> +             struct intel_bw_state *bw_state =
> +                     to_intel_bw_state(dev_priv->bw_obj.state);
>               struct intel_crtc_state *crtc_state =
>                       to_intel_crtc_state(crtc->base.state);
> +             struct intel_plane *plane;
>               int min_cdclk = 0;
>  
>               memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
> @@ -16358,6 +16381,21 @@ static void intel_modeset_readout_hw_state(struct 
> drm_device *dev)
>               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);
> +
> +                     /*
> +                      * FIXME don't have the fb yet, so can't
> +                      * use intel_plane_data_rate() :(
> +                      */
> +                     if (plane_state->base.visible)
> +                             crtc_state->data_rate[plane->id] =
> +                                     4 * crtc_state->pixel_rate;
> +             }
> +
> +             intel_bw_crtc_update(bw_state, crtc_state);
> +
>               intel_pipe_config_sanity_check(dev_priv, crtc_state);
>       }
>  }
> diff --git a/drivers/gpu/drm/i915/intel_drv.h 
> b/drivers/gpu/drm/i915/intel_drv.h
> index 4049e03d2c0d..47f551601a05 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -885,6 +885,8 @@ struct intel_crtc_state {
>  
>       struct intel_crtc_wm_state wm;
>  
> +     u32 data_rate[I915_MAX_PLANES];
> +
>       /* Gamma mode programmed on the pipe */
>       u32 gamma_mode;
>  


_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to