diff mbox series

drm/i915: customize DPCD brightness control for specific panel

Message ID 20191004215851.31446-1-shawn.c.lee@intel.com (mailing list archive)
State New, archived
Headers show
Series drm/i915: customize DPCD brightness control for specific panel | expand

Commit Message

Lee Shawn C Oct. 4, 2019, 9:58 p.m. UTC
This panel (manufacturer is SDC, product ID is 0x4141)
used manufacturer defined DPCD register to control brightness
that not defined in eDP spec so far. This change follow panel
vendor's instruction to support brightness adjustment.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=97883

Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Cooper Chiou <cooper.chiou@intel.com>
Signed-off-by: Lee Shawn C <shawn.c.lee@intel.com>
---
 drivers/gpu/drm/drm_edid.c                    |   6 +-
 drivers/gpu/drm/i915/display/intel_dp.c       |   7 +
 .../drm/i915/display/intel_dp_aux_backlight.c | 143 +++++++++++++++++-
 include/drm/drm_edid.h                        |   3 +
 4 files changed, 153 insertions(+), 6 deletions(-)

Comments

Adam Jackson Oct. 4, 2019, 3:19 p.m. UTC | #1
On Sat, 2019-10-05 at 05:58 +0800, Lee Shawn C wrote:
> This panel (manufacturer is SDC, product ID is 0x4141)
> used manufacturer defined DPCD register to control brightness
> that not defined in eDP spec so far. This change follow panel
> vendor's instruction to support brightness adjustment.

I'm sure this works, but this smells a little funny to me.

> +	/* Samsung eDP panel */
> +	{ "SDC", 0x4141, EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL },

It feels a bit like a layering violation to identify eDP behavior
changes based on EDID. But I'm not sure there's any obvious way to
identify this device by its DPCD. The Sink OUI (from the linked
bugzilla) seems to be 0011F8, which doesn't match up to anything in my
oui.txt...

> @@ -1953,6 +1956,7 @@ static u32 edid_get_quirks(const struct edid *edid)
>  
>  	return 0;
>  }
> +EXPORT_SYMBOL(edid_get_quirks);

If we're going to export this it should probably get a drm_ prefix.

> +#define DPCD_EDP_GETSET_CTRL_PARAMS		0x344
> +#define DPCD_EDP_CONTENT_LUMINANCE		0x346
> +#define DPCD_EDP_PANEL_LUMINANCE_OVERRIDE	0x34a
> +#define DPCD_EDP_BRIGHTNESS_NITS		0x354
> +#define DPCD_EDP_BRIGHTNESS_OPTIMIZATION	0x358
> +
> +#define EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL	(512)

This also seems a bit weird, the 0x300-0x3FF registers belong to the
_source_ DP device. But then later...

> +	/* write source OUI */
> +	write_val[0] = 0x00;
> +	write_val[1] = 0xaa;
> +	write_val[2] = 0x01;

Oh hey, you're writing (an) Intel OUI to the Source OUI, so now it
makes sense that you're writing to registers whose behavior the source
defines. But this does raise the question: is this just a convention
between Intel and this particular panel? Would we expect this to work
with other similar panels? Is there any way to know to expect this
convention from DPCD instead?

- ajax
Jani Nikula Oct. 4, 2019, 6:40 p.m. UTC | #2
On Fri, 04 Oct 2019, Adam Jackson <ajax@redhat.com> wrote:
> On Sat, 2019-10-05 at 05:58 +0800, Lee Shawn C wrote:
>> This panel (manufacturer is SDC, product ID is 0x4141)
>> used manufacturer defined DPCD register to control brightness
>> that not defined in eDP spec so far. This change follow panel
>> vendor's instruction to support brightness adjustment.
>
> I'm sure this works, but this smells a little funny to me.

That was kindly put. ;)

>> +	/* Samsung eDP panel */
>> +	{ "SDC", 0x4141, EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL },
>
> It feels a bit like a layering violation to identify eDP behavior
> changes based on EDID. But I'm not sure there's any obvious way to
> identify this device by its DPCD. The Sink OUI (from the linked
> bugzilla) seems to be 0011F8, which doesn't match up to anything in my
> oui.txt...

We have the DPCD quirk stuff in drm_dp_helper.c, but IIUC in this case
there's only the OUI, and the device id etc. are all zeros. Otherwise I
think that would be the natural thing to do, and all this could be
better hidden away in i915.

>
>> @@ -1953,6 +1956,7 @@ static u32 edid_get_quirks(const struct edid *edid)
>>  
>>  	return 0;
>>  }
>> +EXPORT_SYMBOL(edid_get_quirks);
>
> If we're going to export this it should probably get a drm_ prefix.
>
>> +#define DPCD_EDP_GETSET_CTRL_PARAMS		0x344
>> +#define DPCD_EDP_CONTENT_LUMINANCE		0x346
>> +#define DPCD_EDP_PANEL_LUMINANCE_OVERRIDE	0x34a
>> +#define DPCD_EDP_BRIGHTNESS_NITS		0x354
>> +#define DPCD_EDP_BRIGHTNESS_OPTIMIZATION	0x358
>> +
>> +#define EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL	(512)
>
> This also seems a bit weird, the 0x300-0x3FF registers belong to the
> _source_ DP device. But then later...
>
>> +	/* write source OUI */
>> +	write_val[0] = 0x00;
>> +	write_val[1] = 0xaa;
>> +	write_val[2] = 0x01;
>
> Oh hey, you're writing (an) Intel OUI to the Source OUI, so now it
> makes sense that you're writing to registers whose behavior the source
> defines. But this does raise the question: is this just a convention
> between Intel and this particular panel? Would we expect this to work
> with other similar panels? Is there any way to know to expect this
> convention from DPCD instead?

For one thing, it's not standard. I honestly don't know, but I'd assume
you wouldn't find behaviour with Intel OUI in non-Intel designs... and a
quirk of some sort seems like the only way to make this work.

I suppose we could start off with a DPCD quirk that only looks at the
sink OUI, and then, if needed, limit by DMI matching or by checking for
some DPCD registers (what, I am not sure, perhaps write the source OUI
and see how it behaves).

That would avoid the mildly annoying change in the EDID quirk interface
and how it's being used.

Thoughts?


BR,
Jani.
Lee Shawn C Oct. 7, 2019, 4:02 a.m. UTC | #3
On Fri, 04 Oct 2019, Jani Nikula <jani.nikula@intel.com> wrote:
>On Fri, 04 Oct 2019, Adam Jackson <ajax@redhat.com> wrote:
>> On Sat, 2019-10-05 at 05:58 +0800, Lee Shawn C wrote:
>>> This panel (manufacturer is SDC, product ID is 0x4141) used 
>>> manufacturer defined DPCD register to control brightness that not 
>>> defined in eDP spec so far. This change follow panel vendor's 
>>> instruction to support brightness adjustment.
>>
>> I'm sure this works, but this smells a little funny to me.
>
>That was kindly put. ;)
>
>>> +	/* Samsung eDP panel */
>>> +	{ "SDC", 0x4141, EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL },
>>
>> It feels a bit like a layering violation to identify eDP behavior 
>> changes based on EDID. But I'm not sure there's any obvious way to 
>> identify this device by its DPCD. The Sink OUI (from the linked
>> bugzilla) seems to be 0011F8, which doesn't match up to anything in my 
>> oui.txt...
>
>We have the DPCD quirk stuff in drm_dp_helper.c, but IIUC in this case 
>there's only the OUI, and the device id etc. are all zeros. Otherwise I
>think that would be the natural thing to do, and all this could be
>better hidden away in i915.
>

Below is what we dumped from this panel. Only sink OUI (ba-41-59) in it
and nothing else. 
00000400  ba 41 59 00 00 00 00 00  00 00 00 00 00 00 00 00  |.AY.............|
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................

That's why the patch to identify EDID's manufacturer and product ID
to make sure this method applied on specific panel.

>>
>>> @@ -1953,6 +1956,7 @@ static u32 edid_get_quirks(const struct edid 
>>> *edid)
>>>  
>>>  	return 0;
>>>  }
>>> +EXPORT_SYMBOL(edid_get_quirks);
>>
>> If we're going to export this it should probably get a drm_ prefix.

Yes! It will be better to have drm_ prefix for export funciton.

>>
>>> +#define DPCD_EDP_GETSET_CTRL_PARAMS		0x344
>>> +#define DPCD_EDP_CONTENT_LUMINANCE		0x346
>>> +#define DPCD_EDP_PANEL_LUMINANCE_OVERRIDE	0x34a
>>> +#define DPCD_EDP_BRIGHTNESS_NITS		0x354
>>> +#define DPCD_EDP_BRIGHTNESS_OPTIMIZATION	0x358
>>> +
>>> +#define EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL	(512)
>>
>> This also seems a bit weird, the 0x300-0x3FF registers belong to the 
>> _source_ DP device. But then later...
>>
>>> +	/* write source OUI */
>>> +	write_val[0] = 0x00;
>>> +	write_val[1] = 0xaa;
>>> +	write_val[2] = 0x01;
>>
>> Oh hey, you're writing (an) Intel OUI to the Source OUI, so now it 
>> makes sense that you're writing to registers whose behavior the source 
>> defines. But this does raise the question: is this just a convention 
>> between Intel and this particular panel? Would we expect this to work 
>> with other similar panels? Is there any way to know to expect this 
>> convention from DPCD instead?

TCON would reply on source OUI to configure its capability. And these
DPCD registers were defined by vendor and Intel. This change should works
with similar panels (with same TCON). Seems there is another issue so
vendor decide to use non standard way to setup brightness.

>For one thing, it's not standard. I honestly don't know, but I'd assume
>you wouldn't find behaviour with Intel OUI in non-Intel designs... and a
>quirk of some sort seems like the only way to make this work.
>
>I suppose we could start off with a DPCD quirk that only looks at the
>sink OUI, and then, if needed, limit by DMI matching or by checking for
>some DPCD registers (what, I am not sure, perhaps write the source OUI
>and see how it behaves).
>
>That would avoid the mildly annoying change in the EDID quirk interface
>and how it's being used.
>
>Thoughts?
>
>
>BR,
>Jani.
>

To be honest. Panel vendor did not provide enough sink info in DPCD.
That's why hard to recognize it and we have to confirm EDID instead of DPCD.

Do you mean just confirm sink OUI only from DPCD quirk? I'm afraid it
may impact the other panels with the same TCON. Any suggestion?

>
>--
>Jani Nikula, Intel Open Source Graphics Center
>
Jani Nikula Oct. 7, 2019, 9:08 a.m. UTC | #4
On Mon, 07 Oct 2019, "Lee, Shawn C" <shawn.c.lee@intel.com> wrote:
> On Fri, 04 Oct 2019, Jani Nikula <jani.nikula@intel.com> wrote:
>>On Fri, 04 Oct 2019, Adam Jackson <ajax@redhat.com> wrote:
>>> On Sat, 2019-10-05 at 05:58 +0800, Lee Shawn C wrote:
>>>> This panel (manufacturer is SDC, product ID is 0x4141) used 
>>>> manufacturer defined DPCD register to control brightness that not 
>>>> defined in eDP spec so far. This change follow panel vendor's 
>>>> instruction to support brightness adjustment.
>>>
>>> I'm sure this works, but this smells a little funny to me.
>>
>>That was kindly put. ;)
>>
>>>> +	/* Samsung eDP panel */
>>>> +	{ "SDC", 0x4141, EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL },
>>>
>>> It feels a bit like a layering violation to identify eDP behavior 
>>> changes based on EDID. But I'm not sure there's any obvious way to 
>>> identify this device by its DPCD. The Sink OUI (from the linked
>>> bugzilla) seems to be 0011F8, which doesn't match up to anything in my 
>>> oui.txt...
>>
>>We have the DPCD quirk stuff in drm_dp_helper.c, but IIUC in this case 
>>there's only the OUI, and the device id etc. are all zeros. Otherwise I
>>think that would be the natural thing to do, and all this could be
>>better hidden away in i915.
>>
>
> Below is what we dumped from this panel. Only sink OUI (ba-41-59) in it
> and nothing else. 
> 00000400  ba 41 59 00 00 00 00 00  00 00 00 00 00 00 00 00  |.AY.............|
> 00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................
>
> That's why the patch to identify EDID's manufacturer and product ID
> to make sure this method applied on specific panel.
>
>>>
>>>> @@ -1953,6 +1956,7 @@ static u32 edid_get_quirks(const struct edid 
>>>> *edid)
>>>>  
>>>>  	return 0;
>>>>  }
>>>> +EXPORT_SYMBOL(edid_get_quirks);
>>>
>>> If we're going to export this it should probably get a drm_ prefix.
>
> Yes! It will be better to have drm_ prefix for export funciton.
>
>>>
>>>> +#define DPCD_EDP_GETSET_CTRL_PARAMS		0x344
>>>> +#define DPCD_EDP_CONTENT_LUMINANCE		0x346
>>>> +#define DPCD_EDP_PANEL_LUMINANCE_OVERRIDE	0x34a
>>>> +#define DPCD_EDP_BRIGHTNESS_NITS		0x354
>>>> +#define DPCD_EDP_BRIGHTNESS_OPTIMIZATION	0x358
>>>> +
>>>> +#define EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL	(512)
>>>
>>> This also seems a bit weird, the 0x300-0x3FF registers belong to the 
>>> _source_ DP device. But then later...
>>>
>>>> +	/* write source OUI */
>>>> +	write_val[0] = 0x00;
>>>> +	write_val[1] = 0xaa;
>>>> +	write_val[2] = 0x01;
>>>
>>> Oh hey, you're writing (an) Intel OUI to the Source OUI, so now it 
>>> makes sense that you're writing to registers whose behavior the source 
>>> defines. But this does raise the question: is this just a convention 
>>> between Intel and this particular panel? Would we expect this to work 
>>> with other similar panels? Is there any way to know to expect this 
>>> convention from DPCD instead?
>
> TCON would reply on source OUI to configure its capability. And these
> DPCD registers were defined by vendor and Intel. This change should works
> with similar panels (with same TCON). Seems there is another issue so
> vendor decide to use non standard way to setup brightness.
>
>>For one thing, it's not standard. I honestly don't know, but I'd assume
>>you wouldn't find behaviour with Intel OUI in non-Intel designs... and a
>>quirk of some sort seems like the only way to make this work.
>>
>>I suppose we could start off with a DPCD quirk that only looks at the
>>sink OUI, and then, if needed, limit by DMI matching or by checking for
>>some DPCD registers (what, I am not sure, perhaps write the source OUI
>>and see how it behaves).
>>
>>That would avoid the mildly annoying change in the EDID quirk interface
>>and how it's being used.
>>
>>Thoughts?
>>
>>
>>BR,
>>Jani.
>>
>
> To be honest. Panel vendor did not provide enough sink info in DPCD.
> That's why hard to recognize it and we have to confirm EDID instead of DPCD.
>
> Do you mean just confirm sink OUI only from DPCD quirk? I'm afraid it
> may impact the other panels with the same TCON. Any suggestion?

The problem with the EDID quirks is that exposing the quirks sticks out
like a sore thumb. Thus far all of it has been contained in drm_edid.c
and they affect how the EDID gets parsed, for all drivers. Obviously
this could be changed, but is it the right thing to do?

What I suggested was, check the OUI only, and if it matches, do
more. Perhaps there's something in the 0x300 range of DPCD offsets that
you can read? Or perhaps you need to write the source OUI first, and
then do that.

BR,
Jani.


>
>>
>>--
>>Jani Nikula, Intel Open Source Graphics Center
>>
Adam Jackson Oct. 7, 2019, 5:38 p.m. UTC | #5
On Mon, 2019-10-07 at 12:08 +0300, Jani Nikula wrote:

> The problem with the EDID quirks is that exposing the quirks sticks out
> like a sore thumb. Thus far all of it has been contained in drm_edid.c
> and they affect how the EDID gets parsed, for all drivers. Obviously
> this could be changed, but is it the right thing to do?
> 
> What I suggested was, check the OUI only, and if it matches, do
> more. Perhaps there's something in the 0x300 range of DPCD offsets that
> you can read? Or perhaps you need to write the source OUI first, and
> then do that.

My issue isn't really with identifying the panel from EDID rather than
DPCD, whichever identifier is most specific is probably the best thing
to use. It's more that this quirk is identified in common code but only
applied in one driver. If this panel were ever to be attached to some
other source, they might well want to apply the same kind of fix. My
(admittedly naïve) reading of the OUI handshake process is that when
the source device writes an OUI to DP_SOURCE_OUI it is telling the sink
"I'm about to issue commands that conform to _this_ vendor's own
conventions". If that convention communicates information that is
entirely contained within AUXCH transactions (and doesn't, for example,
require looking at some other strapping pin or external device) then in
principle it doesn't matter if the source device "matches" that OUI; it
would be legal for an AMD GPU to write the same sequence and expect the
same reaction, should that panel be attached to an AMD GPU.

So, it would be nice to know exactly what that protocol is meant to do,
if it applies only to this specific panel or anything else with the
same TCON, how one would identify such TCONs in the wild other than
EDID, if it relies on an external PWM or something, etc. And it might
make sense for now to make this a (shudder) driver-specific EDID quirk
rather than match by DPCD, at least until we know if the panel is ever
seen attached to other source devices and if the OUI convention is
self-contained.

- ajax
Jani Nikula Oct. 8, 2019, 9:19 a.m. UTC | #6
On Mon, 07 Oct 2019, Adam Jackson <ajax@redhat.com> wrote:
> On Mon, 2019-10-07 at 12:08 +0300, Jani Nikula wrote:
>
>> The problem with the EDID quirks is that exposing the quirks sticks out
>> like a sore thumb. Thus far all of it has been contained in drm_edid.c
>> and they affect how the EDID gets parsed, for all drivers. Obviously
>> this could be changed, but is it the right thing to do?
>> 
>> What I suggested was, check the OUI only, and if it matches, do
>> more. Perhaps there's something in the 0x300 range of DPCD offsets that
>> you can read? Or perhaps you need to write the source OUI first, and
>> then do that.
>
> My issue isn't really with identifying the panel from EDID rather than
> DPCD, whichever identifier is most specific is probably the best thing
> to use. It's more that this quirk is identified in common code but only
> applied in one driver. If this panel were ever to be attached to some
> other source, they might well want to apply the same kind of fix. My
> (admittedly naïve) reading of the OUI handshake process is that when
> the source device writes an OUI to DP_SOURCE_OUI it is telling the sink
> "I'm about to issue commands that conform to _this_ vendor's own
> conventions". If that convention communicates information that is
> entirely contained within AUXCH transactions (and doesn't, for example,
> require looking at some other strapping pin or external device) then in
> principle it doesn't matter if the source device "matches" that OUI; it
> would be legal for an AMD GPU to write the same sequence and expect the
> same reaction, should that panel be attached to an AMD GPU.
>
> So, it would be nice to know exactly what that protocol is meant to do,
> if it applies only to this specific panel or anything else with the
> same TCON, how one would identify such TCONs in the wild other than
> EDID, if it relies on an external PWM or something, etc. And it might
> make sense for now to make this a (shudder) driver-specific EDID quirk
> rather than match by DPCD, at least until we know if the panel is ever
> seen attached to other source devices and if the OUI convention is
> self-contained.

Thanks for clarifying. Pretty much agreed, unfortunately also on the
"would be nice to know more" part...

If this were to be an EDID quirk after all, I wonder if it would be
better to store the parsed quirks to, say, struct drm_display_info, and
have a drm_connector_has_quirk() function similar to drm_dp_has_quirk().

This would also allow us to not return quirks from
drm_add_display_info(), which would arguably clean up the interface.

BR,
Jani.
Lyude Paul Nov. 27, 2019, 12:02 a.m. UTC | #7
I'm about to post some more review comments for the v2 version of this, but
some comments down below...

On Tue, 2019-10-08 at 12:19 +0300, Jani Nikula wrote:
> On Mon, 07 Oct 2019, Adam Jackson <ajax@redhat.com> wrote:
> > On Mon, 2019-10-07 at 12:08 +0300, Jani Nikula wrote:
> > 
> > > The problem with the EDID quirks is that exposing the quirks sticks out
> > > like a sore thumb. Thus far all of it has been contained in drm_edid.c
> > > and they affect how the EDID gets parsed, for all drivers. Obviously
> > > this could be changed, but is it the right thing to do?
> > > 
> > > What I suggested was, check the OUI only, and if it matches, do
> > > more. Perhaps there's something in the 0x300 range of DPCD offsets that
> > > you can read? Or perhaps you need to write the source OUI first, and
> > > then do that.
> > 
> > My issue isn't really with identifying the panel from EDID rather than
> > DPCD, whichever identifier is most specific is probably the best thing
> > to use. It's more that this quirk is identified in common code but only
> > applied in one driver. If this panel were ever to be attached to some
> > other source, they might well want to apply the same kind of fix. My
> > (admittedly naïve) reading of the OUI handshake process is that when
> > the source device writes an OUI to DP_SOURCE_OUI it is telling the sink
> > "I'm about to issue commands that conform to _this_ vendor's own
> > conventions". If that convention communicates information that is
> > entirely contained within AUXCH transactions (and doesn't, for example,
> > require looking at some other strapping pin or external device) then in
> > principle it doesn't matter if the source device "matches" that OUI; it
> > would be legal for an AMD GPU to write the same sequence and expect the
> > same reaction, should that panel be attached to an AMD GPU.
> > 
> > So, it would be nice to know exactly what that protocol is meant to do,
> > if it applies only to this specific panel or anything else with the
> > same TCON, how one would identify such TCONs in the wild other than
> > EDID, if it relies on an external PWM or something, etc. And it might
> > make sense for now to make this a (shudder) driver-specific EDID quirk
> > rather than match by DPCD, at least until we know if the panel is ever
> > seen attached to other source devices and if the OUI convention is
> > self-contained.
> 
> Thanks for clarifying. Pretty much agreed, unfortunately also on the
> "would be nice to know more" part...
> 
> If this were to be an EDID quirk after all, I wonder if it would be
> better to store the parsed quirks to, say, struct drm_display_info, and
> have a drm_connector_has_quirk() function similar to drm_dp_has_quirk().
> 
> This would also allow us to not return quirks from
> drm_add_display_info(), which would arguably clean up the interface.

Did anyone check if this is specified in the vbios? There appears to be a
field defined for this right...

enum intel_backlight_type {
	INTEL_BACKLIGHT_PMIC,
	INTEL_BACKLIGHT_LPSS,
	INTEL_BACKLIGHT_DISPLAY_DDI,
	INTEL_BACKLIGHT_DSI_DCS,
	INTEL_BACKLIGHT_PANEL_DRIVER_INTERFACE, /* <- ... over here */
	INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE,
};

> 
> BR,
> Jani.
> 
>
Jani Nikula Nov. 27, 2019, 10:38 a.m. UTC | #8
On Tue, 26 Nov 2019, Lyude Paul <lyude@redhat.com> wrote:
> I'm about to post some more review comments for the v2 version of this, but
> some comments down below...
>
> On Tue, 2019-10-08 at 12:19 +0300, Jani Nikula wrote:
>> On Mon, 07 Oct 2019, Adam Jackson <ajax@redhat.com> wrote:
>> > On Mon, 2019-10-07 at 12:08 +0300, Jani Nikula wrote:
>> > 
>> > > The problem with the EDID quirks is that exposing the quirks sticks out
>> > > like a sore thumb. Thus far all of it has been contained in drm_edid.c
>> > > and they affect how the EDID gets parsed, for all drivers. Obviously
>> > > this could be changed, but is it the right thing to do?
>> > > 
>> > > What I suggested was, check the OUI only, and if it matches, do
>> > > more. Perhaps there's something in the 0x300 range of DPCD offsets that
>> > > you can read? Or perhaps you need to write the source OUI first, and
>> > > then do that.
>> > 
>> > My issue isn't really with identifying the panel from EDID rather than
>> > DPCD, whichever identifier is most specific is probably the best thing
>> > to use. It's more that this quirk is identified in common code but only
>> > applied in one driver. If this panel were ever to be attached to some
>> > other source, they might well want to apply the same kind of fix. My
>> > (admittedly naïve) reading of the OUI handshake process is that when
>> > the source device writes an OUI to DP_SOURCE_OUI it is telling the sink
>> > "I'm about to issue commands that conform to _this_ vendor's own
>> > conventions". If that convention communicates information that is
>> > entirely contained within AUXCH transactions (and doesn't, for example,
>> > require looking at some other strapping pin or external device) then in
>> > principle it doesn't matter if the source device "matches" that OUI; it
>> > would be legal for an AMD GPU to write the same sequence and expect the
>> > same reaction, should that panel be attached to an AMD GPU.
>> > 
>> > So, it would be nice to know exactly what that protocol is meant to do,
>> > if it applies only to this specific panel or anything else with the
>> > same TCON, how one would identify such TCONs in the wild other than
>> > EDID, if it relies on an external PWM or something, etc. And it might
>> > make sense for now to make this a (shudder) driver-specific EDID quirk
>> > rather than match by DPCD, at least until we know if the panel is ever
>> > seen attached to other source devices and if the OUI convention is
>> > self-contained.
>> 
>> Thanks for clarifying. Pretty much agreed, unfortunately also on the
>> "would be nice to know more" part...
>> 
>> If this were to be an EDID quirk after all, I wonder if it would be
>> better to store the parsed quirks to, say, struct drm_display_info, and
>> have a drm_connector_has_quirk() function similar to drm_dp_has_quirk().
>> 
>> This would also allow us to not return quirks from
>> drm_add_display_info(), which would arguably clean up the interface.
>
> Did anyone check if this is specified in the vbios? There appears to be a
> field defined for this right...
>
> enum intel_backlight_type {
> 	INTEL_BACKLIGHT_PMIC,
> 	INTEL_BACKLIGHT_LPSS,
> 	INTEL_BACKLIGHT_DISPLAY_DDI,
> 	INTEL_BACKLIGHT_DSI_DCS,
> 	INTEL_BACKLIGHT_PANEL_DRIVER_INTERFACE, /* <- ... over here */
> 	INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE,
> };

Would just need /sys/kernel/debug/dri/0/i915_vbt on the affected machine
to check.

BR,
Jani.

>
>> 
>> BR,
>> Jani.
>> 
>>
diff mbox series

Patch

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 3c9703b08491..5aee0ebc200e 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -211,6 +211,9 @@  static const struct edid_quirk {
 
 	/* OSVR HDK and HDK2 VR Headsets */
 	{ "SVR", 0x1019, EDID_QUIRK_NON_DESKTOP },
+
+	/* Samsung eDP panel */
+	{ "SDC", 0x4141, EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL },
 };
 
 /*
@@ -1938,7 +1941,7 @@  static bool edid_vendor(const struct edid *edid, const char *vendor)
  *
  * This tells subsequent routines what fixes they need to apply.
  */
-static u32 edid_get_quirks(const struct edid *edid)
+u32 edid_get_quirks(const struct edid *edid)
 {
 	const struct edid_quirk *quirk;
 	int i;
@@ -1953,6 +1956,7 @@  static u32 edid_get_quirks(const struct edid *edid)
 
 	return 0;
 }
+EXPORT_SYMBOL(edid_get_quirks);
 
 #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
 #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 2b1e71f992b0..89193bd2d8ea 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -7097,6 +7097,13 @@  static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 	}
 	intel_connector->edid = edid;
 
+	if (edid_get_quirks(intel_connector->edid) ==
+	    EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL) {
+		i915_modparams.enable_dpcd_backlight = true;
+		i915_modparams.fastboot = false;
+		DRM_DEBUG_KMS("Using specific DPCD to control brightness\n");
+	}
+
 	fixed_mode = intel_panel_edid_fixed_mode(intel_connector);
 	if (fixed_mode)
 		downclock_mode = intel_dp_drrs_init(intel_connector, fixed_mode);
diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
index 020422da2ae2..7d9a2249cfb1 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -25,6 +25,117 @@ 
 #include "intel_display_types.h"
 #include "intel_dp_aux_backlight.h"
 
+#define DPCD_EDP_GETSET_CTRL_PARAMS		0x344
+#define DPCD_EDP_CONTENT_LUMINANCE		0x346
+#define DPCD_EDP_PANEL_LUMINANCE_OVERRIDE	0x34a
+#define DPCD_EDP_BRIGHTNESS_NITS		0x354
+#define DPCD_EDP_BRIGHTNESS_OPTIMIZATION	0x358
+
+#define EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL	(512)
+
+static uint32_t intel_dp_aux_get_customize_backlight(struct intel_connector *connector)
+{
+	struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
+	uint8_t read_val[2] = { 0x0 };
+
+	if (drm_dp_dpcd_read(&intel_dp->aux, DPCD_EDP_BRIGHTNESS_NITS,
+			     &read_val, sizeof(read_val)) < 0) {
+		DRM_DEBUG_KMS("Failed to read DPCD register %x\n",
+			DPCD_EDP_BRIGHTNESS_NITS);
+		return 0;
+	}
+
+	return (read_val[1] << 8 | read_val[0]);
+}
+
+static void
+intel_dp_aux_set_customize_backlight(const struct drm_connector_state *conn_state, u32 level)
+{
+	struct intel_connector *connector = to_intel_connector(conn_state->connector);
+	struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
+	struct intel_panel *panel = &connector->panel;
+	uint8_t new_vals[4];
+
+	if (level > panel->backlight.max)
+		level = panel->backlight.max;
+
+	new_vals[0] = level & 0xFF;
+	new_vals[1] = (level & 0xFF00) >> 8;
+	new_vals[2] = 0;
+	new_vals[3] = 0;
+
+	if (drm_dp_dpcd_write(&intel_dp->aux, DPCD_EDP_BRIGHTNESS_NITS, new_vals, 4) < 0)
+		DRM_DEBUG_KMS("Failed to write aux backlight level\n");
+}
+
+static void intel_dp_aux_enable_customize_backlight(const struct intel_crtc_state *crtc_state,
+					  const struct drm_connector_state *conn_state)
+{
+	struct intel_connector *connector = to_intel_connector(conn_state->connector);
+	struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
+	uint8_t read_val[4], i;
+	uint8_t write_val[8] = {0x00, 0x00, 0xF0, 0x01, 0x90, 0x01, 0x00, 0x00};
+
+	if (drm_dp_dpcd_write(&intel_dp->aux, DPCD_EDP_PANEL_LUMINANCE_OVERRIDE, write_val, sizeof(write_val)) < 0)
+		DRM_DEBUG_KMS("Failed to write panel luminance.\n");
+
+	if (drm_dp_dpcd_writeb(&intel_dp->aux, DPCD_EDP_BRIGHTNESS_OPTIMIZATION, 0x01) < 0)
+		DRM_DEBUG_KMS("Failed to write %x\n",
+			DPCD_EDP_BRIGHTNESS_OPTIMIZATION);
+	/* write source OUI */
+	write_val[0] = 0x00;
+	write_val[1] = 0xaa;
+	write_val[2] = 0x01;
+	if (drm_dp_dpcd_write(&intel_dp->aux, DP_SOURCE_OUI, write_val, 3) < 0)
+		DRM_DEBUG_KMS("Failed to write OUI\n");
+
+	if (drm_dp_dpcd_readb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_writeb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, 0) != 1)
+		DRM_DEBUG_KMS("Failed to write %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_readb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_read(&intel_dp->aux, DPCD_EDP_CONTENT_LUMINANCE, &read_val, sizeof(read_val)) < 0)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_CONTENT_LUMINANCE);
+
+	memset(read_val, 0x0, 4);
+	if (drm_dp_dpcd_write(&intel_dp->aux, DPCD_EDP_CONTENT_LUMINANCE, read_val, sizeof(read_val)) < 0)
+		DRM_DEBUG_KMS("Failed to write %x\n",
+			DPCD_EDP_CONTENT_LUMINANCE);
+
+	if (drm_dp_dpcd_readb(&intel_dp->aux, DPCD_EDP_GETSET_CTRL_PARAMS, read_val) != 1)
+		DRM_DEBUG_KMS("Failed to read %x\n",
+			DPCD_EDP_GETSET_CTRL_PARAMS);
+
+	if (drm_dp_dpcd_read(&intel_dp->aux, DP_SOURCE_OUI, read_val, 4) < 0)
+		DRM_DEBUG_KMS("Failed to read OUI\n");
+
+	DRM_DEBUG_KMS("got OUI %3ph\n", read_val);
+
+	for (i = 0; i < 6; i++) {
+		intel_dp_aux_set_customize_backlight(conn_state, connector->panel.backlight.level);
+
+		msleep(60);
+		if (intel_dp_aux_get_customize_backlight(connector))
+			return;
+	}
+
+	DRM_DEBUG_KMS("Restore brightness may have problem.\n");
+}
+
+static void intel_dp_aux_disable_customize_backlight(const struct drm_connector_state *old_conn_state)
+{
+	// do nothing
+	return;
+}
+
 static void set_aux_backlight_enable(struct intel_dp *intel_dp, bool enable)
 {
 	u8 reg_val = 0;
@@ -225,6 +336,19 @@  static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old
 	set_aux_backlight_enable(enc_to_intel_dp(old_conn_state->best_encoder), false);
 }
 
+static int intel_dp_aux_setup_customize_backlight(struct intel_connector *connector,
+					enum pipe pipe)
+{
+	struct intel_panel *panel = &connector->panel;
+
+	panel->backlight.max = EDP_CUSTOMIZE_MAX_BRIGHTNESS_LEVEL;
+	panel->backlight.min = 0;
+	panel->backlight.level = panel->backlight.get(connector);
+	panel->backlight.enabled = panel->backlight.level != 0;
+
+	return 0;
+}
+
 static int intel_dp_aux_setup_backlight(struct intel_connector *connector,
 					enum pipe pipe)
 {
@@ -274,11 +398,20 @@  int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
 	if (!intel_dp_aux_display_control_capable(intel_connector))
 		return -ENODEV;
 
-	panel->backlight.setup = intel_dp_aux_setup_backlight;
-	panel->backlight.enable = intel_dp_aux_enable_backlight;
-	panel->backlight.disable = intel_dp_aux_disable_backlight;
-	panel->backlight.set = intel_dp_aux_set_backlight;
-	panel->backlight.get = intel_dp_aux_get_backlight;
+	if (edid_get_quirks(intel_connector->edid) ==
+	    EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL) {
+		panel->backlight.setup   = intel_dp_aux_setup_customize_backlight;
+		panel->backlight.enable  = intel_dp_aux_enable_customize_backlight;
+		panel->backlight.disable = intel_dp_aux_disable_customize_backlight;
+		panel->backlight.set = intel_dp_aux_set_customize_backlight;
+		panel->backlight.get = intel_dp_aux_get_customize_backlight;
+	} else {
+		panel->backlight.setup   = intel_dp_aux_setup_backlight;
+		panel->backlight.enable  = intel_dp_aux_enable_backlight;
+		panel->backlight.disable = intel_dp_aux_disable_backlight;
+		panel->backlight.set = intel_dp_aux_set_backlight;
+		panel->backlight.get = intel_dp_aux_get_backlight;
+	}
 
 	return 0;
 }
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index b9719418c3d2..b73bc3ee071e 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -282,6 +282,8 @@  struct detailed_timing {
 
 #define DRM_ELD_CEA_SAD(mnl, sad)	(20 + (mnl) + 3 * (sad))
 
+#define EDID_QUIRK_NON_STD_BRIGHTNESS_CONTROL	(1 << 13)
+
 struct edid {
 	u8 header[8];
 	/* Vendor & product info */
@@ -500,4 +502,5 @@  void drm_edid_get_monitor_name(struct edid *edid, char *name,
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
 					   int hsize, int vsize, int fresh,
 					   bool rb);
+u32 edid_get_quirks(const struct edid *edid);
 #endif /* __DRM_EDID_H__ */