From: "Leo (Sunpeng) Li" <[email protected]>

Frequently, a user may have non-legacy gamma enabled for monitor
correction, while using legacy gamma for things like
redshift/nightlight.

To do so, we compose the two LUTs. Legacy gamma will be applied first,
then non-legacy. i.e. non-legacy_LUT(legacy_LUT(in_color)).

Note that the staged gamma LUT within the driver-private CRTC will
always contain the non-legacy LUT. This is to ensure that we have a
cached copy for future compositions.

v2: Don't compose LUTs if legacy gamma is disabled (which is the case
    for deep 30bpp color). The legacy LUT won't be computed here,
    causing composition to spit out something invalid.

Signed-off-by: Leo (Sunpeng) Li <[email protected]>
---
 src/drmmode_display.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 186 insertions(+), 1 deletion(-)

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 6e5ae74..77a136e 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -788,6 +788,150 @@ static Bool 
drmmode_crtc_cm_enabled(drmmode_crtc_private_ptr drmmode_crtc)
 }
 
 /**
+ * If legacy LUT is a, and non-legacy LUT is b, then the result of b(a(x)) is
+ * returned in out_lut. out_lut's length is expected to be the same as the
+ * non-legacy LUT b.
+ *
+ * @a_(red|green|blue): The red, green, and blue components of the legacy LUT.
+ * @b_lut: The non-legacy LUT, in DRM's color LUT format.
+ * @out_lut: The composed LUT, in DRM's color LUT format.
+ * @len_a: Length of legacy lut.
+ * @len_b: Length of non-legacy lut.
+ */
+static void drmmode_lut_compose(uint16_t *a_red,
+                               uint16_t *a_green,
+                               uint16_t *a_blue,
+                               struct drm_color_lut *b_lut,
+                               struct drm_color_lut *out_lut,
+                               uint32_t len_a, uint32_t len_b)
+{
+       uint32_t i_l, i_r, i;
+       uint32_t i_amax, i_bmax;
+       uint32_t coeff_ibmax;
+       uint32_t j;
+       uint64_t a_out_ibmax;
+       int color;
+       size_t struct_size = sizeof(struct drm_color_lut);
+
+       uint32_t max_lut = (1 << 16) - 1;
+
+       i_amax = len_a - 1;
+       i_bmax = len_b - 1;
+
+       /* A linear interpolation is done on the legacy LUT before it is
+        * composed, to bring it up-to-size with the non-legacy LUT. The
+        * interpolation uses integers by keeping things multiplied until the
+        * last moment.
+        */
+       for (color = 0; color < 3; color++) {
+               uint16_t *a, *b, *out;
+
+               /* Set the initial pointers to the right color components. The
+                * inner for-loop will then maintain the correct offset from
+                * the initial element.
+                */
+               if (color == 0) {
+                       a = a_red;
+                       b = &b_lut[0].red;
+                       out = &out_lut[0].red;
+               } else if (color == 1) {
+                       a = a_green;
+                       b = &b_lut[0].green;
+                       out = &out_lut[0].green;
+               } else {
+                       a = a_blue;
+                       b = &b_lut[0].blue;
+                       out = &out_lut[0].blue;
+               }
+
+               for (i = 0; i < len_b; i++) {
+                       /* i_l and i_r tracks the left and right elements in
+                        * a_lut, to the sample point i. Also handle last
+                        * element edge case, when i_l = i_amax.
+                        */
+                       i_l = i * i_amax / i_bmax;
+                       i_r = i_l + !!(i_amax - i_l);
+
+                       /* coeff is intended to be in [0, 1), depending on
+                        * where sample i is between i_l and i_r. We keep it
+                        * multiplied with i_bmax throughout to maintain
+                        * precision */
+                       coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
+                       a_out_ibmax = i_bmax * a[i_l] +
+                                     coeff_ibmax * (a[i_r] - a[i_l]);
+
+                       /* j = floor((a_out/max_lut)*i_bmax).
+                        * i.e. the element in LUT b that a_out maps to. We
+                        * have to divide by max_lut to normalize a_out, since
+                        * values in the LUTs are [0, 1<<16)
+                        */
+                       j = a_out_ibmax / max_lut;
+                       *(uint16_t*)((void*)out + (i*struct_size)) =
+                               *(uint16_t*)((void*)b + (j*struct_size));
+               }
+       }
+
+       for (i = 0; i < len_b; i++)
+               out_lut[i].reserved = 0;
+}
+
+/**
+ * Resize a LUT, using linear interpolation.
+ *
+ * @in_(red|green|blue): Legacy LUT components
+ * @out_lut: The resized LUT is returned here, in DRM color LUT format.
+ * @len_in: Length of legacy LUT.
+ * @len_out: Length of out_lut, i.e. the target size.
+ */
+static void drmmode_lut_interpolate(uint16_t *in_red,
+                                   uint16_t *in_green,
+                                   uint16_t *in_blue,
+                                   struct drm_color_lut *out_lut,
+                                   uint32_t len_in, uint32_t len_out)
+{
+       uint32_t i_l, i_r, i;
+       uint32_t i_amax, i_bmax;
+       uint32_t coeff_ibmax;
+       uint64_t out_ibmax;
+       int color;
+       size_t struct_size = sizeof(struct drm_color_lut);
+
+       i_amax = len_in - 1;
+       i_bmax = len_out - 1;
+
+       /* See @drmmode_lut_compose for details */
+       for (color = 0; color < 3; color++) {
+               uint16_t *in, *out;
+
+               if (color == 0) {
+                       in = in_red;
+                       out = &out_lut[0].red;
+               } else if (color == 1) {
+                       in = in_green;
+                       out = &out_lut[0].green;
+               } else {
+                       in = in_blue;
+                       out = &out_lut[0].blue;
+               }
+
+               for (i = 0; i < len_out; i++) {
+                       i_l = i * i_amax / i_bmax;
+                       i_r = i_l + !!(i_amax - i_l);
+
+                       coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
+                       out_ibmax = i_bmax * in[i_l] +
+                                     coeff_ibmax * (in[i_r] - in[i_l]);
+
+                       *(uint16_t*)((void*)out + (i*struct_size)) =
+                               out_ibmax / i_bmax;
+               }
+       }
+
+       for (i = 0; i < len_out; i++)
+               out_lut[i].reserved = 0;
+}
+
+/**
  * Configure and change a color property on a CRTC, through RandR. Only the
  * specified output will be affected, even if the CRTC is attached to multiple
  * outputs. Note that changes will be non-pending: the changes won't be pushed
@@ -1000,6 +1144,7 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
 {
        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
        AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
+       Bool free_blob_data = FALSE;
        uint32_t created_blob_id = 0;
        uint32_t drm_prop_id;
        size_t expected_bytes = 0;
@@ -1013,7 +1158,39 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
                /* Calculate the expected size of value in bytes */
                expected_bytes = sizeof(struct drm_color_lut) *
                                        drmmode_crtc->gamma_lut_size;
-               blob_data = drmmode_crtc->gamma_lut;
+
+               /* Legacy gamma LUT is disabled on deep 30bpp color. In which
+                * case, directly use non-legacy LUT.
+                */
+               if (crtc->funcs->gamma_set == NULL) {
+                       blob_data = drmmode_crtc->gamma_lut;
+                       goto do_push;
+               }
+
+               blob_data = malloc(expected_bytes);
+               if (!blob_data)
+                       return BadAlloc;
+
+               free_blob_data = TRUE;
+               /*
+                * Compose legacy and non-legacy LUT if non-legacy was set.
+                * Otherwise, interpolate legacy LUT to non-legacy size.
+                */
+               if (drmmode_crtc->gamma_lut) {
+                       drmmode_lut_compose(crtc->gamma_red,
+                                           crtc->gamma_green,
+                                           crtc->gamma_blue,
+                                           drmmode_crtc->gamma_lut,
+                                           blob_data, crtc->gamma_size,
+                                           drmmode_crtc->gamma_lut_size);
+               } else {
+                       drmmode_lut_interpolate(crtc->gamma_red,
+                                               crtc->gamma_green,
+                                               crtc->gamma_blue,
+                                               blob_data,
+                                               crtc->gamma_size,
+                                               drmmode_crtc->gamma_lut_size);
+               }
        } else if (cm_prop_index == CM_DEGAMMA_LUT) {
                expected_bytes = sizeof(struct drm_color_lut) *
                                        drmmode_crtc->degamma_lut_size;
@@ -1024,6 +1201,7 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
        } else
                return BadName;
 
+do_push:
        if (blob_data) {
                ret = drmModeCreatePropertyBlob(pAMDGPUEnt->fd,
                                                blob_data, expected_bytes,
@@ -1032,6 +1210,8 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
                        xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
                                   "Creating DRM blob failed with errno %d\n",
                                   ret);
+                       if (free_blob_data)
+                               free(blob_data);
                        return BadRequest;
                }
        }
@@ -1053,9 +1233,14 @@ static int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
                xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
                           "Setting DRM property blob failed with errno %d\n",
                           ret);
+               if (free_blob_data)
+                       free(blob_data);
                return BadRequest;
        }
 
+       if (free_blob_data)
+               free(blob_data);
+
        return Success;
 }
 
-- 
2.7.4

_______________________________________________
amd-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/amd-gfx

Reply via email to