[Intel-gfx] [PATCH 10/16] drm/i915: Improve watermark dirtyness checks
Paulo Zanoni
przanoni at gmail.com
Fri Oct 11 18:12:38 CEST 2013
2013/10/11 Ville Syrjälä <ville.syrjala at linux.intel.com>:
> On Fri, Oct 11, 2013 at 12:02:53PM -0300, Paulo Zanoni wrote:
>> 2013/10/9 <ville.syrjala at linux.intel.com>:
>> > From: Ville Syrjälä <ville.syrjala at linux.intel.com>
>> >
>> > Currently hsw_write_vm_values() may write to certain watermark
>> > registers needlessly. For instance it will disable LP1+ watermarks
>> > around WM_PIPE changes even though that's not needed. Also if only,
>> > say, LP3 changes, the current code will again disable all LP1+
>> > watermarks even though only LP3 needs to be reconfigured.
>> >
>> > Add an easy to read function that will compute the dirtyness of the
>> > watermarks, and use that information to further optimize the watermark
>> > programming.
>>
>> Clever solution to the problem!
>>
>> Some comments below.
>>
>>
>> >
>> > Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
>> > ---
>> > drivers/gpu/drm/i915/intel_pm.c | 95 +++++++++++++++++++++++++++++++++--------
>> > 1 file changed, 77 insertions(+), 18 deletions(-)
>> >
>> > diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
>> > index bed96fb..5bd8c73 100644
>> > --- a/drivers/gpu/drm/i915/intel_pm.c
>> > +++ b/drivers/gpu/drm/i915/intel_pm.c
>> > @@ -2776,6 +2776,63 @@ static struct intel_pipe_wm *hsw_find_best_result(struct drm_device *dev,
>> > }
>> > }
>> >
>> > +/* dirty bits used to track which watermarks need changes */
>> > +#define WM_DIRTY_PIPE(pipe) (1 << (pipe))
>> > +#define WM_DIRTY_LINETIME(pipe) (1 << (8 + (pipe)))
>> > +#define WM_DIRTY_LP(wm_lp) (1 << (15 + (wm_lp)))
>> > +#define WM_DIRTY_LP_ALL (WM_DIRTY_LP(1) | WM_DIRTY_LP(2) | WM_DIRTY_LP(3))
>> > +#define WM_DIRTY_FBC (1 << 24)
>> > +#define WM_DIRTY_DDB (1 << 25)
>> > +
>> > +static unsigned int ilk_compute_wm_dirty(struct drm_device *dev,
>> > + const struct hsw_wm_values *old,
>> > + const struct hsw_wm_values *new)
>> > +{
>> > + unsigned int dirty = 0;
>> > + enum pipe pipe;
>> > + int wm_lp;
>> > +
>> > + for_each_pipe(pipe) {
>> > + if (old->wm_linetime[pipe] != new->wm_linetime[pipe]) {
>> > + dirty |= WM_DIRTY_LINETIME(pipe);
>> > + /* Must disable LP1+ watermarks too */
>> > + dirty |= WM_DIRTY_LP_ALL;
>> > + }
>> > +
>> > + if (old->wm_pipe[pipe] != new->wm_pipe[pipe])
>> > + dirty |= WM_DIRTY_PIPE(pipe);
>>
>> I think this one also needs WM_DIRTY_LP_ALL. I don't think changing
>> level zero without stopping the LP levels is safe.
>
> Dunno. Spec doesn't tell me to do it. Although I haven't even bothered
> to check how hard it would be to cook up a configuration where just
> WM_PIPE changes but WM_LPx stays the same.
>
> Actually the spec doesn't even say that we should disable a particular
> LP1+ watermark if we want to change it. It just says that you have to
> disable and enable them in order.
>
>>
>>
>> > + }
>> > +
>> > + if (old->enable_fbc_wm != new->enable_fbc_wm) {
>> > + dirty |= WM_DIRTY_FBC;
>> > + /* Must disable LP1+ watermarks too */
>> > + dirty |= WM_DIRTY_LP_ALL;
>> > + }
>> > +
>> > + if (old->partitioning != new->partitioning) {
>> > + dirty |= WM_DIRTY_DDB;
>> > + /* Must disable LP1+ watermarks too */
>> > + dirty |= WM_DIRTY_LP_ALL;
>> > + }
>> > +
>> > + /* LP1+ watermarks already deemed dirty, no need to continue */
>> > + if (dirty & WM_DIRTY_LP_ALL)
>> > + return dirty;
>> > +
>> > + /* Find the lowest numbered LP1+ watermark in need of an update... */
>> > + for (wm_lp = 1; wm_lp <= 3; wm_lp++) {
>> > + if (old->wm_lp[wm_lp - 1] != new->wm_lp[wm_lp - 1] ||
>> > + old->wm_lp_spr[wm_lp - 1] != new->wm_lp_spr[wm_lp - 1])
>> > + break;
>> > + }
>> > +
>> > + /* ...and mark it and all higher numbered LP1+ watermarks as dirty */
>> > + for (; wm_lp <= 3; wm_lp++)
>> > + dirty |= WM_DIRTY_LP(wm_lp);
>> > +
>> > + return dirty;
>> > +}
>> > +
>> > /*
>> > * The spec says we shouldn't write when we don't need, because every write
>> > * causes WMs to be re-evaluated, expending some power.
>> > @@ -2784,6 +2841,7 @@ static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
>> > struct hsw_wm_values *results)
>> > {
>> > struct hsw_wm_values previous;
>> > + unsigned int dirty;
>> > uint32_t val;
>> >
>> > previous.wm_pipe[0] = I915_READ(WM0_PIPEA_ILK);
>> > @@ -2804,31 +2862,32 @@ static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
>> >
>> > previous.enable_fbc_wm = !(I915_READ(DISP_ARB_CTL) & DISP_FBC_WM_DIS);
>> >
>> > - if (memcmp(results, &previous, sizeof(*results)) == 0)
>>
>> Oh, you've just removed the memcmp I complained about... Still, I
>> believe it had a bug.
>>
>>
>> > + dirty = ilk_compute_wm_dirty(dev_priv->dev, &previous, results);
>> > + if (!dirty)
>> > return;
>> >
>> > - if (previous.wm_lp[2] != 0)
>> > + if (dirty & WM_DIRTY_LP(3) && previous.wm_lp[2] != 0)
>> > I915_WRITE(WM3_LP_ILK, 0);
>> > - if (previous.wm_lp[1] != 0)
>> > + if (dirty & WM_DIRTY_LP(2) && previous.wm_lp[1] != 0)
>> > I915_WRITE(WM2_LP_ILK, 0);
>> > - if (previous.wm_lp[0] != 0)
>> > + if (dirty & WM_DIRTY_LP(1) && previous.wm_lp[0] != 0)
>> > I915_WRITE(WM1_LP_ILK, 0);
>> >
>> > - if (previous.wm_pipe[0] != results->wm_pipe[0])
>> > + if (dirty & WM_DIRTY_PIPE(PIPE_A))
>> > I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]);
>> > - if (previous.wm_pipe[1] != results->wm_pipe[1])
>> > + if (dirty & WM_DIRTY_PIPE(PIPE_B))
>> > I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]);
>> > - if (previous.wm_pipe[2] != results->wm_pipe[2])
>> > + if (dirty & WM_DIRTY_PIPE(PIPE_C))
>> > I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]);
>> >
>> > - if (previous.wm_linetime[0] != results->wm_linetime[0])
>> > + if (dirty & WM_DIRTY_LINETIME(PIPE_A))
>> > I915_WRITE(PIPE_WM_LINETIME(PIPE_A), results->wm_linetime[0]);
>> > - if (previous.wm_linetime[1] != results->wm_linetime[1])
>> > + if (dirty & WM_DIRTY_LINETIME(PIPE_B))
>> > I915_WRITE(PIPE_WM_LINETIME(PIPE_B), results->wm_linetime[1]);
>> > - if (previous.wm_linetime[2] != results->wm_linetime[2])
>> > + if (dirty & WM_DIRTY_LINETIME(PIPE_C))
>> > I915_WRITE(PIPE_WM_LINETIME(PIPE_C), results->wm_linetime[2]);
>> >
>> > - if (previous.partitioning != results->partitioning) {
>> > + if (dirty & WM_DIRTY_DDB) {
>> > val = I915_READ(WM_MISC);
>> > if (results->partitioning == INTEL_DDB_PART_1_2)
>> > val &= ~WM_MISC_DATA_PARTITION_5_6;
>> > @@ -2837,7 +2896,7 @@ static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
>> > I915_WRITE(WM_MISC, val);
>> > }
>> >
>> > - if (previous.enable_fbc_wm != results->enable_fbc_wm) {
>> > + if (dirty & WM_DIRTY_FBC) {
>> > val = I915_READ(DISP_ARB_CTL);
>> > if (results->enable_fbc_wm)
>> > val &= ~DISP_FBC_WM_DIS;
>> > @@ -2846,18 +2905,18 @@ static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
>> > I915_WRITE(DISP_ARB_CTL, val);
>> > }
>> >
>> > - if (previous.wm_lp_spr[0] != results->wm_lp_spr[0])
>> > + if (dirty & WM_DIRTY_LP(1) && previous.wm_lp_spr[0] != results->wm_lp_spr[0])
>> > I915_WRITE(WM1S_LP_ILK, results->wm_lp_spr[0]);
>>
>> As far as I understood, if previous.wm_lp_spr[X] !=
>> results->wm_lp_spr[X], then for sure "dirty & WM_DIRTY_LP(X)", right?
>> So the "dirty" check would be redundant here. And we can't remove the
>> second part because we can have "dirty & WM_DIRTY_LP(X) while the
>> sprite values don't change.
>
> Right, because there's no separate enable bit for the sprite LP1+
> watermarks, the dirty check is not really needed here. You want me to
> remove it?
It's up to you, since it's not really a bug. Sometimes I ask just to
make sure I understand everything :)
I was more worried about the other question above, I'd play the safer
side and mark levels 1+ dirty when level 0 also changes.
Thanks,
Paulo
>
>>
>> > - if (previous.wm_lp_spr[1] != results->wm_lp_spr[1])
>> > + if (dirty & WM_DIRTY_LP(2) && previous.wm_lp_spr[1] != results->wm_lp_spr[1])
>> > I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]);
>> > - if (previous.wm_lp_spr[2] != results->wm_lp_spr[2])
>> > + if (dirty & WM_DIRTY_LP(3) && previous.wm_lp_spr[2] != results->wm_lp_spr[2])
>> > I915_WRITE(WM3S_LP_IVB, results->wm_lp_spr[2]);
>> >
>> > - if (results->wm_lp[0] != 0)
>> > + if (dirty & WM_DIRTY_LP(1) && results->wm_lp[0] != 0)
>> > I915_WRITE(WM1_LP_ILK, results->wm_lp[0]);
>> > - if (results->wm_lp[1] != 0)
>> > + if (dirty & WM_DIRTY_LP(2) && results->wm_lp[1] != 0)
>> > I915_WRITE(WM2_LP_ILK, results->wm_lp[1]);
>> > - if (results->wm_lp[2] != 0)
>> > + if (dirty & WM_DIRTY_LP(3) && results->wm_lp[2] != 0)
>> > I915_WRITE(WM3_LP_ILK, results->wm_lp[2]);
>> >
>> > dev_priv->wm.hw = *results;
>> > --
>> > 1.8.1.5
>> >
>> > _______________________________________________
>> > Intel-gfx mailing list
>> > Intel-gfx at lists.freedesktop.org
>> > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
>>
>>
>>
>> --
>> Paulo Zanoni
>
> --
> Ville Syrjälä
> Intel OTC
--
Paulo Zanoni
More information about the Intel-gfx
mailing list