Re: [PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set v2
On Mon, Oct 28, 2019 at 07:58:51PM +0100, 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 commit_pipe_config() to load the luts earlier if this is set. > and setting this from intel_color_check when enabling gamma (rather then > updating an existing gamma table). > > Changes in v2: > -Simply check for setting load_lut_before_commit to: > if (!old_crtc_state->gamma_enable && new_crtc_state->gamma_enable) > > Fixes: 051a6d8d3ca0 ("drm/i915: Move LUT programming to happen after vblank > waits") > Signed-off-by: Hans de Goede > --- > drivers/gpu/drm/i915/display/intel_color.c | 14 ++ > drivers/gpu/drm/i915/display/intel_display.c | 6 +- > drivers/gpu/drm/i915/display/intel_display_types.h | 3 +++ > 3 files changed, 22 insertions(+), 1 deletion(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_color.c > b/drivers/gpu/drm/i915/display/intel_color.c > index fa44eb73d088..954a232c15d1 100644 > --- a/drivers/gpu/drm/i915/display/intel_color.c > +++ b/drivers/gpu/drm/i915/display/intel_color.c > @@ -1063,6 +1063,8 @@ intel_color_add_affected_planes(struct intel_crtc_state > *new_crtc_state) > intel_atomic_get_old_crtc_state(state, crtc); > struct intel_plane *plane; > > + new_crtc_state->load_lut_before_commit = false; > + > if (!new_crtc_state->base.active || > drm_atomic_crtc_needs_modeset(_crtc_state->base)) > return 0; > @@ -1071,6 +1073,18 @@ intel_color_add_affected_planes(struct > intel_crtc_state *new_crtc_state) > new_crtc_state->csc_enable == old_crtc_state->csc_enable) > return 0; > > + /* > + * 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. > + */ > + if (!old_crtc_state->gamma_enable && new_crtc_state->gamma_enable) > + new_crtc_state->load_lut_before_commit = true; Unfortunately gamma_enable is not abstract enough to cover all platforms. > + > for_each_intel_plane_on_crtc(_priv->drm, crtc, plane) { > struct intel_plane_state *plane_state; > > diff --git a/drivers/gpu/drm/i915/display/intel_display.c > b/drivers/gpu/drm/i915/display/intel_display.c > index cbf9cf30050c..6b1dc5a5aeb1 100644 > --- a/drivers/gpu/drm/i915/display/intel_display.c > +++ b/drivers/gpu/drm/i915/display/intel_display.c > @@ -14168,8 +14168,11 @@ static void commit_pipe_config(struct > intel_atomic_state *state, >*/ > if (!modeset) { > if (new_crtc_state->base.color_mgmt_changed || > - new_crtc_state->update_pipe) > + new_crtc_state->update_pipe) { > + if (new_crtc_state->load_lut_before_commit) > + intel_color_load_luts(new_crtc_state); We don't want to do this from within the vblank evade critical section, so needs to be moved earlier. Lemme try to cook up something... > intel_color_commit(new_crtc_state); > + } > > if (INTEL_GEN(dev_priv) >= 9) > skl_detach_scalers(new_crtc_state); > @@ -14717,6 +14720,7 @@ static void intel_atomic_commit_tail(struct > intel_atomic_state *state) > for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { > if (new_crtc_state->base.active && > !needs_modeset(new_crtc_state) && > + !new_crtc_state->load_lut_before_commit && > (new_crtc_state->base.color_mgmt_changed || >new_crtc_state->update_pipe)) > intel_color_load_luts(new_crtc_state); > diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h
[PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set v2
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 commit_pipe_config() to load the luts earlier if this is set. and setting this from intel_color_check when enabling gamma (rather then updating an existing gamma table). Changes in v2: -Simply check for setting load_lut_before_commit to: if (!old_crtc_state->gamma_enable && new_crtc_state->gamma_enable) Fixes: 051a6d8d3ca0 ("drm/i915: Move LUT programming to happen after vblank waits") Signed-off-by: Hans de Goede --- drivers/gpu/drm/i915/display/intel_color.c | 14 ++ drivers/gpu/drm/i915/display/intel_display.c | 6 +- drivers/gpu/drm/i915/display/intel_display_types.h | 3 +++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index fa44eb73d088..954a232c15d1 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -1063,6 +1063,8 @@ intel_color_add_affected_planes(struct intel_crtc_state *new_crtc_state) intel_atomic_get_old_crtc_state(state, crtc); struct intel_plane *plane; + new_crtc_state->load_lut_before_commit = false; + if (!new_crtc_state->base.active || drm_atomic_crtc_needs_modeset(_crtc_state->base)) return 0; @@ -1071,6 +1073,18 @@ intel_color_add_affected_planes(struct intel_crtc_state *new_crtc_state) new_crtc_state->csc_enable == old_crtc_state->csc_enable) return 0; + /* +* 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. +*/ + if (!old_crtc_state->gamma_enable && new_crtc_state->gamma_enable) + new_crtc_state->load_lut_before_commit = true; + for_each_intel_plane_on_crtc(_priv->drm, crtc, plane) { struct intel_plane_state *plane_state; diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index cbf9cf30050c..6b1dc5a5aeb1 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -14168,8 +14168,11 @@ static void commit_pipe_config(struct intel_atomic_state *state, */ if (!modeset) { if (new_crtc_state->base.color_mgmt_changed || - new_crtc_state->update_pipe) + new_crtc_state->update_pipe) { + if (new_crtc_state->load_lut_before_commit) + intel_color_load_luts(new_crtc_state); intel_color_commit(new_crtc_state); + } if (INTEL_GEN(dev_priv) >= 9) skl_detach_scalers(new_crtc_state); @@ -14717,6 +14720,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { if (new_crtc_state->base.active && !needs_modeset(new_crtc_state) && + !new_crtc_state->load_lut_before_commit && (new_crtc_state->base.color_mgmt_changed || new_crtc_state->update_pipe)) intel_color_load_luts(new_crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 40184e823c84..6bcc997b7ecb 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -990,6 +990,9 @@ struct intel_crtc_state { /* enable pipe csc? */ bool csc_enable; + /* load luts before color settings commit */ + bool
Re: [PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set
Hi, On 25-10-2019 21:45, Ville Syrjälä wrote: 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 --- 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
Re: [PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set
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 > >> --- > >> 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
Re: [PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set
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 --- 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. Regards, Hans + return 0; } diff --git
Re: [PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set
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. > > Fixes: 051a6d8d3ca0 ("drm/i915: Move LUT programming to happen after vblank > waits") > Signed-off-by: Hans de Goede > --- > 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. 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. > + > return 0; > } > > diff --git a/drivers/gpu/drm/i915/display/intel_display.c > b/drivers/gpu/drm/i915/display/intel_display.c > index aa54bb22796d..21442b0dd134 100644 > --- a/drivers/gpu/drm/i915/display/intel_display.c > +++ b/drivers/gpu/drm/i915/display/intel_display.c > @@ -14033,6 +14033,7 @@ static void intel_atomic_commit_tail(struct > intel_atomic_state *state) > for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { > if (new_crtc_state->base.active && > !needs_modeset(new_crtc_state) && > +
Re: [PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set
Hi, On 20-10-2019 20:19, 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. Fixes: 051a6d8d3ca0 ("drm/i915: Move LUT programming to happen after vblank waits") Signed-off-by: Hans de Goede So this failed to apply in CI, I based this on 5.4 and I clearly need to rebase it on top of dinq. Before I do this I would appreciate a review though, as I'm not entirely sure that this is the right approach to fixing the issue. Regards, Hans --- 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; + return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index aa54bb22796d..21442b0dd134 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -14033,6 +14033,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { if (new_crtc_state->base.active && !needs_modeset(new_crtc_state) && + !new_crtc_state->load_lut_before_commit && (new_crtc_state->base.color_mgmt_changed || new_crtc_state->update_pipe)) intel_color_load_luts(new_crtc_state); @@ -14529,6 +14530,12 @@ static void intel_begin_crtc_commit(struct intel_atomic_state *state, intel_atomic_get_new_crtc_state(state, crtc); bool modeset = needs_modeset(new_crtc_state); + if (!modeset && + new_crtc_state->load_lut_before_commit && + (new_crtc_state->base.color_mgmt_changed || +new_crtc_state->update_pipe)) + intel_color_load_luts(new_crtc_state); + /* Perform vblank evasion around commit operation */ intel_pipe_update_start(new_crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h
[PATCH] drm/i915: Program LUT before intel_color_commit() if LUT was not previously set
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. Fixes: 051a6d8d3ca0 ("drm/i915: Move LUT programming to happen after vblank waits") Signed-off-by: Hans de Goede --- 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; + return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index aa54bb22796d..21442b0dd134 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -14033,6 +14033,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { if (new_crtc_state->base.active && !needs_modeset(new_crtc_state) && + !new_crtc_state->load_lut_before_commit && (new_crtc_state->base.color_mgmt_changed || new_crtc_state->update_pipe)) intel_color_load_luts(new_crtc_state); @@ -14529,6 +14530,12 @@ static void intel_begin_crtc_commit(struct intel_atomic_state *state, intel_atomic_get_new_crtc_state(state, crtc); bool modeset = needs_modeset(new_crtc_state); + if (!modeset && + new_crtc_state->load_lut_before_commit && + (new_crtc_state->base.color_mgmt_changed || +new_crtc_state->update_pipe)) + intel_color_load_luts(new_crtc_state); + /* Perform vblank evasion around commit operation */ intel_pipe_update_start(new_crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 449abaea619f..bbdeb3be64e6 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -973,6 +973,9 @@ struct intel_crtc_state { /* enable pipe csc? */ bool csc_enable;