[PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set

Ville Syrjälä ville.syrjala at linux.intel.com
Fri Oct 25 19:45:15 UTC 2019


On Fri, Oct 25, 2019 at 09:23:47PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 21-10-2019 16:39, Ville Syrjälä wrote:
> > On Sun, Oct 20, 2019 at 08:19:33PM +0200, Hans de Goede wrote:
> >> Since commit 051a6d8d3ca0 ("drm/i915: Move LUT programming to happen after
> >> vblank waits"), I am seeing an ugly colored flash of the first few display
> >> lines on 2 Cherry Trail devices when the gamma table gets set for the first
> >> time. A blue flash on a GPD win and a yellow flash on an Asus T100HA.
> >>
> >> The problem is that since this change, the LUT is programmed after the
> >> write *and latching* of the double-buffered register which causes the LUT
> >> to be used starting at the next frame. This means that the old LUT is still
> >> used for the first couple of lines of the display. If no LUT was in use
> >> before then the LUT registers may contain bogus values. This leads to
> >> messed up colors until the new LUT values are written. At least on CHT DSI
> >> panels this causes messed up colors on the first few lines.
> >>
> >> This commit fixes this by adding a load_lut_before_commit boolean,
> >> modifying intel_begin_crtc_commit to load the luts earlier if this is set,
> >> and setting this from intel_color_check when a LUT table was not in use
> >> before (and thus may contain bogus values), or when the table size
> >> changes.
> > 
> > The real solution is vblank workers, which I have somewhat implemented
> > here:
> > git://github.com/vsyrjala/linux.git vblank_worker_8_kthread
> > 
> > Though even with the qos tricks there we still probably can't quite make
> > it in time. Essentially we have a bit less than one scanline after start
> > of vblank to do the work before pixels start to flow through the pipe.
> > We might be extend that to almost four scanlines but that partocular
> > thing is documeted as debug feature so not sure we should really use it.
> > Also I don't think four scanlines is always enough either. So it's still
> > very much possible that we get the first 100 or so pixels with the old LUT.
> 
> Thank you for the info and for the review.
> 
> 
> >> Fixes: 051a6d8d3ca0 ("drm/i915: Move LUT programming to happen after vblank waits")
> >> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> >> ---
> >>   drivers/gpu/drm/i915/display/intel_color.c    | 26 +++++++++++++++++++
> >>   drivers/gpu/drm/i915/display/intel_display.c  |  7 +++++
> >>   .../drm/i915/display/intel_display_types.h    |  3 +++
> >>   3 files changed, 36 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c
> >> index 71a0201437a9..0da6dcc5bebd 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_color.c
> >> +++ b/drivers/gpu/drm/i915/display/intel_color.c
> >> @@ -1052,6 +1052,32 @@ intel_color_add_affected_planes(struct intel_crtc_state *new_crtc_state)
> >>   		new_crtc_state->update_planes |= BIT(plane->id);
> >>   	}
> >>   
> >> +	/*
> >> +	 * Normally we load the LUTs after vblank / after the double-buffer
> >> +	 * registers written by commit have been latched, this avoids a
> >> +	 * gamma change mid-way the screen. This does mean that the first
> >> +	 * few lines of the display will (sometimes) still use the old
> >> +	 * table. This is fine when changing an existing LUT, but if this
> >> +	 * is the first time the LUT gets loaded, then the hw may contain
> >> +	 * random values, causing the first lines to have funky colors.
> >> +	 *
> >> +	 * So if were enabling a LUT for the first time or changing the table
> >> +	 * size, then we must do this before the commit to avoid corrupting
> >> +	 * the first lines of the display.
> >> +	 */
> >> +	if (!old_crtc_state->base.gamma_lut && new_crtc_state->base.gamma_lut)
> >> +		new_crtc_state->load_lut_before_commit = true;
> >> +	else if (!old_crtc_state->base.degamma_lut &&
> >> +		 new_crtc_state->base.degamma_lut)
> >> +		new_crtc_state->load_lut_before_commit = true;
> >> +	else if (old_crtc_state->base.gamma_lut &&
> >> +		 new_crtc_state->base.gamma_lut &&
> >> +		 lut_is_legacy(old_crtc_state->base.gamma_lut) !=
> >> +			lut_is_legacy(new_crtc_state->base.gamma_lut))
> >> +		new_crtc_state->load_lut_before_commit = true;
> >> +	else
> >> +		new_crtc_state->load_lut_before_commit = false;
> > 
> > The 'no gamma -> yes gamma' thing I might be willing to accept. The rest
> > not so much. I was already pondering about such optimizations for the
> > plane gamma/csc stuff in my vblank branch.
> 
> Ok, so I can submit a v2 based on dinq with only the
> 	if (!old_crtc_state->base.gamma_lut && new_crtc_state->base.gamma_lut)
> check, or
> 
> > But for the fastboot case I think what we could do is just sanitize
> > the LUT(s) after readout if gamma wasn't enabled by the BIOS.
> 
> We could do this, but this falls a bit outside of my expertise, I would be
> more then happy to test a patch on one of the machines which needs this
> LUTS sanitizing though. I get a very visible flash of a couple of bright
> blue or yellow (2 different machines) on the upper few lines the first
> time the gamma table gets loaded, so verifying that the sanitation kicks
> in is easy.

Actually, how recent is your kernel?

Some issues with the gamma readout were fixed quite recently:
9b000b47cc18 ("drm/i915/color: fix broken gamma state-checker during boot")
d50341274d01 ("drm/i915/color: move check of gamma_enable to specific func/platform")

If those don't help we could try the quick path and just blast the
gamma from orbit like so:

@@ -16787,18 +16787,23 @@ static int intel_initial_commit(struct drm_device *dev)
 			goto out;
 		}
 
+		/*
+		 * Let's just turn off the BIOS leftover LUT(s).
+		 *
+		 * FIXME maybe we shouldn't do this, and instead
+		 * we should let fb_helper/whatever replace the
+		 * LUT(s) when they start to actually render stuff.
+		 * But for now we may get an ugly flash due to
+		 * non-atomic gamma updates.
+		 */
+		drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
+		drm_property_replace_blob(&crtc_state->gamma_lut, NULL);
+		crtc_state->color_mgmt_changed = true;
+
 		if (crtc_state->active) {
 			ret = drm_atomic_add_affected_planes(state, crtc);
 			if (ret)
 				goto out;
-
-			/*
-			 * FIXME hack to force a LUT update to avoid the
-			 * plane update forcing the pipe gamma on without
-			 * having a proper LUT loaded. Remove once we
-			 * have readout for pipe gamma enable.
-			 */
-			crtc_state->color_mgmt_changed = true;
 		}
 	}

-- 
Ville Syrjälä
Intel


More information about the dri-devel mailing list