From: "Leo (Sunpeng) Li" <sunpeng.li@xxxxxxx> 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. Signed-off-by: Leo (Sunpeng) Li <sunpeng.li at amd.com> --- src/drmmode_display.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/src/drmmode_display.c b/src/drmmode_display.c index 7223b93..9c8c344 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -869,6 +869,95 @@ err_allocs: } /** + * 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; +} + +/** * 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. If the request is pending, then the change will make it's way into @@ -1130,7 +1219,22 @@ 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->degamma_lut; + + /* Compose legacy and non-legacy LUT */ + if (drmmode_crtc->gamma_lut) { + blob_data = malloc(expected_bytes); + if (!blob_data) + return BadAlloc; + 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); + free_blob_data = TRUE; + } else + blob_data = NULL; + } else if (cm_prop_index == CM_DEGAMMA_LUT) { expected_bytes = sizeof(struct drm_color_lut) * drmmode_crtc->degamma_lut_size; -- 2.7.4