Certain OLED devices malfunction on specific brightness levels.
Specifically, when DP_SOURCE_BACKLIGHT_LEVEL is written to with
the minor byte being 0x00 and sometimes 0x01, the panel forcibly
turns off until the device sleeps again. This is an issue on
multiple handhelds, including OneXPlayer F1 Pro and Ayaneo 3
(the panel is suspected to be the same-1080p 7in OLED).

Below are some examples. This was found by iterating over brighness
ranges while printing DP_SOURCE_BACKLIGHT_LEVEL. It was found that
the screen would malfunction on specific values, and some of them
were collected.

Broken:
 86016:  10101000000000000
 86272:  10101000100000000
 87808:  10101011100000000
251648: 111101011100000000
251649: 111101011100000001

Working:
 86144:  10101000010000000
 87809:  10101011100000001
251650: 111101011100000010

The reason for this is that the range manipulation is too granular.
AUX is currently written to with a granularity of 1. Forcing 100,
which on the Ayaneo 3 OLED yields 400*10=4000 values, is plenty of
granularity and fixes this issue. Iterating over the values through
Python shows that the final byte is never 0x00, and testing over the
entire range with a cadence of 0.2s/it and 73 increments (to saturate
the range) shows no issues. Windows likewise shows no issues.

Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3803
Signed-off-by: Antheas Kapenekakis <l...@antheas.dev>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 28 +++++++++++--------
 1 file changed, 17 insertions(+), 11 deletions(-)

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 cd0e2976e268..bb16adcafb88 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -4739,7 +4739,8 @@ static void amdgpu_dm_update_backlight_caps(struct 
amdgpu_display_manager *dm,
 }
 
 static int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
-                               unsigned int *min, unsigned int *max)
+                               unsigned int *min, unsigned int *max,
+                               unsigned int *multiple)
 {
        if (!caps)
                return 0;
@@ -4748,10 +4749,12 @@ static int get_brightness_range(const struct 
amdgpu_dm_backlight_caps *caps,
                // Firmware limits are in nits, DC API wants millinits.
                *max = 1000 * caps->aux_max_input_signal;
                *min = 1000 * caps->aux_min_input_signal;
+               *multiple = 100;
        } else {
                // Firmware limits are 8-bit, PWM control is 16-bit.
                *max = 0x101 * caps->max_input_signal;
                *min = 0x101 * caps->min_input_signal;
+               *multiple = 1;
        }
        return 1;
 }
@@ -4813,23 +4816,25 @@ static void convert_custom_brightness(const struct 
amdgpu_dm_backlight_caps *cap
 static u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps 
*caps,
                                        uint32_t brightness)
 {
-       unsigned int min, max;
+       unsigned int min, max, multiple;
 
-       if (!get_brightness_range(caps, &min, &max))
+       if (!get_brightness_range(caps, &min, &max, &multiple))
                return brightness;
 
        convert_custom_brightness(caps, min, max, &brightness);
 
-       // Rescale 0..max to min..max
-       return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);
+       // Rescale 0..max to min..max rounding to nearest multiple
+       return rounddown(
+               min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max),
+               multiple);
 }
 
 static u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps 
*caps,
                                      uint32_t brightness)
 {
-       unsigned int min, max;
+       unsigned int min, max, multiple;
 
-       if (!get_brightness_range(caps, &min, &max))
+       if (!get_brightness_range(caps, &min, &max, &multiple))
                return brightness;
 
        if (brightness < min)
@@ -4970,7 +4975,7 @@ amdgpu_dm_register_backlight_device(struct 
amdgpu_dm_connector *aconnector)
        struct backlight_properties props = { 0 };
        struct amdgpu_dm_backlight_caps *caps;
        char bl_name[16];
-       int min, max;
+       int min, max, multiple;
 
        if (aconnector->bl_idx == -1)
                return;
@@ -4983,15 +4988,16 @@ amdgpu_dm_register_backlight_device(struct 
amdgpu_dm_connector *aconnector)
        }
 
        caps = &dm->backlight_caps[aconnector->bl_idx];
-       if (get_brightness_range(caps, &min, &max)) {
+       if (get_brightness_range(caps, &min, &max, &multiple)) {
                if (power_supply_is_system_supplied() > 0)
                        props.brightness = DIV_ROUND_CLOSEST((max - min) * 
caps->ac_level, 100);
                else
                        props.brightness = DIV_ROUND_CLOSEST((max - min) * 
caps->dc_level, 100);
                /* min is zero, so max needs to be adjusted */
                props.max_brightness = max - min;
-               drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc 
%d\n", min, max,
-                       caps->ac_level, caps->dc_level);
+               drm_dbg(drm,
+                       "Backlight caps: min: %d, max: %d, ac %d, dc %d, 
multiple: %d\n",
+                       min, max, caps->ac_level, caps->dc_level, multiple);
        } else
                props.brightness = props.max_brightness = MAX_BACKLIGHT_LEVEL;
 
-- 
2.50.1


Reply via email to