On 8/25/2025 4:17 PM, Antheas Kapenekakis wrote:
On Mon, 25 Aug 2025 at 20:02, Mario Limonciello
<mario.limoncie...@amd.com> wrote:
On 8/24/2025 3:02 PM, Antheas Kapenekakis wrote:
Certain OLED devices malfunction on specific brightness levels.
Specifically, when DP_SOURCE_BACKLIGHT_LEVEL is written to with
the first byte being 0x00 and sometimes 0x01, the panel forcibly
turns off until the device sleeps again.
Below are some examples. This was found by iterating over brighness
brightness
ranges while printing DP_SOURCE_BACKLIGHT_LEVEL. It was found that
the screen would malfunction on specific values, and some of them
were collected. Summary examples are found below.
This quirk was tested by removing the workarounds and iterating
from 0 to 50_000 value ranges with a cadence of 0.2s/it. The
range of the panel is 1000...400_000, so the values were slightly
interpolated during testing. The custom brightness curve added on
6.15 was disabled.
86016: 10101000000000000
86272: 10101000100000000
87808: 10101011100000000
251648: 111101011100000000
251649: 111101011100000001
86144: 10101000010000000
87809: 10101011100000001
251650: 111101011100000010
Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3803
Signed-off-by: Antheas Kapenekakis <l...@antheas.dev>
To me this sounds like a panel firmware bug that is best driven with the
panel vendor. But I'm guessing you're reporting it on proudution
hardware already in the field right? In the field it's basically
unheard of to update the panel firmware. The process is generally
speaking too dangerous/fragile.
So in that case a workaround would make sense. The actual issue as I'm
hearing it is that some fractional brightness values aren't working?
The API takes millinits, and I guess this was exposed by increasing the
granularity of values that userspace can program recently.
It's possible it was there before too, but there are probably "more"
values that can hit it.
Yes, it seems like if the first byte is 0 (always) or 1 (sometimes)
then the panel turns off. The patch that forces using 100mnits works
fully in my testing but Phil can still hit values on his device.
Perhaps at a 1nit or 2 nit accuracy all the values are ok which is why
Windows works fine
It has already shipped in 4 SKUs from 3 manufacturers as shown below.
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 7 +++++
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 6 ++++
drivers/gpu/drm/drm_panel_backlight_quirks.c | 29 +++++++++++++++++++
include/drm/drm_utils.h | 1 +
4 files changed, 43 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 4ad80ae615a2..156f2aae6828 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -3662,6 +3662,9 @@ static void update_connector_ext_caps(struct
amdgpu_dm_connector *aconnector)
if (panel_backlight_quirk->min_brightness)
caps->min_input_signal =
panel_backlight_quirk->min_brightness - 1;
+ if (panel_backlight_quirk->brightness_mask)
+ caps->brightness_mask =
+ panel_backlight_quirk->brightness_mask;
}
}
@@ -4862,6 +4865,10 @@ static void amdgpu_dm_backlight_set_level(struct
amdgpu_display_manager *dm,
brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
link = (struct dc_link *)dm->backlight_link[bl_idx];
+ /* Apply brightness quirk */
+ if (caps->brightness_mask)
+ brightness |= caps->brightness_mask;
+
I guess a problem I could see with using a mask is that there are
basically a bunch of values that are basically becoming no-op.
Yeah, 1/3, but those were noops anyway, because 3mnit is not much.
An alternative would be to decrease the max value (IE some number
smaller than 65535 and scale so userspace doesn't request these "broken"
values).
I'm not sure it's worth the effort though because you will probably
still find some subset of values with this problem.
The patch that was with the VPE quirk essentially does that and works
really well. But it is not perfect. That's at 100mnits.
The other comment I would say is this is probably very specific to AMD
and the millinit based brightness API; it might be better to keep the
quirk localized to amdgpu. I also talked to Phil offline about this and
he's got a draft patch that helps a similar system he's seeing this on
(presumably) with another panel.
I think it's worth getting that patch onto the list and we can weigh out
the alternatives.
I think it is the same panel spec. Perhaps it is a different factory,
but all affected devices are 7in OLED 1080p portrait panels with HDR.
I sent this patch before I cleaned it up fully because Phil wanted to
test it. It seems to work well. I think he's looking into implementing
the same quirk essentially.
I would not say it is specific to AMD per say. The bug happens in the
panel after being sent a command to set the brightness through a
shared drm helper. The same panel on intel with the same value would
have the same problem.
Does Intel also have millinits brightness control? I wasn't sure.
Perhaps not depending on the backlight code
that preprocesses the values. The drm_panel_backlight_quirks is used
only by amd currently in any case. And this quirk system allows
specifying EDID information, where the DRM code one that you did did
not. I only saw that after I had made this series.
Antheas
OK thanks. As you and Phil both have affected devices that are very
similar result get offline with Phil and come together with a new patch
series that works for both of you via quirks.
/* Change brightness based on AUX property */
mutex_lock(&dm->dc_lock);
if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index b937da0a4e4a..340f9b5f68eb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -200,6 +200,12 @@ struct amdgpu_dm_backlight_caps {
* @aux_support: Describes if the display supports AUX backlight.
*/
bool aux_support;
+ /**
+ * @brightness_mask: After deriving brightness, or it with this mask.
+ * This is used to workaround panels that have issues with certain
+ * brightness values.
+ */
+ u32 brightness_mask;
/**
* @ac_level: the default brightness if booted on AC
*/
diff --git a/drivers/gpu/drm/drm_panel_backlight_quirks.c
b/drivers/gpu/drm/drm_panel_backlight_quirks.c
index 3d386a96e50e..78c430b07d6a 100644
--- a/drivers/gpu/drm/drm_panel_backlight_quirks.c
+++ b/drivers/gpu/drm/drm_panel_backlight_quirks.c
@@ -45,6 +45,35 @@ static const struct drm_get_panel_backlight_quirk
drm_panel_min_backlight_quirks
.ident.name = "NE135A1M-NY1",
.quirk = { .min_brightness = 1, },
},
+ /* Have OLED Panels with brightness issue when last byte is 0/1 */
+ {
+ .dmi_match.field = DMI_SYS_VENDOR,
+ .dmi_match.value = "AYANEO",
+ .dmi_match_other.field = DMI_PRODUCT_NAME,
+ .dmi_match_other.value = "AYANEO 3",
+ .quirk = { .brightness_mask = 3, },
+ },
+ {
+ .dmi_match.field = DMI_SYS_VENDOR,
+ .dmi_match.value = "ZOTAC",
+ .dmi_match_other.field = DMI_BOARD_NAME,
+ .dmi_match_other.value = "G0A1W",
+ .quirk = { .brightness_mask = 3, },
+ },
+ {
+ .dmi_match.field = DMI_SYS_VENDOR,
+ .dmi_match.value = "ONE-NETBOOK",
+ .dmi_match_other.field = DMI_PRODUCT_NAME,
+ .dmi_match_other.value = "ONEXPLAYER F1Pro",
+ .quirk = { .brightness_mask = 3, },
+ },
+ {
+ .dmi_match.field = DMI_SYS_VENDOR,
+ .dmi_match.value = "ONE-NETBOOK",
+ .dmi_match_other.field = DMI_PRODUCT_NAME,
+ .dmi_match_other.value = "ONEXPLAYER F1 EVA-02",
+ .quirk = { .brightness_mask = 3, },
+ }
};
static bool drm_panel_min_backlight_quirk_matches(
diff --git a/include/drm/drm_utils.h b/include/drm/drm_utils.h
index 82eeee4a58ab..6a46f755daba 100644
--- a/include/drm/drm_utils.h
+++ b/include/drm/drm_utils.h
@@ -18,6 +18,7 @@ int drm_get_panel_orientation_quirk(int width, int height);
struct drm_panel_backlight_quirk {
u16 min_brightness;
+ u32 brightness_mask;
};
const struct drm_panel_backlight_quirk *