mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 00/11] drm/vkms: improve color curve LUTs precision
@ 2026-08-04 20:33 Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 01/11] drm/vkms: rename VKMS_LUT_SIZE to VKMS_GAMMA_LUT_SIZE Leandro Ribeiro
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

Currently VKMS only supports uniform LUTs. Besides the userspace
provided LUTs, it also represents color curves with LUTs, as it can't
simply implement the continuous curve formulas (floating-point would be
required).

This adds support to represent color curves with non-uniform LUTs,
improving the precision significantly. It also provides a script
that was used to create these LUTs.

The optimized LUTs use more samples in regions where the interpolation
and quantization errors are higher (usually in regions with a steeper
slope). The LUT generator script compares different strategies and uses
greedy subdivision, which outperformed the other methods, to generate
the optimized LUTs for VKMS.

Leandro Ribeiro (11):
  drm/vkms: rename VKMS_LUT_SIZE to VKMS_GAMMA_LUT_SIZE
  drm/vkms: allow color curve LUTs to have different sizes
  drm/vkms: remove TEST_LUT_SIZE
  drm/vkms: remove linear_eotf
  drm/vkms: improve the way in which we access LUT member
  drm/vkms: rename struct vkms_color_lut::base to y
  drm/vkms: rename get_lut_index() to get_uniform_lut_index()
  drm/vkms: add support to non-uniform LUT for internal color curves
  drm/vkms: test sRGB and inverse sRGB LUTs using more samples
  drm/vkms: add script to create optimized LUTs for color curves
  drm/vkms: replace uniform sRGB LUT and its inverse with optimal ones

 .../gpu/drm/vkms/scripts/color-curve-lut.py   |  561 +++++++
 drivers/gpu/drm/vkms/tests/vkms_color_test.c  |   46 +-
 drivers/gpu/drm/vkms/vkms_composer.c          |  127 +-
 drivers/gpu/drm/vkms/vkms_composer.h          |    2 +-
 drivers/gpu/drm/vkms/vkms_crtc.c              |    4 +-
 drivers/gpu/drm/vkms/vkms_drv.c               |    2 +-
 drivers/gpu/drm/vkms/vkms_drv.h               |   13 +-
 drivers/gpu/drm/vkms/vkms_luts.c              | 1469 ++++++++---------
 drivers/gpu/drm/vkms/vkms_luts.h              |    3 -
 9 files changed, 1383 insertions(+), 844 deletions(-)
 create mode 100644 drivers/gpu/drm/vkms/scripts/color-curve-lut.py

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 01/11] drm/vkms: rename VKMS_LUT_SIZE to VKMS_GAMMA_LUT_SIZE
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 02/11] drm/vkms: allow color curve LUTs to have different sizes Leandro Ribeiro
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

This is the size of the CRTC gamma LUT size. So let's specify this
in the name.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/vkms_crtc.c | 4 ++--
 drivers/gpu/drm/vkms/vkms_drv.c  | 2 +-
 drivers/gpu/drm/vkms/vkms_drv.h  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index 079abfba427d..54ebb112b3b3 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -220,13 +220,13 @@ struct vkms_output *vkms_crtc_init(struct drm_device *dev, struct drm_plane *pri
 
 	drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs);
 
-	ret = drm_mode_crtc_set_gamma_size(crtc, VKMS_LUT_SIZE);
+	ret = drm_mode_crtc_set_gamma_size(crtc, VKMS_GAMMA_LUT_SIZE);
 	if (ret) {
 		DRM_ERROR("Failed to set gamma size\n");
 		return ERR_PTR(ret);
 	}
 
-	drm_crtc_enable_color_mgmt(crtc, 0, false, VKMS_LUT_SIZE);
+	drm_crtc_enable_color_mgmt(crtc, 0, false, VKMS_GAMMA_LUT_SIZE);
 
 	drm_crtc_attach_background_color_property(crtc);
 
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 5a640b531d88..86a8a95694fd 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -113,7 +113,7 @@ static int vkms_atomic_check(struct drm_device *dev, struct drm_atomic_commit *s
 			continue;
 
 		if (new_crtc_state->gamma_lut->length / sizeof(struct drm_color_lut *)
-		    > VKMS_LUT_SIZE)
+		    > VKMS_GAMMA_LUT_SIZE)
 			return -EINVAL;
 	}
 
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 0933e4ce0ff0..1975843abe92 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -25,7 +25,7 @@
 
 #define NUM_OVERLAY_PLANES 8
 
-#define VKMS_LUT_SIZE 256
+#define VKMS_GAMMA_LUT_SIZE 256
 
 /**
  * struct vkms_frame_info - Structure to store the state of a frame
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 02/11] drm/vkms: allow color curve LUTs to have different sizes
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 01/11] drm/vkms: rename VKMS_LUT_SIZE to VKMS_GAMMA_LUT_SIZE Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 03/11] drm/vkms: remove TEST_LUT_SIZE Leandro Ribeiro
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

Instead of keeping a hardcoded size that all LUTs representing color
curves must use, use ARRAY_SIZE() instead.

Besides making the code clearer, this allows us to create optimal LUTs
for each color curve, and each of them can naturally have a different
size. In the next commits we introduce these optimal LUTs.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c |  4 ++--
 drivers/gpu/drm/vkms/vkms_luts.c             | 12 ++++++------
 drivers/gpu/drm/vkms/vkms_luts.h             |  2 --
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index 1a1c7cac2f15..18e73a8a5c31 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -128,7 +128,7 @@ static void vkms_color_test_lerp(struct kunit *test)
 
 static void vkms_color_test_linear(struct kunit *test)
 {
-	for (int i = 0; i < LUT_SIZE; i++) {
+	for (int i = 0; i < linear_eotf.lut_length; i++) {
 		int linear = apply_lut_to_channel_value(&linear_eotf, i * 0x101, LUT_RED);
 
 		KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(linear, 0x101), i);
@@ -139,7 +139,7 @@ static void vkms_color_srgb_inv_srgb(struct kunit *test)
 {
 	u16 srgb, final;
 
-	for (int i = 0; i < LUT_SIZE; i++) {
+	for (int i = 0; i < srgb_eotf.lut_length; i++) {
 		srgb = apply_lut_to_channel_value(&srgb_eotf, i * 0x101, LUT_RED);
 		final = apply_lut_to_channel_value(&srgb_inv_eotf, srgb, LUT_RED);
 
diff --git a/drivers/gpu/drm/vkms/vkms_luts.c b/drivers/gpu/drm/vkms/vkms_luts.c
index 82cb792f10d8..6dcdef26bda8 100644
--- a/drivers/gpu/drm/vkms/vkms_luts.c
+++ b/drivers/gpu/drm/vkms/vkms_luts.c
@@ -12,7 +12,7 @@
  * https://gitlab.freedesktop.org/hwentland/lutgen
  */
 
-static struct drm_color_lut linear_array[LUT_SIZE] = {
+static struct drm_color_lut linear_array[] = {
 	{ 0x0, 0x0, 0x0, 0 },
 	{ 0x101, 0x101, 0x101, 0 },
 	{ 0x202, 0x202, 0x202, 0 },
@@ -273,12 +273,12 @@ static struct drm_color_lut linear_array[LUT_SIZE] = {
 
 const struct vkms_color_lut linear_eotf = {
 	.base = linear_array,
-	.lut_length = LUT_SIZE,
+	.lut_length = ARRAY_SIZE(linear_array),
 	.channel_value2index_ratio = 0xff00ffll
 };
 EXPORT_SYMBOL(linear_eotf);
 
-static struct drm_color_lut srgb_array[LUT_SIZE] = {
+static struct drm_color_lut srgb_array[] = {
 	{ 0x0, 0x0, 0x0, 0 },
 	{ 0x13, 0x13, 0x13, 0 },
 	{ 0x27, 0x27, 0x27, 0 },
@@ -539,12 +539,12 @@ static struct drm_color_lut srgb_array[LUT_SIZE] = {
 
 const struct vkms_color_lut srgb_eotf = {
 	.base = srgb_array,
-	.lut_length = LUT_SIZE,
+	.lut_length = ARRAY_SIZE(srgb_array),
 	.channel_value2index_ratio = 0xff00ffll
 };
 EXPORT_SYMBOL(srgb_eotf);
 
-static struct drm_color_lut srgb_inv_array[LUT_SIZE] = {
+static struct drm_color_lut srgb_inv_array[] = {
 	{ 0x0, 0x0, 0x0, 0 },
 	{ 0xcc2, 0xcc2, 0xcc2, 0 },
 	{ 0x15be, 0x15be, 0x15be, 0 },
@@ -805,7 +805,7 @@ static struct drm_color_lut srgb_inv_array[LUT_SIZE] = {
 
 const struct vkms_color_lut srgb_inv_eotf = {
 	.base = srgb_inv_array,
-	.lut_length = LUT_SIZE,
+	.lut_length = ARRAY_SIZE(srgb_inv_array),
 	.channel_value2index_ratio = 0xff00ffll
 };
 EXPORT_SYMBOL(srgb_inv_eotf);
diff --git a/drivers/gpu/drm/vkms/vkms_luts.h b/drivers/gpu/drm/vkms/vkms_luts.h
index 925a4a7b84e2..8078ab33b83e 100644
--- a/drivers/gpu/drm/vkms/vkms_luts.h
+++ b/drivers/gpu/drm/vkms/vkms_luts.h
@@ -3,8 +3,6 @@
 #ifndef _VKMS_LUTS_H_
 #define _VKMS_LUTS_H_
 
-#define LUT_SIZE 256
-
 extern const struct vkms_color_lut linear_eotf;
 extern const struct vkms_color_lut srgb_eotf;
 extern const struct vkms_color_lut srgb_inv_eotf;
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 03/11] drm/vkms: remove TEST_LUT_SIZE
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 01/11] drm/vkms: rename VKMS_LUT_SIZE to VKMS_GAMMA_LUT_SIZE Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 02/11] drm/vkms: allow color curve LUTs to have different sizes Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 04/11] drm/vkms: remove linear_eotf Leandro Ribeiro
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

This can be replaced by ARRAY_SIZE(), so drop it.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index 18e73a8a5c31..3489513af352 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -8,11 +8,9 @@
 #include "../vkms_drv.h"
 #include "../vkms_luts.h"
 
-#define TEST_LUT_SIZE 16
-
 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
 
-static struct drm_color_lut test_linear_array[TEST_LUT_SIZE] = {
+static struct drm_color_lut test_linear_array[] = {
 	{ 0x0, 0x0, 0x0, 0 },
 	{ 0x1111, 0x1111, 0x1111, 0 },
 	{ 0x2222, 0x2222, 0x2222, 0 },
@@ -84,7 +82,7 @@ static const struct vkms_color_test_lerp_params color_test_lerp_cases[] = {
 
 static const struct vkms_color_lut test_linear_lut = {
 	.base = test_linear_array,
-	.lut_length = TEST_LUT_SIZE,
+	.lut_length = ARRAY_SIZE(test_linear_array),
 	.channel_value2index_ratio = 0xf000fll
 };
 
@@ -96,7 +94,7 @@ static void vkms_color_test_get_lut_index(struct kunit *test)
 	lut_index = get_lut_index(&test_linear_lut, test_linear_array[0].red);
 	KUNIT_EXPECT_EQ(test, drm_fixp2int(lut_index), 0);
 
-	for (i = 0; i < TEST_LUT_SIZE; i++) {
+	for (i = 0; i < test_linear_lut.lut_length; i++) {
 		lut_index = get_lut_index(&test_linear_lut, test_linear_array[i].red);
 		KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i);
 	}
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 04/11] drm/vkms: remove linear_eotf
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (2 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 03/11] drm/vkms: remove TEST_LUT_SIZE Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 05/11] drm/vkms: improve the way in which we access LUT member Leandro Ribeiro
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

This is being used only for testing purposes. We can achieve the same
using test_linear_lut, so let's drop it.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c |   4 +-
 drivers/gpu/drm/vkms/vkms_luts.c             | 266 -------------------
 drivers/gpu/drm/vkms/vkms_luts.h             |   1 -
 3 files changed, 2 insertions(+), 269 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index 3489513af352..28614edb8eb1 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -126,8 +126,8 @@ static void vkms_color_test_lerp(struct kunit *test)
 
 static void vkms_color_test_linear(struct kunit *test)
 {
-	for (int i = 0; i < linear_eotf.lut_length; i++) {
-		int linear = apply_lut_to_channel_value(&linear_eotf, i * 0x101, LUT_RED);
+	for (int i = 0; i < test_linear_lut.lut_length; i++) {
+		int linear = apply_lut_to_channel_value(&test_linear_lut, i * 0x101, LUT_RED);
 
 		KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(linear, 0x101), i);
 	}
diff --git a/drivers/gpu/drm/vkms/vkms_luts.c b/drivers/gpu/drm/vkms/vkms_luts.c
index 6dcdef26bda8..d0e96df2c120 100644
--- a/drivers/gpu/drm/vkms/vkms_luts.c
+++ b/drivers/gpu/drm/vkms/vkms_luts.c
@@ -12,272 +12,6 @@
  * https://gitlab.freedesktop.org/hwentland/lutgen
  */
 
-static struct drm_color_lut linear_array[] = {
-	{ 0x0, 0x0, 0x0, 0 },
-	{ 0x101, 0x101, 0x101, 0 },
-	{ 0x202, 0x202, 0x202, 0 },
-	{ 0x303, 0x303, 0x303, 0 },
-	{ 0x404, 0x404, 0x404, 0 },
-	{ 0x505, 0x505, 0x505, 0 },
-	{ 0x606, 0x606, 0x606, 0 },
-	{ 0x707, 0x707, 0x707, 0 },
-	{ 0x808, 0x808, 0x808, 0 },
-	{ 0x909, 0x909, 0x909, 0 },
-	{ 0xa0a, 0xa0a, 0xa0a, 0 },
-	{ 0xb0b, 0xb0b, 0xb0b, 0 },
-	{ 0xc0c, 0xc0c, 0xc0c, 0 },
-	{ 0xd0d, 0xd0d, 0xd0d, 0 },
-	{ 0xe0e, 0xe0e, 0xe0e, 0 },
-	{ 0xf0f, 0xf0f, 0xf0f, 0 },
-	{ 0x1010, 0x1010, 0x1010, 0 },
-	{ 0x1111, 0x1111, 0x1111, 0 },
-	{ 0x1212, 0x1212, 0x1212, 0 },
-	{ 0x1313, 0x1313, 0x1313, 0 },
-	{ 0x1414, 0x1414, 0x1414, 0 },
-	{ 0x1515, 0x1515, 0x1515, 0 },
-	{ 0x1616, 0x1616, 0x1616, 0 },
-	{ 0x1717, 0x1717, 0x1717, 0 },
-	{ 0x1818, 0x1818, 0x1818, 0 },
-	{ 0x1919, 0x1919, 0x1919, 0 },
-	{ 0x1a1a, 0x1a1a, 0x1a1a, 0 },
-	{ 0x1b1b, 0x1b1b, 0x1b1b, 0 },
-	{ 0x1c1c, 0x1c1c, 0x1c1c, 0 },
-	{ 0x1d1d, 0x1d1d, 0x1d1d, 0 },
-	{ 0x1e1e, 0x1e1e, 0x1e1e, 0 },
-	{ 0x1f1f, 0x1f1f, 0x1f1f, 0 },
-	{ 0x2020, 0x2020, 0x2020, 0 },
-	{ 0x2121, 0x2121, 0x2121, 0 },
-	{ 0x2222, 0x2222, 0x2222, 0 },
-	{ 0x2323, 0x2323, 0x2323, 0 },
-	{ 0x2424, 0x2424, 0x2424, 0 },
-	{ 0x2525, 0x2525, 0x2525, 0 },
-	{ 0x2626, 0x2626, 0x2626, 0 },
-	{ 0x2727, 0x2727, 0x2727, 0 },
-	{ 0x2828, 0x2828, 0x2828, 0 },
-	{ 0x2929, 0x2929, 0x2929, 0 },
-	{ 0x2a2a, 0x2a2a, 0x2a2a, 0 },
-	{ 0x2b2b, 0x2b2b, 0x2b2b, 0 },
-	{ 0x2c2c, 0x2c2c, 0x2c2c, 0 },
-	{ 0x2d2d, 0x2d2d, 0x2d2d, 0 },
-	{ 0x2e2e, 0x2e2e, 0x2e2e, 0 },
-	{ 0x2f2f, 0x2f2f, 0x2f2f, 0 },
-	{ 0x3030, 0x3030, 0x3030, 0 },
-	{ 0x3131, 0x3131, 0x3131, 0 },
-	{ 0x3232, 0x3232, 0x3232, 0 },
-	{ 0x3333, 0x3333, 0x3333, 0 },
-	{ 0x3434, 0x3434, 0x3434, 0 },
-	{ 0x3535, 0x3535, 0x3535, 0 },
-	{ 0x3636, 0x3636, 0x3636, 0 },
-	{ 0x3737, 0x3737, 0x3737, 0 },
-	{ 0x3838, 0x3838, 0x3838, 0 },
-	{ 0x3939, 0x3939, 0x3939, 0 },
-	{ 0x3a3a, 0x3a3a, 0x3a3a, 0 },
-	{ 0x3b3b, 0x3b3b, 0x3b3b, 0 },
-	{ 0x3c3c, 0x3c3c, 0x3c3c, 0 },
-	{ 0x3d3d, 0x3d3d, 0x3d3d, 0 },
-	{ 0x3e3e, 0x3e3e, 0x3e3e, 0 },
-	{ 0x3f3f, 0x3f3f, 0x3f3f, 0 },
-	{ 0x4040, 0x4040, 0x4040, 0 },
-	{ 0x4141, 0x4141, 0x4141, 0 },
-	{ 0x4242, 0x4242, 0x4242, 0 },
-	{ 0x4343, 0x4343, 0x4343, 0 },
-	{ 0x4444, 0x4444, 0x4444, 0 },
-	{ 0x4545, 0x4545, 0x4545, 0 },
-	{ 0x4646, 0x4646, 0x4646, 0 },
-	{ 0x4747, 0x4747, 0x4747, 0 },
-	{ 0x4848, 0x4848, 0x4848, 0 },
-	{ 0x4949, 0x4949, 0x4949, 0 },
-	{ 0x4a4a, 0x4a4a, 0x4a4a, 0 },
-	{ 0x4b4b, 0x4b4b, 0x4b4b, 0 },
-	{ 0x4c4c, 0x4c4c, 0x4c4c, 0 },
-	{ 0x4d4d, 0x4d4d, 0x4d4d, 0 },
-	{ 0x4e4e, 0x4e4e, 0x4e4e, 0 },
-	{ 0x4f4f, 0x4f4f, 0x4f4f, 0 },
-	{ 0x5050, 0x5050, 0x5050, 0 },
-	{ 0x5151, 0x5151, 0x5151, 0 },
-	{ 0x5252, 0x5252, 0x5252, 0 },
-	{ 0x5353, 0x5353, 0x5353, 0 },
-	{ 0x5454, 0x5454, 0x5454, 0 },
-	{ 0x5555, 0x5555, 0x5555, 0 },
-	{ 0x5656, 0x5656, 0x5656, 0 },
-	{ 0x5757, 0x5757, 0x5757, 0 },
-	{ 0x5858, 0x5858, 0x5858, 0 },
-	{ 0x5959, 0x5959, 0x5959, 0 },
-	{ 0x5a5a, 0x5a5a, 0x5a5a, 0 },
-	{ 0x5b5b, 0x5b5b, 0x5b5b, 0 },
-	{ 0x5c5c, 0x5c5c, 0x5c5c, 0 },
-	{ 0x5d5d, 0x5d5d, 0x5d5d, 0 },
-	{ 0x5e5e, 0x5e5e, 0x5e5e, 0 },
-	{ 0x5f5f, 0x5f5f, 0x5f5f, 0 },
-	{ 0x6060, 0x6060, 0x6060, 0 },
-	{ 0x6161, 0x6161, 0x6161, 0 },
-	{ 0x6262, 0x6262, 0x6262, 0 },
-	{ 0x6363, 0x6363, 0x6363, 0 },
-	{ 0x6464, 0x6464, 0x6464, 0 },
-	{ 0x6565, 0x6565, 0x6565, 0 },
-	{ 0x6666, 0x6666, 0x6666, 0 },
-	{ 0x6767, 0x6767, 0x6767, 0 },
-	{ 0x6868, 0x6868, 0x6868, 0 },
-	{ 0x6969, 0x6969, 0x6969, 0 },
-	{ 0x6a6a, 0x6a6a, 0x6a6a, 0 },
-	{ 0x6b6b, 0x6b6b, 0x6b6b, 0 },
-	{ 0x6c6c, 0x6c6c, 0x6c6c, 0 },
-	{ 0x6d6d, 0x6d6d, 0x6d6d, 0 },
-	{ 0x6e6e, 0x6e6e, 0x6e6e, 0 },
-	{ 0x6f6f, 0x6f6f, 0x6f6f, 0 },
-	{ 0x7070, 0x7070, 0x7070, 0 },
-	{ 0x7171, 0x7171, 0x7171, 0 },
-	{ 0x7272, 0x7272, 0x7272, 0 },
-	{ 0x7373, 0x7373, 0x7373, 0 },
-	{ 0x7474, 0x7474, 0x7474, 0 },
-	{ 0x7575, 0x7575, 0x7575, 0 },
-	{ 0x7676, 0x7676, 0x7676, 0 },
-	{ 0x7777, 0x7777, 0x7777, 0 },
-	{ 0x7878, 0x7878, 0x7878, 0 },
-	{ 0x7979, 0x7979, 0x7979, 0 },
-	{ 0x7a7a, 0x7a7a, 0x7a7a, 0 },
-	{ 0x7b7b, 0x7b7b, 0x7b7b, 0 },
-	{ 0x7c7c, 0x7c7c, 0x7c7c, 0 },
-	{ 0x7d7d, 0x7d7d, 0x7d7d, 0 },
-	{ 0x7e7e, 0x7e7e, 0x7e7e, 0 },
-	{ 0x7f7f, 0x7f7f, 0x7f7f, 0 },
-	{ 0x8080, 0x8080, 0x8080, 0 },
-	{ 0x8181, 0x8181, 0x8181, 0 },
-	{ 0x8282, 0x8282, 0x8282, 0 },
-	{ 0x8383, 0x8383, 0x8383, 0 },
-	{ 0x8484, 0x8484, 0x8484, 0 },
-	{ 0x8585, 0x8585, 0x8585, 0 },
-	{ 0x8686, 0x8686, 0x8686, 0 },
-	{ 0x8787, 0x8787, 0x8787, 0 },
-	{ 0x8888, 0x8888, 0x8888, 0 },
-	{ 0x8989, 0x8989, 0x8989, 0 },
-	{ 0x8a8a, 0x8a8a, 0x8a8a, 0 },
-	{ 0x8b8b, 0x8b8b, 0x8b8b, 0 },
-	{ 0x8c8c, 0x8c8c, 0x8c8c, 0 },
-	{ 0x8d8d, 0x8d8d, 0x8d8d, 0 },
-	{ 0x8e8e, 0x8e8e, 0x8e8e, 0 },
-	{ 0x8f8f, 0x8f8f, 0x8f8f, 0 },
-	{ 0x9090, 0x9090, 0x9090, 0 },
-	{ 0x9191, 0x9191, 0x9191, 0 },
-	{ 0x9292, 0x9292, 0x9292, 0 },
-	{ 0x9393, 0x9393, 0x9393, 0 },
-	{ 0x9494, 0x9494, 0x9494, 0 },
-	{ 0x9595, 0x9595, 0x9595, 0 },
-	{ 0x9696, 0x9696, 0x9696, 0 },
-	{ 0x9797, 0x9797, 0x9797, 0 },
-	{ 0x9898, 0x9898, 0x9898, 0 },
-	{ 0x9999, 0x9999, 0x9999, 0 },
-	{ 0x9a9a, 0x9a9a, 0x9a9a, 0 },
-	{ 0x9b9b, 0x9b9b, 0x9b9b, 0 },
-	{ 0x9c9c, 0x9c9c, 0x9c9c, 0 },
-	{ 0x9d9d, 0x9d9d, 0x9d9d, 0 },
-	{ 0x9e9e, 0x9e9e, 0x9e9e, 0 },
-	{ 0x9f9f, 0x9f9f, 0x9f9f, 0 },
-	{ 0xa0a0, 0xa0a0, 0xa0a0, 0 },
-	{ 0xa1a1, 0xa1a1, 0xa1a1, 0 },
-	{ 0xa2a2, 0xa2a2, 0xa2a2, 0 },
-	{ 0xa3a3, 0xa3a3, 0xa3a3, 0 },
-	{ 0xa4a4, 0xa4a4, 0xa4a4, 0 },
-	{ 0xa5a5, 0xa5a5, 0xa5a5, 0 },
-	{ 0xa6a6, 0xa6a6, 0xa6a6, 0 },
-	{ 0xa7a7, 0xa7a7, 0xa7a7, 0 },
-	{ 0xa8a8, 0xa8a8, 0xa8a8, 0 },
-	{ 0xa9a9, 0xa9a9, 0xa9a9, 0 },
-	{ 0xaaaa, 0xaaaa, 0xaaaa, 0 },
-	{ 0xabab, 0xabab, 0xabab, 0 },
-	{ 0xacac, 0xacac, 0xacac, 0 },
-	{ 0xadad, 0xadad, 0xadad, 0 },
-	{ 0xaeae, 0xaeae, 0xaeae, 0 },
-	{ 0xafaf, 0xafaf, 0xafaf, 0 },
-	{ 0xb0b0, 0xb0b0, 0xb0b0, 0 },
-	{ 0xb1b1, 0xb1b1, 0xb1b1, 0 },
-	{ 0xb2b2, 0xb2b2, 0xb2b2, 0 },
-	{ 0xb3b3, 0xb3b3, 0xb3b3, 0 },
-	{ 0xb4b4, 0xb4b4, 0xb4b4, 0 },
-	{ 0xb5b5, 0xb5b5, 0xb5b5, 0 },
-	{ 0xb6b6, 0xb6b6, 0xb6b6, 0 },
-	{ 0xb7b7, 0xb7b7, 0xb7b7, 0 },
-	{ 0xb8b8, 0xb8b8, 0xb8b8, 0 },
-	{ 0xb9b9, 0xb9b9, 0xb9b9, 0 },
-	{ 0xbaba, 0xbaba, 0xbaba, 0 },
-	{ 0xbbbb, 0xbbbb, 0xbbbb, 0 },
-	{ 0xbcbc, 0xbcbc, 0xbcbc, 0 },
-	{ 0xbdbd, 0xbdbd, 0xbdbd, 0 },
-	{ 0xbebe, 0xbebe, 0xbebe, 0 },
-	{ 0xbfbf, 0xbfbf, 0xbfbf, 0 },
-	{ 0xc0c0, 0xc0c0, 0xc0c0, 0 },
-	{ 0xc1c1, 0xc1c1, 0xc1c1, 0 },
-	{ 0xc2c2, 0xc2c2, 0xc2c2, 0 },
-	{ 0xc3c3, 0xc3c3, 0xc3c3, 0 },
-	{ 0xc4c4, 0xc4c4, 0xc4c4, 0 },
-	{ 0xc5c5, 0xc5c5, 0xc5c5, 0 },
-	{ 0xc6c6, 0xc6c6, 0xc6c6, 0 },
-	{ 0xc7c7, 0xc7c7, 0xc7c7, 0 },
-	{ 0xc8c8, 0xc8c8, 0xc8c8, 0 },
-	{ 0xc9c9, 0xc9c9, 0xc9c9, 0 },
-	{ 0xcaca, 0xcaca, 0xcaca, 0 },
-	{ 0xcbcb, 0xcbcb, 0xcbcb, 0 },
-	{ 0xcccc, 0xcccc, 0xcccc, 0 },
-	{ 0xcdcd, 0xcdcd, 0xcdcd, 0 },
-	{ 0xcece, 0xcece, 0xcece, 0 },
-	{ 0xcfcf, 0xcfcf, 0xcfcf, 0 },
-	{ 0xd0d0, 0xd0d0, 0xd0d0, 0 },
-	{ 0xd1d1, 0xd1d1, 0xd1d1, 0 },
-	{ 0xd2d2, 0xd2d2, 0xd2d2, 0 },
-	{ 0xd3d3, 0xd3d3, 0xd3d3, 0 },
-	{ 0xd4d4, 0xd4d4, 0xd4d4, 0 },
-	{ 0xd5d5, 0xd5d5, 0xd5d5, 0 },
-	{ 0xd6d6, 0xd6d6, 0xd6d6, 0 },
-	{ 0xd7d7, 0xd7d7, 0xd7d7, 0 },
-	{ 0xd8d8, 0xd8d8, 0xd8d8, 0 },
-	{ 0xd9d9, 0xd9d9, 0xd9d9, 0 },
-	{ 0xdada, 0xdada, 0xdada, 0 },
-	{ 0xdbdb, 0xdbdb, 0xdbdb, 0 },
-	{ 0xdcdc, 0xdcdc, 0xdcdc, 0 },
-	{ 0xdddd, 0xdddd, 0xdddd, 0 },
-	{ 0xdede, 0xdede, 0xdede, 0 },
-	{ 0xdfdf, 0xdfdf, 0xdfdf, 0 },
-	{ 0xe0e0, 0xe0e0, 0xe0e0, 0 },
-	{ 0xe1e1, 0xe1e1, 0xe1e1, 0 },
-	{ 0xe2e2, 0xe2e2, 0xe2e2, 0 },
-	{ 0xe3e3, 0xe3e3, 0xe3e3, 0 },
-	{ 0xe4e4, 0xe4e4, 0xe4e4, 0 },
-	{ 0xe5e5, 0xe5e5, 0xe5e5, 0 },
-	{ 0xe6e6, 0xe6e6, 0xe6e6, 0 },
-	{ 0xe7e7, 0xe7e7, 0xe7e7, 0 },
-	{ 0xe8e8, 0xe8e8, 0xe8e8, 0 },
-	{ 0xe9e9, 0xe9e9, 0xe9e9, 0 },
-	{ 0xeaea, 0xeaea, 0xeaea, 0 },
-	{ 0xebeb, 0xebeb, 0xebeb, 0 },
-	{ 0xecec, 0xecec, 0xecec, 0 },
-	{ 0xeded, 0xeded, 0xeded, 0 },
-	{ 0xeeee, 0xeeee, 0xeeee, 0 },
-	{ 0xefef, 0xefef, 0xefef, 0 },
-	{ 0xf0f0, 0xf0f0, 0xf0f0, 0 },
-	{ 0xf1f1, 0xf1f1, 0xf1f1, 0 },
-	{ 0xf2f2, 0xf2f2, 0xf2f2, 0 },
-	{ 0xf3f3, 0xf3f3, 0xf3f3, 0 },
-	{ 0xf4f4, 0xf4f4, 0xf4f4, 0 },
-	{ 0xf5f5, 0xf5f5, 0xf5f5, 0 },
-	{ 0xf6f6, 0xf6f6, 0xf6f6, 0 },
-	{ 0xf7f7, 0xf7f7, 0xf7f7, 0 },
-	{ 0xf8f8, 0xf8f8, 0xf8f8, 0 },
-	{ 0xf9f9, 0xf9f9, 0xf9f9, 0 },
-	{ 0xfafa, 0xfafa, 0xfafa, 0 },
-	{ 0xfbfb, 0xfbfb, 0xfbfb, 0 },
-	{ 0xfcfc, 0xfcfc, 0xfcfc, 0 },
-	{ 0xfdfd, 0xfdfd, 0xfdfd, 0 },
-	{ 0xfefe, 0xfefe, 0xfefe, 0 },
-	{ 0xffff, 0xffff, 0xffff, 0 },
-};
-
-const struct vkms_color_lut linear_eotf = {
-	.base = linear_array,
-	.lut_length = ARRAY_SIZE(linear_array),
-	.channel_value2index_ratio = 0xff00ffll
-};
-EXPORT_SYMBOL(linear_eotf);
-
 static struct drm_color_lut srgb_array[] = {
 	{ 0x0, 0x0, 0x0, 0 },
 	{ 0x13, 0x13, 0x13, 0 },
diff --git a/drivers/gpu/drm/vkms/vkms_luts.h b/drivers/gpu/drm/vkms/vkms_luts.h
index 8078ab33b83e..655e9553cd91 100644
--- a/drivers/gpu/drm/vkms/vkms_luts.h
+++ b/drivers/gpu/drm/vkms/vkms_luts.h
@@ -3,7 +3,6 @@
 #ifndef _VKMS_LUTS_H_
 #define _VKMS_LUTS_H_
 
-extern const struct vkms_color_lut linear_eotf;
 extern const struct vkms_color_lut srgb_eotf;
 extern const struct vkms_color_lut srgb_inv_eotf;
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 05/11] drm/vkms: improve the way in which we access LUT member
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (3 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 04/11] drm/vkms: remove linear_eotf Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 06/11] drm/vkms: rename struct vkms_color_lut::base to y Leandro Ribeiro
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

apply_lut_to_channel_value() indexes a struct drm_color_lut array, and
then cast to (__u16 *) and index with the channel (r -> index 0, g ->
index 1, b -> index 2). This is a bit fragile and prone to issues.

Instead, add a more robust helper lut_channel_value() such that, given a
struct drm_color_lut and a color channel, it retrives the value.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/vkms_composer.c | 30 ++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 83d217085ad0..bd539ee4c5be 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -75,6 +75,24 @@ VISIBLE_IF_KUNIT u16 lerp_u16(u16 a, u16 b, s64 t)
 }
 EXPORT_SYMBOL_IF_KUNIT(lerp_u16);
 
+static inline u16 lut_channel_value(const struct drm_color_lut *lut,
+				    enum lut_channel channel)
+{
+	switch (channel) {
+	case LUT_RED:
+		return lut->red;
+	case LUT_BLUE:
+		return lut->blue;
+	case LUT_GREEN:
+		return lut->green;
+	case LUT_RESERVED:
+		DRM_DEBUG_DRIVER("LUT_RESERVED channel should not be accessed");
+		return 0;
+	}
+	DRM_DEBUG_DRIVER("unknown LUT channel");
+	return 0;
+}
+
 VISIBLE_IF_KUNIT s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
 {
 	s64 color_channel_fp = drm_int2fixp(channel_value);
@@ -86,8 +104,8 @@ EXPORT_SYMBOL_IF_KUNIT(get_lut_index);
 VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
 						enum lut_channel channel)
 {
+	const struct drm_color_lut *lut_y_floor, *lut_y_ceil;
 	s64 lut_index = get_lut_index(lut, channel_value);
-	u16 *floor_lut_value, *ceil_lut_value;
 	u16 floor_channel_value, ceil_channel_value;
 
 	/*
@@ -96,15 +114,15 @@ VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut
 	 */
 	static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4);
 
-	floor_lut_value = (__u16 *)&lut->base[drm_fixp2int(lut_index)];
+	lut_y_floor = &lut->base[drm_fixp2int(lut_index)];
 	if (drm_fixp2int(lut_index) == (lut->lut_length - 1))
 		/* We're at the end of the LUT array, use same value for ceil and floor */
-		ceil_lut_value = floor_lut_value;
+		lut_y_ceil = lut_y_floor;
 	else
-		ceil_lut_value = (__u16 *)&lut->base[drm_fixp2int_ceil(lut_index)];
+		lut_y_ceil = &lut->base[drm_fixp2int_ceil(lut_index)];
 
-	floor_channel_value = floor_lut_value[channel];
-	ceil_channel_value = ceil_lut_value[channel];
+	floor_channel_value = lut_channel_value(lut_y_floor, channel);
+	ceil_channel_value = lut_channel_value(lut_y_ceil, channel);
 
 	return lerp_u16(floor_channel_value, ceil_channel_value,
 			lut_index & DRM_FIXED_DECIMAL_MASK);
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 06/11] drm/vkms: rename struct vkms_color_lut::base to y
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (4 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 05/11] drm/vkms: improve the way in which we access LUT member Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 07/11] drm/vkms: rename get_lut_index() to get_uniform_lut_index() Leandro Ribeiro
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

No behavior change. In the following commits we'll add x, so y better
matches that.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c |  2 +-
 drivers/gpu/drm/vkms/vkms_composer.c         | 10 +++++-----
 drivers/gpu/drm/vkms/vkms_drv.h              |  2 +-
 drivers/gpu/drm/vkms/vkms_luts.c             |  4 ++--
 4 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index 28614edb8eb1..cfcd7e8e7640 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -81,7 +81,7 @@ static const struct vkms_color_test_lerp_params color_test_lerp_cases[] = {
 };
 
 static const struct vkms_color_lut test_linear_lut = {
-	.base = test_linear_array,
+	.y = test_linear_array,
 	.lut_length = ARRAY_SIZE(test_linear_array),
 	.channel_value2index_ratio = 0xf000fll
 };
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index bd539ee4c5be..a022554e4b1a 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -114,12 +114,12 @@ VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut
 	 */
 	static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4);
 
-	lut_y_floor = &lut->base[drm_fixp2int(lut_index)];
+	lut_y_floor = &lut->y[drm_fixp2int(lut_index)];
 	if (drm_fixp2int(lut_index) == (lut->lut_length - 1))
 		/* We're at the end of the LUT array, use same value for ceil and floor */
 		lut_y_ceil = lut_y_floor;
 	else
-		lut_y_ceil = &lut->base[drm_fixp2int_ceil(lut_index)];
+		lut_y_ceil = &lut->y[drm_fixp2int_ceil(lut_index)];
 
 	floor_channel_value = lut_channel_value(lut_y_floor, channel);
 	ceil_channel_value = lut_channel_value(lut_y_ceil, channel);
@@ -132,7 +132,7 @@ EXPORT_SYMBOL_IF_KUNIT(apply_lut_to_channel_value);
 
 static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buffer *output_buffer)
 {
-	if (!crtc_state->gamma_lut.base)
+	if (!crtc_state->gamma_lut.y)
 		return;
 
 	if (!crtc_state->gamma_lut.lut_length)
@@ -641,7 +641,7 @@ void vkms_composer_worker(struct work_struct *work)
 		s64 max_lut_index_fp;
 		s64 u16_max_fp = drm_int2fixp(0xffff);
 
-		crtc_state->gamma_lut.base = (struct drm_color_lut *)crtc->state->gamma_lut->data;
+		crtc_state->gamma_lut.y = (struct drm_color_lut *)crtc->state->gamma_lut->data;
 		crtc_state->gamma_lut.lut_length =
 			crtc->state->gamma_lut->length / sizeof(struct drm_color_lut);
 		max_lut_index_fp = drm_int2fixp(crtc_state->gamma_lut.lut_length - 1);
@@ -649,7 +649,7 @@ void vkms_composer_worker(struct work_struct *work)
 									       u16_max_fp);
 
 	} else {
-		crtc_state->gamma_lut.base = NULL;
+		crtc_state->gamma_lut.y = NULL;
 	}
 
 	spin_unlock_irq(&out->composer_lock);
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 1975843abe92..55a3ea184e44 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -159,7 +159,7 @@ struct vkms_plane {
 };
 
 struct vkms_color_lut {
-	struct drm_color_lut *base;
+	struct drm_color_lut *y;
 	size_t lut_length;
 	s64 channel_value2index_ratio;
 };
diff --git a/drivers/gpu/drm/vkms/vkms_luts.c b/drivers/gpu/drm/vkms/vkms_luts.c
index d0e96df2c120..7b0c8eaf83b8 100644
--- a/drivers/gpu/drm/vkms/vkms_luts.c
+++ b/drivers/gpu/drm/vkms/vkms_luts.c
@@ -272,7 +272,7 @@ static struct drm_color_lut srgb_array[] = {
 };
 
 const struct vkms_color_lut srgb_eotf = {
-	.base = srgb_array,
+	.y = srgb_array,
 	.lut_length = ARRAY_SIZE(srgb_array),
 	.channel_value2index_ratio = 0xff00ffll
 };
@@ -538,7 +538,7 @@ static struct drm_color_lut srgb_inv_array[] = {
 };
 
 const struct vkms_color_lut srgb_inv_eotf = {
-	.base = srgb_inv_array,
+	.y = srgb_inv_array,
 	.lut_length = ARRAY_SIZE(srgb_inv_array),
 	.channel_value2index_ratio = 0xff00ffll
 };
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 07/11] drm/vkms: rename get_lut_index() to get_uniform_lut_index()
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (5 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 06/11] drm/vkms: rename struct vkms_color_lut::base to y Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 08/11] drm/vkms: add support to non-uniform LUT for internal color curves Leandro Ribeiro
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

In the next commits we'll add non-uniform LUTs, and this function will
be used exclusively by uniform LUTs. So rename it to make this clearer.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c | 28 ++++++++++----------
 drivers/gpu/drm/vkms/vkms_composer.c         |  6 ++---
 drivers/gpu/drm/vkms/vkms_composer.h         |  2 +-
 3 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index cfcd7e8e7640..bb9d84377b97 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -86,31 +86,31 @@ static const struct vkms_color_lut test_linear_lut = {
 	.channel_value2index_ratio = 0xf000fll
 };
 
-static void vkms_color_test_get_lut_index(struct kunit *test)
+static void vkms_color_test_get_uniform_lut_index(struct kunit *test)
 {
 	s64 lut_index;
 	int i;
 
-	lut_index = get_lut_index(&test_linear_lut, test_linear_array[0].red);
+	lut_index = get_uniform_lut_index(&test_linear_lut, test_linear_array[0].red);
 	KUNIT_EXPECT_EQ(test, drm_fixp2int(lut_index), 0);
 
 	for (i = 0; i < test_linear_lut.lut_length; i++) {
-		lut_index = get_lut_index(&test_linear_lut, test_linear_array[i].red);
+		lut_index = get_uniform_lut_index(&test_linear_lut, test_linear_array[i].red);
 		KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i);
 	}
 
-	KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x101)), 0x1);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x202)), 0x2);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int(get_uniform_lut_index(&srgb_eotf, 0x0)), 0x0);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0x0)), 0x0);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0x101)), 0x1);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0x202)), 0x2);
 
-	KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x101)), 0x1);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x202)), 0x2);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int(get_uniform_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_inv_eotf, 0x101)), 0x1);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_inv_eotf, 0x202)), 0x2);
 
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0xfefe)), 0xfe);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0xffff)), 0xff);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0xfefe)), 0xfe);
+	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0xffff)), 0xff);
 }
 
 static void vkms_color_test_lerp(struct kunit *test)
@@ -392,7 +392,7 @@ static void vkms_color_ctm_3x4_bt709(struct kunit *test)
 }
 
 static struct kunit_case vkms_color_test_cases[] = {
-	KUNIT_CASE(vkms_color_test_get_lut_index),
+	KUNIT_CASE(vkms_color_test_get_uniform_lut_index),
 	KUNIT_CASE(vkms_color_test_lerp),
 	KUNIT_CASE(vkms_color_test_linear),
 	KUNIT_CASE(vkms_color_srgb_inv_srgb),
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index a022554e4b1a..1a5e899e5b4a 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -93,19 +93,19 @@ static inline u16 lut_channel_value(const struct drm_color_lut *lut,
 	return 0;
 }
 
-VISIBLE_IF_KUNIT s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
+VISIBLE_IF_KUNIT s64 get_uniform_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
 {
 	s64 color_channel_fp = drm_int2fixp(channel_value);
 
 	return drm_fixp_mul(color_channel_fp, lut->channel_value2index_ratio);
 }
-EXPORT_SYMBOL_IF_KUNIT(get_lut_index);
+EXPORT_SYMBOL_IF_KUNIT(get_uniform_lut_index);
 
 VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
 						enum lut_channel channel)
 {
 	const struct drm_color_lut *lut_y_floor, *lut_y_ceil;
-	s64 lut_index = get_lut_index(lut, channel_value);
+	s64 lut_index = get_uniform_lut_index(lut, channel_value);
 	u16 floor_channel_value, ceil_channel_value;
 
 	/*
diff --git a/drivers/gpu/drm/vkms/vkms_composer.h b/drivers/gpu/drm/vkms/vkms_composer.h
index 04dd5646f672..fa003af12f85 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.h
+++ b/drivers/gpu/drm/vkms/vkms_composer.h
@@ -19,7 +19,7 @@ enum lut_channel {
 
 #if IS_ENABLED(CONFIG_KUNIT)
 u16 lerp_u16(u16 a, u16 b, s64 t);
-s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value);
+s64 get_uniform_lut_index(const struct vkms_color_lut *lut, u16 channel_value);
 u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
 			       enum lut_channel channel);
 void apply_3x4_matrix(struct pixel_argb_s32 *pixel, const struct drm_color_ctm_3x4 *matrix);
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 08/11] drm/vkms: add support to non-uniform LUT for internal color curves
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (6 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 07/11] drm/vkms: rename get_lut_index() to get_uniform_lut_index() Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 09/11] drm/vkms: test sRGB and inverse sRGB LUTs using more samples Leandro Ribeiro
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

This extends VKMS internal LUT implementation, allowing it to represent
non-uniform LUTs.

Such LUTs have the X axis. LUTs that come from userspace (gamma LUT) are
always uniform, so they don't have a X axis.

Note: vkms_color_srgb_inv_srgb() error tolerance reduced from 1/255 to
119/65535 ~= 0.46/255. Before this patch, the test compared the results
after quantizing them to 8-bit precision, while the updated test
compares directly in 16-bit. The LUT precision does not change with this
patch.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c | 20 +----
 drivers/gpu/drm/vkms/vkms_composer.c         | 85 ++++++++++++++++++--
 drivers/gpu/drm/vkms/vkms_drv.h              |  9 +++
 drivers/gpu/drm/vkms/vkms_luts.c             | 57 +++++++++++--
 4 files changed, 139 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index bb9d84377b97..571b1b579310 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -98,19 +98,6 @@ static void vkms_color_test_get_uniform_lut_index(struct kunit *test)
 		lut_index = get_uniform_lut_index(&test_linear_lut, test_linear_array[i].red);
 		KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i);
 	}
-
-	KUNIT_EXPECT_EQ(test, drm_fixp2int(get_uniform_lut_index(&srgb_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0x101)), 0x1);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0x202)), 0x2);
-
-	KUNIT_EXPECT_EQ(test, drm_fixp2int(get_uniform_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_inv_eotf, 0x101)), 0x1);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_inv_eotf, 0x202)), 0x2);
-
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0xfefe)), 0xfe);
-	KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_uniform_lut_index(&srgb_eotf, 0xffff)), 0xff);
 }
 
 static void vkms_color_test_lerp(struct kunit *test)
@@ -136,13 +123,14 @@ static void vkms_color_test_linear(struct kunit *test)
 static void vkms_color_srgb_inv_srgb(struct kunit *test)
 {
 	u16 srgb, final;
+	u16 tolerance = 119;
 
 	for (int i = 0; i < srgb_eotf.lut_length; i++) {
-		srgb = apply_lut_to_channel_value(&srgb_eotf, i * 0x101, LUT_RED);
+		srgb = apply_lut_to_channel_value(&srgb_eotf, srgb_eotf.x[i], LUT_RED);
 		final = apply_lut_to_channel_value(&srgb_inv_eotf, srgb, LUT_RED);
 
-		KUNIT_EXPECT_GE(test, final / 0x101, i - 1);
-		KUNIT_EXPECT_LE(test, final / 0x101, i + 1);
+		KUNIT_EXPECT_GE(test, final, (int)srgb_eotf.x[i] - tolerance);
+		KUNIT_EXPECT_LE(test, final, (int)srgb_eotf.x[i] + tolerance);
 	}
 }
 
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 1a5e899e5b4a..4ae1ffd31406 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -93,27 +93,81 @@ static inline u16 lut_channel_value(const struct drm_color_lut *lut,
 	return 0;
 }
 
+static u16 apply_non_uniform_lut(const struct vkms_color_lut *lut,
+				 u16 channel_value,
+				 enum lut_channel channel)
+{
+	const struct drm_color_lut *lut_y_lo, *lut_y_hi;
+	u16 y_lo, y_hi;
+	u16 x_lo, x_hi;
+	unsigned int lo, hi, mid;
+	s64 t;
+
+	/*
+	 * Handle values out of LUT domain.
+	 */
+	if (channel_value <= lut->x[0])
+		return lut_channel_value(&lut->y[0], channel);
+	if (channel_value >= lut->x[lut->lut_length - 1])
+		return lut_channel_value(&lut->y[lut->lut_length - 1], channel);
+
+	/*
+	 * Binary search to find the largest index lo such that
+	 * x[lo] <= channel_value.
+	 */
+	lo = 0;
+	hi = lut->lut_length - 1;
+	while (lo < hi) {
+		mid = lo + (hi - lo + 1) / 2;
+		if (lut->x[mid] <= channel_value)
+			lo = mid;
+		else
+			hi = mid - 1;
+	}
+	lut_y_lo = &lut->y[lo];
+
+	/*
+	 * As x[0] < channel_value < x[lut_length - 1] and
+	 * x[lo] <= channel_value, lo + 1 is a valid index.
+	 */
+	lut_y_hi = &lut->y[lo + 1];
+
+	x_lo = lut->x[lo];
+	x_hi = lut->x[lo + 1];
+	y_lo = lut_channel_value(lut_y_lo, channel);
+	y_hi = lut_channel_value(lut_y_hi, channel);
+
+	/* Avoid division by zero when two consecutive x values are equal. */
+	if (x_hi == x_lo)
+		return y_lo;
+
+	t = drm_fixp_div(drm_int2fixp(channel_value - x_lo),
+			 drm_int2fixp(x_hi - x_lo));
+
+	return lerp_u16(y_lo, y_hi, t);
+}
+
 VISIBLE_IF_KUNIT s64 get_uniform_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
 {
 	s64 color_channel_fp = drm_int2fixp(channel_value);
 
+	if (lut->x) {
+		DRM_DEBUG_DRIVER("Non-uniform LUT should not use get_uniform_lut_index()");
+		return 0;
+	}
+
 	return drm_fixp_mul(color_channel_fp, lut->channel_value2index_ratio);
 }
 EXPORT_SYMBOL_IF_KUNIT(get_uniform_lut_index);
 
-VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
-						enum lut_channel channel)
+static u16 apply_uniform_lut(const struct vkms_color_lut *lut,
+			     u16 channel_value,
+			     enum lut_channel channel)
 {
 	const struct drm_color_lut *lut_y_floor, *lut_y_ceil;
 	s64 lut_index = get_uniform_lut_index(lut, channel_value);
 	u16 floor_channel_value, ceil_channel_value;
 
-	/*
-	 * This checks if `struct drm_color_lut` has any gap added by the compiler
-	 * between the struct fields.
-	 */
-	static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4);
-
 	lut_y_floor = &lut->y[drm_fixp2int(lut_index)];
 	if (drm_fixp2int(lut_index) == (lut->lut_length - 1))
 		/* We're at the end of the LUT array, use same value for ceil and floor */
@@ -127,6 +181,21 @@ VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut
 	return lerp_u16(floor_channel_value, ceil_channel_value,
 			lut_index & DRM_FIXED_DECIMAL_MASK);
 }
+
+VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
+						enum lut_channel channel)
+{
+	/*
+	 * This checks if `struct drm_color_lut` has any gap added by the compiler
+	 * between the struct fields.
+	 */
+	static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4);
+
+	if (lut->x)
+		return apply_non_uniform_lut(lut, channel_value, channel);
+
+	return apply_uniform_lut(lut, channel_value, channel);
+}
 EXPORT_SYMBOL_IF_KUNIT(apply_lut_to_channel_value);
 
 
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 55a3ea184e44..be81844dfd22 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -158,7 +158,16 @@ struct vkms_plane {
 	struct drm_plane base;
 };
 
+/**
+ * struct vkms_color_lut - Driver specific color LUT representation
+ * @x: LUT x-values, must be non-decreasing and may be non-uniformly spaced.
+ *     Only required for non-uniform LUTs.
+ * @y: LUT y-values.
+ * @lut_length: The LUT length.
+ * @channel_value2index_ratio: helper for uniform LUTs (no x-values).
+ */
 struct vkms_color_lut {
+	u16 *x;
 	struct drm_color_lut *y;
 	size_t lut_length;
 	s64 channel_value2index_ratio;
diff --git a/drivers/gpu/drm/vkms/vkms_luts.c b/drivers/gpu/drm/vkms/vkms_luts.c
index 7b0c8eaf83b8..c2d5f9e502ae 100644
--- a/drivers/gpu/drm/vkms/vkms_luts.c
+++ b/drivers/gpu/drm/vkms/vkms_luts.c
@@ -12,7 +12,42 @@
  * https://gitlab.freedesktop.org/hwentland/lutgen
  */
 
-static struct drm_color_lut srgb_array[] = {
+static u16 linear_x[] = {
+	0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
+	0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
+	0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
+	0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f,
+	0x2020, 0x2121, 0x2222, 0x2323, 0x2424, 0x2525, 0x2626, 0x2727,
+	0x2828, 0x2929, 0x2a2a, 0x2b2b, 0x2c2c, 0x2d2d, 0x2e2e, 0x2f2f,
+	0x3030, 0x3131, 0x3232, 0x3333, 0x3434, 0x3535, 0x3636, 0x3737,
+	0x3838, 0x3939, 0x3a3a, 0x3b3b, 0x3c3c, 0x3d3d, 0x3e3e, 0x3f3f,
+	0x4040, 0x4141, 0x4242, 0x4343, 0x4444, 0x4545, 0x4646, 0x4747,
+	0x4848, 0x4949, 0x4a4a, 0x4b4b, 0x4c4c, 0x4d4d, 0x4e4e, 0x4f4f,
+	0x5050, 0x5151, 0x5252, 0x5353, 0x5454, 0x5555, 0x5656, 0x5757,
+	0x5858, 0x5959, 0x5a5a, 0x5b5b, 0x5c5c, 0x5d5d, 0x5e5e, 0x5f5f,
+	0x6060, 0x6161, 0x6262, 0x6363, 0x6464, 0x6565, 0x6666, 0x6767,
+	0x6868, 0x6969, 0x6a6a, 0x6b6b, 0x6c6c, 0x6d6d, 0x6e6e, 0x6f6f,
+	0x7070, 0x7171, 0x7272, 0x7373, 0x7474, 0x7575, 0x7676, 0x7777,
+	0x7878, 0x7979, 0x7a7a, 0x7b7b, 0x7c7c, 0x7d7d, 0x7e7e, 0x7f7f,
+	0x8080, 0x8181, 0x8282, 0x8383, 0x8484, 0x8585, 0x8686, 0x8787,
+	0x8888, 0x8989, 0x8a8a, 0x8b8b, 0x8c8c, 0x8d8d, 0x8e8e, 0x8f8f,
+	0x9090, 0x9191, 0x9292, 0x9393, 0x9494, 0x9595, 0x9696, 0x9797,
+	0x9898, 0x9999, 0x9a9a, 0x9b9b, 0x9c9c, 0x9d9d, 0x9e9e, 0x9f9f,
+	0xa0a0, 0xa1a1, 0xa2a2, 0xa3a3, 0xa4a4, 0xa5a5, 0xa6a6, 0xa7a7,
+	0xa8a8, 0xa9a9, 0xaaaa, 0xabab, 0xacac, 0xadad, 0xaeae, 0xafaf,
+	0xb0b0, 0xb1b1, 0xb2b2, 0xb3b3, 0xb4b4, 0xb5b5, 0xb6b6, 0xb7b7,
+	0xb8b8, 0xb9b9, 0xbaba, 0xbbbb, 0xbcbc, 0xbdbd, 0xbebe, 0xbfbf,
+	0xc0c0, 0xc1c1, 0xc2c2, 0xc3c3, 0xc4c4, 0xc5c5, 0xc6c6, 0xc7c7,
+	0xc8c8, 0xc9c9, 0xcaca, 0xcbcb, 0xcccc, 0xcdcd, 0xcece, 0xcfcf,
+	0xd0d0, 0xd1d1, 0xd2d2, 0xd3d3, 0xd4d4, 0xd5d5, 0xd6d6, 0xd7d7,
+	0xd8d8, 0xd9d9, 0xdada, 0xdbdb, 0xdcdc, 0xdddd, 0xdede, 0xdfdf,
+	0xe0e0, 0xe1e1, 0xe2e2, 0xe3e3, 0xe4e4, 0xe5e5, 0xe6e6, 0xe7e7,
+	0xe8e8, 0xe9e9, 0xeaea, 0xebeb, 0xecec, 0xeded, 0xeeee, 0xefef,
+	0xf0f0, 0xf1f1, 0xf2f2, 0xf3f3, 0xf4f4, 0xf5f5, 0xf6f6, 0xf7f7,
+	0xf8f8, 0xf9f9, 0xfafa, 0xfbfb, 0xfcfc, 0xfdfd, 0xfefe, 0xffff
+};
+
+static struct drm_color_lut srgb_y[] = {
 	{ 0x0, 0x0, 0x0, 0 },
 	{ 0x13, 0x13, 0x13, 0 },
 	{ 0x27, 0x27, 0x27, 0 },
@@ -271,14 +306,17 @@ static struct drm_color_lut srgb_array[] = {
 	{ 0xffff, 0xffff, 0xffff, 0 },
 };
 
+static_assert(ARRAY_SIZE(linear_x) == ARRAY_SIZE(srgb_y),
+	      "srgb x and y must have the same number of entries");
+
 const struct vkms_color_lut srgb_eotf = {
-	.y = srgb_array,
-	.lut_length = ARRAY_SIZE(srgb_array),
-	.channel_value2index_ratio = 0xff00ffll
+	.x = linear_x,
+	.y = srgb_y,
+	.lut_length = ARRAY_SIZE(srgb_y)
 };
 EXPORT_SYMBOL(srgb_eotf);
 
-static struct drm_color_lut srgb_inv_array[] = {
+static struct drm_color_lut srgb_inv_y[] = {
 	{ 0x0, 0x0, 0x0, 0 },
 	{ 0xcc2, 0xcc2, 0xcc2, 0 },
 	{ 0x15be, 0x15be, 0x15be, 0 },
@@ -537,9 +575,12 @@ static struct drm_color_lut srgb_inv_array[] = {
 	{ 0xffff, 0xffff, 0xffff, 0 },
 };
 
+static_assert(ARRAY_SIZE(linear_x) == ARRAY_SIZE(srgb_inv_y),
+	      "srgb_inv x and y must have the same number of entries");
+
 const struct vkms_color_lut srgb_inv_eotf = {
-	.y = srgb_inv_array,
-	.lut_length = ARRAY_SIZE(srgb_inv_array),
-	.channel_value2index_ratio = 0xff00ffll
+	.x = linear_x,
+	.y = srgb_inv_y,
+	.lut_length = ARRAY_SIZE(srgb_inv_y)
 };
 EXPORT_SYMBOL(srgb_inv_eotf);
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 09/11] drm/vkms: test sRGB and inverse sRGB LUTs using more samples
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (7 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 08/11] drm/vkms: add support to non-uniform LUT for internal color curves Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 10/11] drm/vkms: add script to create optimized LUTs for color curves Leandro Ribeiro
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

We're currently using srgb_eotf.lut_length samples in
vkms_color_srgb_inv_srgb(). This does not exercise any points that are
not in the forward sRGB LUT, although it exercises points that are not
in its inverse.

Let's test with more samples, to also exercise points that are not in
the forward sRGB LUT.

We chose 4096 samples. LUT inputs are u16, so we could have up to 65536
samples. LUT sizes are usually not higher than 512, so with 4096 we have
at least 8 samples between each LUT point.

We can see a minor error bump with this change. In the next commits we
replace such LUTs with non-uniform optimal ones to reduce the error.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index 571b1b579310..78f67bba33b5 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -123,14 +123,16 @@ static void vkms_color_test_linear(struct kunit *test)
 static void vkms_color_srgb_inv_srgb(struct kunit *test)
 {
 	u16 srgb, final;
-	u16 tolerance = 119;
+	u16 tolerance = 122;
+	u32 x;
 
-	for (int i = 0; i < srgb_eotf.lut_length; i++) {
-		srgb = apply_lut_to_channel_value(&srgb_eotf, srgb_eotf.x[i], LUT_RED);
+	for (u32 i = 0; i <= 4095; i++) {
+		x = i * U16_MAX / 4095;
+		srgb = apply_lut_to_channel_value(&srgb_eotf, x, LUT_RED);
 		final = apply_lut_to_channel_value(&srgb_inv_eotf, srgb, LUT_RED);
 
-		KUNIT_EXPECT_GE(test, final, (int)srgb_eotf.x[i] - tolerance);
-		KUNIT_EXPECT_LE(test, final, (int)srgb_eotf.x[i] + tolerance);
+		KUNIT_EXPECT_GE(test, final, (int)x - tolerance);
+		KUNIT_EXPECT_LE(test, final, (int)x + tolerance);
 	}
 }
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 10/11] drm/vkms: add script to create optimized LUTs for color curves
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (8 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 09/11] drm/vkms: test sRGB and inverse sRGB LUTs using more samples Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-08-04 20:33 ` [PATCH 11/11] drm/vkms: replace uniform sRGB LUT and its inverse with optimal ones Leandro Ribeiro
  2026-11-04  9:15 ` [PATCH 00/11] drm/vkms: improve color curve LUTs precision Robert Mader
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

Add a Python script to generate u16 LUTs for VKMS color transfer
functions while minimizing interpolation and quantization errors.

The script compares different LUT generation strategies and uses greedy
subdivision to generate optimized LUTs for the supported transfer
functions.

It also outputs the generated LUTs in a format ready to be added to
vkms_luts.c.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 .../gpu/drm/vkms/scripts/color-curve-lut.py   | 561 ++++++++++++++++++
 1 file changed, 561 insertions(+)
 create mode 100644 drivers/gpu/drm/vkms/scripts/color-curve-lut.py

diff --git a/drivers/gpu/drm/vkms/scripts/color-curve-lut.py b/drivers/gpu/drm/vkms/scripts/color-curve-lut.py
new file mode 100644
index 000000000000..f80e67120f5b
--- /dev/null
+++ b/drivers/gpu/drm/vkms/scripts/color-curve-lut.py
@@ -0,0 +1,561 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""
+Context
+=======
+
+VKMS uses u16 LUTs for color transfer functions. This script generates LUTs
+while minimizing interpolation and quantization error without making them
+unnecessarily large.
+
+Three LUT generation methods are compared with this script:
+
+    1. uniform: distribute points uniformly in the domain.
+    2. inverse uniform: distribute points uniformly in the range.
+    3. greedy subdivision: iteratively adds points where interpolation error is
+       highest.
+
+Since VKMS LUTs use u16 values, max_error is evaluated at all 65536 possible
+input values, comparing to the original continuous curve. For each color curve,
+the LUT size is chosen so the error is close to the u16 quantization step
+(roughly 10^-5). Greedy subdivision performed best for all transfer functions of
+interest, so this script generates the corresponding C arrays to be used by VKMS
+(see vkms_luts.c).
+
+Error evaluation
+================
+
+To evaluate the LUTs, we compare the interpolated result against the original
+continuous curve at all 65536 possible u16 input values. Sampling between u16
+values could report errors that VKMS cannot encounter, since its input is always
+a u16 value, so there is no benefit in using more samples.
+
+We initially experimented with float LUTs, where more samples were useful, and
+the results were similar: greedy subdivision outperformed the other methods.
+
+LUT size selection
+==================
+
+u16 quantization step is roughly 10^-5 in normalized space. So for each curve we
+target LUT interpolation error close to or below this (making errors negligible
+in the pipeline), and select the minimum LUT size that satisfies this target.
+
+Inverse PQ requires a much higher LUT size than the other curves to satisfy this
+error metric. This is expected, as it has a high slope near-zero.
+
+Results
+=======
+
+As stated, greedy subdivision improves the results significantly. Besides
+accounting for interpolation error, it also accounts for quantization error.
+Uniform LUTs perform well for curves similar to power-law, while inverse uniform
+LUTs perform well for curves similar to inverse power-law. Still, for u16 LUTs
+greedy subdivision algorithm outperforms both.
+
+Here are the LUT sizes and errors at which the error converged to the target
+error:
+
+Results using greedy subdivision u16 LUTs
+----------------------------------------------
+Function               LUT size   Max error
+----------------------------------------------
+pow 2.2                270        9.320331e-06
+inverse pow 2.2        370        1.056258e-05
+piece-wise sRGB        260        9.298192e-06
+inv piece-wise sRGB    330        1.001394e-05
+PQ                     380        9.159226e-06
+inverse PQ             540        9.476497e-06
+BT.2020 OETF           270        9.762646e-06
+inv BT.2020 OETF       240        9.924004e-06
+----------------------------------------------
+
+Note: inverse pow 2.2 and inverse piece-wise sRGB LUTs stopped improving with
+fewer taps than inverse PQ, but their max_error didn't drop below 10^-5, even
+with very large LUTs. This can be attributed to quantization error.
+"""
+
+import sys
+import os
+import time
+import bisect
+import math
+import numpy as np
+import matplotlib.pyplot as plt
+
+
+def u16_to_float(u16_val):
+    """Convert u16 value to normalized float [0, 1]"""
+    return u16_val / 65535.0
+
+
+def float_to_u16(float_val):
+    """Convert normalized float [0, 1] to u16"""
+    return int(np.round(np.clip(float_val * 65535.0, 0, 65535)))
+
+
+class LUT:
+    """
+    Base class for all LUT.
+    """
+    def __init__(self, name, function_pair, lut_size):
+        self.name = name
+        self.lut_size = lut_size
+        self.func = function_pair[0]
+        self.inverse_func = function_pair[1]
+        self.x = None
+        self.y = None
+        self.x_float = None  # cached float x array for interpolation
+        self.y_float = None  # cached float y array for interpolation
+        self.build_lut()
+        self.validate()
+        self.cache_float()
+
+    def validate(self):
+        """Sanity-check the generated LUT arrays"""
+        errors = []
+
+        if self.lut_size < 2:
+            errors.append(f"LUT size ({self.lut_size}) < 2")
+        if len(self.x) != self.lut_size:
+            errors.append(f"x length ({len(self.x)}) != lut_size ({self.lut_size})")
+        if len(self.y) != self.lut_size:
+            errors.append(f"y length ({len(self.y)}) != lut_size ({self.lut_size})")
+
+        # All curves start with (0, 0) and end with (1, 1)
+        if self.x[0] != 0:
+            errors.append(f"x[0] = {self.x[0]}, expected 0")
+        if self.x[-1] != 65535:
+            errors.append(f"x[-1] = {self.x[-1]}, expected 65535")
+        if self.y[0] != 0:
+            errors.append(f"y[0] = {self.y[0]}, expected 0")
+        if self.y[-1] != 65535:
+            errors.append(f"y[-1] = {self.y[-1]}, expected 65535")
+
+        # X must be strictly increasing (no repeated points)
+        for i in range(1, self.lut_size):
+            if self.x[i] <= self.x[i - 1]:
+                errors.append(f"x not strictly increasing at index {i}: "
+                              f"x[{i-1}]={self.x[i-1]}, x[{i}]={self.x[i]}")
+                break
+
+        # Y must be non-decreasing (all supported TFs are increasing on [0,1],
+        # so the quantized output should never decrease)
+        for i in range(1, self.lut_size):
+            if self.y[i] < self.y[i - 1]:
+                errors.append(f"y decreases between index {i-1} and {i}: "
+                              f"y[{i-1}]={self.y[i-1]}, y[{i}]={self.y[i]}")
+                break
+
+        if errors:
+            print(f"\n[{self.name}] VALIDATION FAILED:")
+            for e in errors:
+                print(f"  - {e}")
+            sys.exit(1)
+
+    def cache_float(self):
+        """Cache u16 x and y arrays as float for interpolation"""
+        self.x_float = self.x / 65535.0
+        self.y_float = self.y / 65535.0
+
+    @staticmethod
+    def lut_interp(x, x_array, y_array):
+        """
+        Lookup with linear interpolation using binary search.
+        All inputs and output are floats in [0, 1].
+        """
+        if x <= x_array[0]:
+            return y_array[0]
+        if x >= x_array[-1]:
+            return y_array[-1]
+
+        # Use bisect for Python lists, np.searchsorted for numpy arrays. This
+        # avoids type conversions that are extremely slow.
+        if isinstance(x_array, list):
+            i1 = bisect.bisect_left(x_array, x)
+        else:
+            i1 = np.searchsorted(x_array, x)
+
+        i0 = i1 - 1
+
+        x0, y0 = x_array[i0], y_array[i0]
+        x1, y1 = x_array[i1], y_array[i1]
+
+        return y0 + (x - x0) * (y1 - y0) / (x1 - x0)
+
+    def build_lut(self):
+        raise NotImplementedError
+
+    def compute_error(self, num_samples=65536):
+        """
+        Compute average absolute error and max absolute error for this LUT.
+        Samples all 65536 possible u16 input values, interpolates using cached
+        float arrays, and compares to the continuous function.
+        """
+        average_abs_error = 0
+        max_abs_error = 0
+        max_abs_error_x = 0
+        for i in range(num_samples):
+            x = i / (num_samples - 1)
+            lut_res = self.lut_interp(x, self.x_float, self.y_float)
+            real_res = self.func(x)
+            error = abs(real_res - lut_res)
+            average_abs_error += error
+            if error > max_abs_error:
+                max_abs_error = error
+                max_abs_error_x = x
+        average_abs_error = average_abs_error / num_samples
+
+        # Check if max_error occurs between two adjacent u16 x
+        i1 = np.searchsorted(self.x_float, max_abs_error_x)
+        i0 = i1 - 1
+        if i0 >= 0 and i1 < self.lut_size:
+            x0_u16 = self.x[i0]
+            x1_u16 = self.x[i1]
+            if x1_u16 - x0_u16 == 1:
+                indent = " " * (len(self.name) + 3)
+                print(f"[{self.name}] Warning: max_error at x={max_abs_error_x:.6e}\n"
+                      f"{indent}is between adjacent u16 x points ({x0_u16}, {x1_u16}).\n"
+                      f"{indent}Increasing LUT size probably won't help.")
+
+        return average_abs_error, max_abs_error
+
+
+class LUT_uniform(LUT):
+    """
+    Regular uniform LUT, equidistant points in the domain
+    """
+    def __init__(self, function_pair, lut_size):
+        super().__init__("Uniform", function_pair, lut_size)
+
+    def build_lut(self):
+        x_list = []
+        y_list = []
+        for i in range(self.lut_size):
+            # Generate x uniformly in u16 space [0, 65535]
+            x_u16 = min(65535, round(i * 65535 / (self.lut_size - 1)))
+            x_float = u16_to_float(x_u16)
+            y_float = self.func(x_float)
+            x_list.append(x_u16)
+            y_list.append(float_to_u16(y_float))
+
+        self.x = np.array(x_list, dtype=np.uint16)
+        self.y = np.array(y_list, dtype=np.uint16)
+
+
+class LUT_inverse_uniform(LUT):
+    """
+    Inverse uniform LUT: uniformly spaces y values and computes x = f_inv(y).
+    Similar to inverting x and y arrays from regular LUT. This naturally places
+    more points in regions where the function has high slope (e.g. near zero for
+    inverse power-law curves).
+    """
+    def __init__(self, function_pair, lut_size):
+        super().__init__("Inverse uniform", function_pair, lut_size)
+
+    def build_lut(self):
+        # Uniformly space points in the range, compute x = inverse_func(y)
+        x_list = []
+        y_list = []
+        for i in range(self.lut_size):
+            y_u16 = min(65535, round(i * 65535 / (self.lut_size - 1)))
+            x_u16 = float_to_u16(self.inverse_func(u16_to_float(y_u16)))
+
+            # Ignore duplicates.
+            if x_list and x_u16 == x_list[-1]:
+                continue
+
+            x_list.append(x_u16)
+            # As x_u16 may contain quantization error (it is the result of
+            # computing inverse_func(y) but rounded to closest quantization
+            # step), let's recompute y_u16 based on x_u16 using func(). This
+            # creates a more precise LUT.
+            y_list.append(float_to_u16(self.func(u16_to_float(x_u16))))
+
+        self.x = np.array(x_list, dtype=np.uint16)
+        self.y = np.array(y_list, dtype=np.uint16)
+
+        # Update size, as we may have ignored duplicates
+        self.lut_size = len(x_list)
+
+
+class LUT_greedy_subdivision(LUT):
+    """
+    This greedily looks for domain points in which the interpolation error is
+    maximal, and keeps adding points in such regions to reduce the LUT error.
+
+    The code here only samples the midpoint of each segment to compute error,
+    not the true maximum error point in the segment. This is an approximation,
+    and one would have to try several points in the segments to have even more
+    accurate results. But the midpoint heuristic is fast and accurate enough.
+
+    Note: the optimal solution (optimal in terms of minimizing interpolation
+    error) for the approximation problem would be using the method of globally
+    optimal knot placement, but that's much more complex. This one is good
+    enough.
+    """
+    def __init__(self, function_pair, lut_size):
+        super().__init__("Greedy subdivision", function_pair, lut_size)
+
+    def build_lut(self):
+        # Use Python lists during construction, as it's much faster to
+        # dynamically change their size.
+        x_list = [0, 65535]
+        y_list = [float_to_u16(self.func(0.0)), float_to_u16(self.func(1.0))]
+
+        # Maintain float lists for interpolation during construction
+        x_list_float = [0.0, 1.0]
+        y_list_float = [u16_to_float(y_list[0]), u16_to_float(y_list[1])]
+
+        # Add points based on max error
+        while len(x_list) < self.lut_size:
+            max_error = 0.0
+            width_cur = 0.0
+            index = 0
+            best_x_mid_u16 = None
+
+            # Find segment with max error
+            for i in range(len(x_list) - 1):
+                x0_u16 = x_list[i]
+                x1_u16 = x_list[i + 1]
+                segment_width_u16 = x1_u16 - x0_u16
+
+                # Skip segments in which we can't add more points in between
+                if segment_width_u16 < 2:
+                    continue
+
+                # Calculate midpoint in u16 space
+                x_mid_u16 = (x0_u16 + x1_u16) // 2
+                x_mid_float = u16_to_float(x_mid_u16)
+
+                # Compute error at midpoint
+                y_sample = self.func(x_mid_float)
+                y_interp = self.lut_interp(x_mid_float, x_list_float, y_list_float)
+                segment_max_error = abs(y_sample - y_interp)
+
+                # Prefer biggest segment to tie-break error == max_error. This
+                # avoids issues with e.g. linear segments. In these segments,
+                # the error is always zero, so we need to add points evenly
+                # spaced.
+                if (segment_max_error > max_error or
+                    (math.isclose(segment_max_error, max_error, abs_tol=1e-6) and
+                     segment_width_u16 > width_cur)):
+                    max_error = segment_max_error
+                    index = i
+                    width_cur = segment_width_u16
+                    best_x_mid_u16 = x_mid_u16
+
+            # If no valid segment found, stop. Also update size as we may have
+            # converged without the LUT size given by end user.
+            if best_x_mid_u16 is None:
+                self.lut_size = len(x_list)
+                break
+
+            x_mid_float = u16_to_float(best_x_mid_u16)
+            y_mid_float = self.func(x_mid_float)
+            y_mid_u16 = float_to_u16(y_mid_float)
+
+            x_list.insert(index + 1, best_x_mid_u16)
+            y_list.insert(index + 1, y_mid_u16)
+            x_list_float.insert(index + 1, x_mid_float)
+            y_list_float.insert(index + 1, u16_to_float(y_mid_u16))
+
+        self.x = np.array(x_list, dtype=np.uint16)
+        self.y = np.array(y_list, dtype=np.uint16)
+
+
+class Functions:
+    """
+    Functions and their inverse
+    """
+    @staticmethod
+    def pow22(x):
+        return pow(x, 2.2)
+
+    @staticmethod
+    def inv_pow22(x):
+        return pow(x, 1.0 / 2.2)
+
+    @staticmethod
+    def srgb(x):
+        if x <= 0.04045:
+            return x / 12.92
+        return pow(((x + 0.055) / 1.055), 2.4)
+
+    @staticmethod
+    def inv_srgb(x):
+        if x <= 0.0031308:
+            return 12.92 * x
+        return 1.055 * pow(x, (1.0 / 2.4)) - 0.055
+
+    @staticmethod
+    def pq(x):
+        m1_inv = 1.0 / 0.1593017578125
+        m2_inv = 1.0 / 78.84375
+        c1 = 0.8359375
+        c2 = 18.8515625
+        c3 = 18.6875
+        aux = pow(x, m2_inv)
+        return pow(max(aux - c1, 0.0) / (c2 - c3 * aux), m1_inv)
+
+    @staticmethod
+    def inv_pq(x):
+        m1 = 0.1593017578125
+        m2 = 78.84375
+        c1 = 0.8359375
+        c2 = 18.8515625
+        c3 = 18.6875
+        aux = pow(x, m1)
+        return pow((c1 + c2 * aux) / (1.0 + c3 * aux), m2)
+
+    @staticmethod
+    def bt2020_oetf(x):
+        a = 1.0993
+        if x < 0.018:
+            return 4.5 * x
+        return a * (pow(x, 0.45)) - (a - 1.0)
+
+    @staticmethod
+    def inv_bt2020_oetf(x):
+        a = 1.0993
+        if x < 0.081:
+            return x / 4.5
+        k = (x + a - 1.0) / a
+        return pow(k, 1.0 / 0.45)
+
+
+def emit_c_arrays(lut, func_str):
+    """
+    Write X and Y arrays in C format compatible with vkms_luts.c to a file.
+    """
+    name = func_str
+
+    output_file = f"{func_str}{lut.lut_size}.txt"
+    if os.path.isfile(output_file):
+        print(f"  File {output_file} already exists, not overriding it")
+        return
+
+    lines = []
+
+    # X array: format eight values per line.
+    lines.append(f"static u16 {name}_x[] = {{")
+    for i in range(0, lut.lut_size, 8):
+        chunk = lut.x[i:i+8]
+        vals = ", ".join(f"0x{int(v):04x}" for v in chunk)
+        comma = "," if i + 8 < lut.lut_size else ""
+        lines.append(f"\t{vals}{comma}")
+    lines.append("};")
+    lines.append("")
+
+    # Y array: one struct drm_color_lut entry per line: { R = val, G = val, B = val, reserved = 0 }
+    lines.append(f"static struct drm_color_lut {name}_y[] = {{")
+    for i, y_val in enumerate(lut.y):
+        v = int(y_val)
+        comma = "," if i < lut.lut_size - 1 else ""
+        lines.append(f"\t{{ 0x{v:04x}, 0x{v:04x}, 0x{v:04x}, 0 }}{comma}")
+    lines.append("};")
+
+    output = "\n".join(lines) + "\n"
+
+    with open(output_file, 'w') as f:
+        f.write(output)
+    print(f"  C arrays written to {output_file}")
+
+
+def print_help_and_quit(function_table):
+    """
+    Print usage message and quit
+    """
+    print("Usage: python script.py <function> <LUT_size>")
+    print("LUT size in range [2, 4096]")
+    print("LUTs based on greedy subdivision algorithm are printed to file <function><LUT_size>.txt")
+    print("Possible function names are:")
+    for func in function_table.keys():
+        print(f"  {func}")
+        print(f"  inv_{func}")
+    print("  -h, --help    Show this help message and exit")
+    sys.exit(1)
+
+
+def parse_cli_params(args, function_table):
+    """
+    Parse cli params from users
+    """
+    if len(args) != 3:
+        print_help_and_quit(function_table)
+
+    func_str = args[1]
+    use_inverse = func_str.startswith("inv_")
+    base_func_str = func_str[4:] if use_inverse else func_str
+
+    func_pair = function_table.get(base_func_str)
+    if func_pair is None:
+        print(f"Error: unknown function '{func_str}'\n")
+        print_help_and_quit(function_table)
+
+    if use_inverse:
+        func_pair = (func_pair[1], func_pair[0])
+
+    lut_size_str = args[2]
+    try:
+        lut_size = int(lut_size_str)
+        if lut_size < 2 or lut_size > 4096:
+            print(f"Error: LUT size '{lut_size}' not in valid range\n")
+            print_help_and_quit(function_table)
+    except ValueError:
+        print(f"Error: '{lut_size_str}' is not a valid LUT size\n")
+        print_help_and_quit(function_table)
+
+    return func_str, func_pair, lut_size
+
+
+def main(args):
+    function_table = {
+        "srgb": (Functions.srgb, Functions.inv_srgb),
+        "pow22": (Functions.pow22, Functions.inv_pow22),
+        "pq": (Functions.pq, Functions.inv_pq),
+        "bt2020_oetf": (Functions.bt2020_oetf, Functions.inv_bt2020_oetf),
+    }
+
+    func_str, func_pair, lut_size = parse_cli_params(args, function_table)
+
+    start_time = time.perf_counter()
+
+    luts = [LUT_uniform(func_pair, lut_size),
+            LUT_inverse_uniform(func_pair, lut_size),
+            LUT_greedy_subdivision(func_pair, lut_size)]
+
+    luts_error = [lut.compute_error() for lut in luts]
+
+    print(f"\nFunction: {func_str}, LUT size (specified by user): {lut_size}\n")
+    print(f"{'LUT Type':<41} {'Average Error':>15} {'Max Error':>11}")
+    print("-" * 72)
+
+    for i, lut in enumerate(luts):
+        avg_error, max_error = luts_error[i]
+        label = f"{lut.name:<20} (actual size: {lut.lut_size})"
+        print(f"{label:<40} {avg_error:>15.6e} {max_error:>15.6e}")
+
+    end_time = time.perf_counter()
+    print(f"\nTime to compute LUTs and errors: {end_time - start_time:.4f} seconds")
+
+    # Save the greedy subdivision LUT as C arrays for VKMS
+    greedy_lut = luts[2]
+    emit_c_arrays(greedy_lut, func_str)
+
+    # Plot the LUTs... kind of useless when we have a big LUT, but still good to
+    # visualize with fewer taps.
+    fig, axes = plt.subplots(1, len(luts), figsize=(15, 6))
+    for i, lut in enumerate(luts):
+        ax = axes[i]
+        ax.set_title(f"{lut.name} - {func_str}, LUT size: {lut.lut_size}")
+        ax.set_xlabel('X')
+        ax.set_ylabel('Y')
+        ax.grid(True, linestyle='--')
+        ax.plot(lut.x, lut.y, 'o', color="blue")
+        ax.set_aspect('equal')
+    plt.tight_layout()
+    plt.show()
+
+
+if __name__ == "__main__":
+    main(sys.argv)
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 11/11] drm/vkms: replace uniform sRGB LUT and its inverse with optimal ones
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (9 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 10/11] drm/vkms: add script to create optimized LUTs for color curves Leandro Ribeiro
@ 2026-08-04 20:33 ` Leandro Ribeiro
  2026-11-04  9:15 ` [PATCH 00/11] drm/vkms: improve color curve LUTs precision Robert Mader
  11 siblings, 0 replies; 13+ messages in thread
From: Leandro Ribeiro @ 2026-08-04 20:33 UTC (permalink / raw)
  To: louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen,
	robert.mader, simona, tzimmermann, dri-devel, linux-kernel

This uses the script added in "drm/vkms: add script to create optimized
LUTs for color curves" to produce optimized LUTs for sRGB and its
inverse.

The LUTs are non-uniform and have more taps where it matters (areas with
high slope), decreasing the error they produce.
vkms_color_srgb_inv_srgb() error tolerance dropped from 122/65535 to
11/65535.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
---
 drivers/gpu/drm/vkms/tests/vkms_color_test.c |    2 +-
 drivers/gpu/drm/vkms/vkms_luts.c             | 1228 ++++++++++--------
 2 files changed, 676 insertions(+), 554 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
index 78f67bba33b5..0b3a652d311b 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -123,7 +123,7 @@ static void vkms_color_test_linear(struct kunit *test)
 static void vkms_color_srgb_inv_srgb(struct kunit *test)
 {
 	u16 srgb, final;
-	u16 tolerance = 122;
+	u16 tolerance = 11;
 	u32 x;
 
 	for (u32 i = 0; i <= 4095; i++) {
diff --git a/drivers/gpu/drm/vkms/vkms_luts.c b/drivers/gpu/drm/vkms/vkms_luts.c
index c2d5f9e502ae..4b766ae7c125 100644
--- a/drivers/gpu/drm/vkms/vkms_luts.c
+++ b/drivers/gpu/drm/vkms/vkms_luts.c
@@ -6,580 +6,702 @@
 #include "vkms_luts.h"
 
 /*
- * These luts were generated with a LUT generated based on
- * skia's transfer function code. The LUT generator can be
- * found at
- * https://gitlab.freedesktop.org/hwentland/lutgen
+ * These LUTs were generated by scripts/color-curve-lut.py using greedy
+ * subdivision to minimize interpolation error.
  */
 
-static u16 linear_x[] = {
-	0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
-	0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
-	0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
-	0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f,
-	0x2020, 0x2121, 0x2222, 0x2323, 0x2424, 0x2525, 0x2626, 0x2727,
-	0x2828, 0x2929, 0x2a2a, 0x2b2b, 0x2c2c, 0x2d2d, 0x2e2e, 0x2f2f,
-	0x3030, 0x3131, 0x3232, 0x3333, 0x3434, 0x3535, 0x3636, 0x3737,
-	0x3838, 0x3939, 0x3a3a, 0x3b3b, 0x3c3c, 0x3d3d, 0x3e3e, 0x3f3f,
-	0x4040, 0x4141, 0x4242, 0x4343, 0x4444, 0x4545, 0x4646, 0x4747,
-	0x4848, 0x4949, 0x4a4a, 0x4b4b, 0x4c4c, 0x4d4d, 0x4e4e, 0x4f4f,
-	0x5050, 0x5151, 0x5252, 0x5353, 0x5454, 0x5555, 0x5656, 0x5757,
-	0x5858, 0x5959, 0x5a5a, 0x5b5b, 0x5c5c, 0x5d5d, 0x5e5e, 0x5f5f,
-	0x6060, 0x6161, 0x6262, 0x6363, 0x6464, 0x6565, 0x6666, 0x6767,
-	0x6868, 0x6969, 0x6a6a, 0x6b6b, 0x6c6c, 0x6d6d, 0x6e6e, 0x6f6f,
-	0x7070, 0x7171, 0x7272, 0x7373, 0x7474, 0x7575, 0x7676, 0x7777,
-	0x7878, 0x7979, 0x7a7a, 0x7b7b, 0x7c7c, 0x7d7d, 0x7e7e, 0x7f7f,
-	0x8080, 0x8181, 0x8282, 0x8383, 0x8484, 0x8585, 0x8686, 0x8787,
-	0x8888, 0x8989, 0x8a8a, 0x8b8b, 0x8c8c, 0x8d8d, 0x8e8e, 0x8f8f,
-	0x9090, 0x9191, 0x9292, 0x9393, 0x9494, 0x9595, 0x9696, 0x9797,
-	0x9898, 0x9999, 0x9a9a, 0x9b9b, 0x9c9c, 0x9d9d, 0x9e9e, 0x9f9f,
-	0xa0a0, 0xa1a1, 0xa2a2, 0xa3a3, 0xa4a4, 0xa5a5, 0xa6a6, 0xa7a7,
-	0xa8a8, 0xa9a9, 0xaaaa, 0xabab, 0xacac, 0xadad, 0xaeae, 0xafaf,
-	0xb0b0, 0xb1b1, 0xb2b2, 0xb3b3, 0xb4b4, 0xb5b5, 0xb6b6, 0xb7b7,
-	0xb8b8, 0xb9b9, 0xbaba, 0xbbbb, 0xbcbc, 0xbdbd, 0xbebe, 0xbfbf,
-	0xc0c0, 0xc1c1, 0xc2c2, 0xc3c3, 0xc4c4, 0xc5c5, 0xc6c6, 0xc7c7,
-	0xc8c8, 0xc9c9, 0xcaca, 0xcbcb, 0xcccc, 0xcdcd, 0xcece, 0xcfcf,
-	0xd0d0, 0xd1d1, 0xd2d2, 0xd3d3, 0xd4d4, 0xd5d5, 0xd6d6, 0xd7d7,
-	0xd8d8, 0xd9d9, 0xdada, 0xdbdb, 0xdcdc, 0xdddd, 0xdede, 0xdfdf,
-	0xe0e0, 0xe1e1, 0xe2e2, 0xe3e3, 0xe4e4, 0xe5e5, 0xe6e6, 0xe7e7,
-	0xe8e8, 0xe9e9, 0xeaea, 0xebeb, 0xecec, 0xeded, 0xeeee, 0xefef,
-	0xf0f0, 0xf1f1, 0xf2f2, 0xf3f3, 0xf4f4, 0xf5f5, 0xf6f6, 0xf7f7,
-	0xf8f8, 0xf9f9, 0xfafa, 0xfbfb, 0xfcfc, 0xfdfd, 0xfefe, 0xffff
+static u16 srgb_x[] = {
+	0x0000, 0x07ff, 0x09ff, 0x0aff, 0x0bff, 0x0dff, 0x0fff, 0x10ff,
+	0x11ff, 0x12ff, 0x13ff, 0x14ff, 0x15ff, 0x16ff, 0x17ff, 0x18ff,
+	0x19ff, 0x1aff, 0x1bff, 0x1dff, 0x1fff, 0x21ff, 0x23ff, 0x25ff,
+	0x26ff, 0x27ff, 0x28ff, 0x29ff, 0x2aff, 0x2bff, 0x2cff, 0x2dff,
+	0x2eff, 0x2fff, 0x30ff, 0x31ff, 0x32ff, 0x33ff, 0x34ff, 0x35ff,
+	0x36ff, 0x37ff, 0x38ff, 0x39ff, 0x3aff, 0x3bff, 0x3c7f, 0x3cff,
+	0x3d7f, 0x3dff, 0x3eff, 0x3fff, 0x40ff, 0x41ff, 0x427f, 0x42ff,
+	0x437f, 0x43ff, 0x44ff, 0x45ff, 0x46ff, 0x47ff, 0x48ff, 0x49ff,
+	0x4aff, 0x4bff, 0x4cff, 0x4dff, 0x4eff, 0x4fff, 0x50ff, 0x51ff,
+	0x52ff, 0x53ff, 0x54ff, 0x55ff, 0x56ff, 0x57ff, 0x58ff, 0x59ff,
+	0x5aff, 0x5bff, 0x5cff, 0x5dff, 0x5eff, 0x5fff, 0x607f, 0x60ff,
+	0x61ff, 0x62ff, 0x637f, 0x63ff, 0x64ff, 0x65ff, 0x66ff, 0x67ff,
+	0x68ff, 0x69ff, 0x6aff, 0x6bff, 0x6cff, 0x6dff, 0x6eff, 0x6fff,
+	0x70ff, 0x71ff, 0x72ff, 0x73ff, 0x74ff, 0x75ff, 0x76ff, 0x77ff,
+	0x78ff, 0x79ff, 0x7aff, 0x7bff, 0x7cff, 0x7dff, 0x7eff, 0x7fff,
+	0x80ff, 0x81ff, 0x82ff, 0x83ff, 0x84ff, 0x85ff, 0x86ff, 0x87ff,
+	0x887f, 0x88ff, 0x89ff, 0x8aff, 0x8bff, 0x8cff, 0x8d7f, 0x8dff,
+	0x8eff, 0x8fff, 0x90ff, 0x91ff, 0x92ff, 0x93ff, 0x94ff, 0x95ff,
+	0x96ff, 0x97ff, 0x987f, 0x98ff, 0x99ff, 0x9aff, 0x9bff, 0x9cff,
+	0x9dff, 0x9eff, 0x9fff, 0xa0ff, 0xa1ff, 0xa2ff, 0xa3ff, 0xa4ff,
+	0xa5ff, 0xa6ff, 0xa7ff, 0xa8ff, 0xa9ff, 0xaaff, 0xabff, 0xacff,
+	0xadff, 0xaeff, 0xafff, 0xb0ff, 0xb1ff, 0xb2ff, 0xb3ff, 0xb4ff,
+	0xb5ff, 0xb6ff, 0xb7ff, 0xb87f, 0xb8ff, 0xb97f, 0xb9ff, 0xbaff,
+	0xbbff, 0xbcff, 0xbdff, 0xbeff, 0xbfff, 0xc0ff, 0xc1ff, 0xc2ff,
+	0xc3ff, 0xc4ff, 0xc5ff, 0xc6ff, 0xc7ff, 0xc87f, 0xc8ff, 0xc9ff,
+	0xcaff, 0xcbff, 0xccff, 0xcdff, 0xceff, 0xcfff, 0xd0ff, 0xd1ff,
+	0xd2ff, 0xd3ff, 0xd4ff, 0xd5ff, 0xd67f, 0xd6ff, 0xd7ff, 0xd8ff,
+	0xd9ff, 0xdaff, 0xdbff, 0xdcff, 0xddff, 0xdeff, 0xdfff, 0xe0ff,
+	0xe1ff, 0xe2ff, 0xe3ff, 0xe4ff, 0xe5ff, 0xe6ff, 0xe7ff, 0xe8ff,
+	0xe9ff, 0xeaff, 0xebff, 0xecff, 0xedff, 0xeeff, 0xefff, 0xf0ff,
+	0xf17f, 0xf1ff, 0xf27f, 0xf2ff, 0xf37f, 0xf3ff, 0xf47f, 0xf4ff,
+	0xf5ff, 0xf6ff, 0xf7ff, 0xf8ff, 0xf9ff, 0xfaff, 0xfb7f, 0xfbff,
+	0xfcff, 0xfdff, 0xfeff, 0xffff
 };
 
 static struct drm_color_lut srgb_y[] = {
-	{ 0x0, 0x0, 0x0, 0 },
-	{ 0x13, 0x13, 0x13, 0 },
-	{ 0x27, 0x27, 0x27, 0 },
-	{ 0x3b, 0x3b, 0x3b, 0 },
-	{ 0x4f, 0x4f, 0x4f, 0 },
-	{ 0x63, 0x63, 0x63, 0 },
-	{ 0x77, 0x77, 0x77, 0 },
-	{ 0x8b, 0x8b, 0x8b, 0 },
-	{ 0x9f, 0x9f, 0x9f, 0 },
-	{ 0xb3, 0xb3, 0xb3, 0 },
-	{ 0xc6, 0xc6, 0xc6, 0 },
-	{ 0xdb, 0xdb, 0xdb, 0 },
-	{ 0xf0, 0xf0, 0xf0, 0 },
-	{ 0x107, 0x107, 0x107, 0 },
-	{ 0x11f, 0x11f, 0x11f, 0 },
-	{ 0x139, 0x139, 0x139, 0 },
-	{ 0x153, 0x153, 0x153, 0 },
-	{ 0x16f, 0x16f, 0x16f, 0 },
-	{ 0x18c, 0x18c, 0x18c, 0 },
-	{ 0x1aa, 0x1aa, 0x1aa, 0 },
-	{ 0x1ca, 0x1ca, 0x1ca, 0 },
-	{ 0x1eb, 0x1eb, 0x1eb, 0 },
-	{ 0x20d, 0x20d, 0x20d, 0 },
-	{ 0x231, 0x231, 0x231, 0 },
-	{ 0x256, 0x256, 0x256, 0 },
-	{ 0x27d, 0x27d, 0x27d, 0 },
-	{ 0x2a4, 0x2a4, 0x2a4, 0 },
-	{ 0x2ce, 0x2ce, 0x2ce, 0 },
-	{ 0x2f9, 0x2f9, 0x2f9, 0 },
-	{ 0x325, 0x325, 0x325, 0 },
-	{ 0x352, 0x352, 0x352, 0 },
-	{ 0x381, 0x381, 0x381, 0 },
-	{ 0x3b2, 0x3b2, 0x3b2, 0 },
-	{ 0x3e4, 0x3e4, 0x3e4, 0 },
-	{ 0x418, 0x418, 0x418, 0 },
-	{ 0x44d, 0x44d, 0x44d, 0 },
-	{ 0x484, 0x484, 0x484, 0 },
-	{ 0x4bc, 0x4bc, 0x4bc, 0 },
-	{ 0x4f6, 0x4f6, 0x4f6, 0 },
-	{ 0x531, 0x531, 0x531, 0 },
-	{ 0x56e, 0x56e, 0x56e, 0 },
-	{ 0x5ad, 0x5ad, 0x5ad, 0 },
-	{ 0x5ed, 0x5ed, 0x5ed, 0 },
-	{ 0x62f, 0x62f, 0x62f, 0 },
-	{ 0x672, 0x672, 0x672, 0 },
-	{ 0x6b7, 0x6b7, 0x6b7, 0 },
-	{ 0x6fe, 0x6fe, 0x6fe, 0 },
-	{ 0x746, 0x746, 0x746, 0 },
-	{ 0x791, 0x791, 0x791, 0 },
-	{ 0x7dc, 0x7dc, 0x7dc, 0 },
-	{ 0x82a, 0x82a, 0x82a, 0 },
-	{ 0x879, 0x879, 0x879, 0 },
-	{ 0x8ca, 0x8ca, 0x8ca, 0 },
-	{ 0x91d, 0x91d, 0x91d, 0 },
-	{ 0x971, 0x971, 0x971, 0 },
-	{ 0x9c7, 0x9c7, 0x9c7, 0 },
-	{ 0xa1f, 0xa1f, 0xa1f, 0 },
-	{ 0xa79, 0xa79, 0xa79, 0 },
-	{ 0xad4, 0xad4, 0xad4, 0 },
-	{ 0xb32, 0xb32, 0xb32, 0 },
-	{ 0xb91, 0xb91, 0xb91, 0 },
-	{ 0xbf2, 0xbf2, 0xbf2, 0 },
-	{ 0xc54, 0xc54, 0xc54, 0 },
-	{ 0xcb9, 0xcb9, 0xcb9, 0 },
-	{ 0xd1f, 0xd1f, 0xd1f, 0 },
-	{ 0xd88, 0xd88, 0xd88, 0 },
-	{ 0xdf2, 0xdf2, 0xdf2, 0 },
-	{ 0xe5e, 0xe5e, 0xe5e, 0 },
-	{ 0xecc, 0xecc, 0xecc, 0 },
-	{ 0xf3c, 0xf3c, 0xf3c, 0 },
-	{ 0xfad, 0xfad, 0xfad, 0 },
-	{ 0x1021, 0x1021, 0x1021, 0 },
-	{ 0x1096, 0x1096, 0x1096, 0 },
-	{ 0x110e, 0x110e, 0x110e, 0 },
-	{ 0x1187, 0x1187, 0x1187, 0 },
-	{ 0x1203, 0x1203, 0x1203, 0 },
-	{ 0x1280, 0x1280, 0x1280, 0 },
-	{ 0x12ff, 0x12ff, 0x12ff, 0 },
-	{ 0x1380, 0x1380, 0x1380, 0 },
-	{ 0x1404, 0x1404, 0x1404, 0 },
-	{ 0x1489, 0x1489, 0x1489, 0 },
-	{ 0x1510, 0x1510, 0x1510, 0 },
-	{ 0x1599, 0x1599, 0x1599, 0 },
-	{ 0x1624, 0x1624, 0x1624, 0 },
-	{ 0x16b2, 0x16b2, 0x16b2, 0 },
-	{ 0x1741, 0x1741, 0x1741, 0 },
-	{ 0x17d2, 0x17d2, 0x17d2, 0 },
-	{ 0x1865, 0x1865, 0x1865, 0 },
-	{ 0x18fb, 0x18fb, 0x18fb, 0 },
-	{ 0x1992, 0x1992, 0x1992, 0 },
-	{ 0x1a2c, 0x1a2c, 0x1a2c, 0 },
-	{ 0x1ac8, 0x1ac8, 0x1ac8, 0 },
-	{ 0x1b65, 0x1b65, 0x1b65, 0 },
-	{ 0x1c05, 0x1c05, 0x1c05, 0 },
-	{ 0x1ca7, 0x1ca7, 0x1ca7, 0 },
-	{ 0x1d4b, 0x1d4b, 0x1d4b, 0 },
-	{ 0x1df1, 0x1df1, 0x1df1, 0 },
-	{ 0x1e99, 0x1e99, 0x1e99, 0 },
-	{ 0x1f44, 0x1f44, 0x1f44, 0 },
-	{ 0x1ff0, 0x1ff0, 0x1ff0, 0 },
-	{ 0x209f, 0x209f, 0x209f, 0 },
-	{ 0x2150, 0x2150, 0x2150, 0 },
-	{ 0x2203, 0x2203, 0x2203, 0 },
-	{ 0x22b8, 0x22b8, 0x22b8, 0 },
-	{ 0x2370, 0x2370, 0x2370, 0 },
-	{ 0x2429, 0x2429, 0x2429, 0 },
-	{ 0x24e5, 0x24e5, 0x24e5, 0 },
-	{ 0x25a3, 0x25a3, 0x25a3, 0 },
-	{ 0x2663, 0x2663, 0x2663, 0 },
-	{ 0x2726, 0x2726, 0x2726, 0 },
-	{ 0x27ea, 0x27ea, 0x27ea, 0 },
-	{ 0x28b1, 0x28b1, 0x28b1, 0 },
-	{ 0x297a, 0x297a, 0x297a, 0 },
-	{ 0x2a45, 0x2a45, 0x2a45, 0 },
-	{ 0x2b13, 0x2b13, 0x2b13, 0 },
-	{ 0x2be3, 0x2be3, 0x2be3, 0 },
-	{ 0x2cb5, 0x2cb5, 0x2cb5, 0 },
-	{ 0x2d89, 0x2d89, 0x2d89, 0 },
-	{ 0x2e60, 0x2e60, 0x2e60, 0 },
-	{ 0x2f39, 0x2f39, 0x2f39, 0 },
-	{ 0x3014, 0x3014, 0x3014, 0 },
-	{ 0x30f2, 0x30f2, 0x30f2, 0 },
-	{ 0x31d2, 0x31d2, 0x31d2, 0 },
-	{ 0x32b4, 0x32b4, 0x32b4, 0 },
-	{ 0x3398, 0x3398, 0x3398, 0 },
-	{ 0x347f, 0x347f, 0x347f, 0 },
-	{ 0x3569, 0x3569, 0x3569, 0 },
-	{ 0x3654, 0x3654, 0x3654, 0 },
-	{ 0x3742, 0x3742, 0x3742, 0 },
-	{ 0x3832, 0x3832, 0x3832, 0 },
-	{ 0x3925, 0x3925, 0x3925, 0 },
-	{ 0x3a1a, 0x3a1a, 0x3a1a, 0 },
-	{ 0x3b11, 0x3b11, 0x3b11, 0 },
-	{ 0x3c0b, 0x3c0b, 0x3c0b, 0 },
-	{ 0x3d07, 0x3d07, 0x3d07, 0 },
-	{ 0x3e05, 0x3e05, 0x3e05, 0 },
-	{ 0x3f06, 0x3f06, 0x3f06, 0 },
-	{ 0x400a, 0x400a, 0x400a, 0 },
-	{ 0x410f, 0x410f, 0x410f, 0 },
-	{ 0x4218, 0x4218, 0x4218, 0 },
-	{ 0x4322, 0x4322, 0x4322, 0 },
-	{ 0x442f, 0x442f, 0x442f, 0 },
-	{ 0x453f, 0x453f, 0x453f, 0 },
-	{ 0x4650, 0x4650, 0x4650, 0 },
-	{ 0x4765, 0x4765, 0x4765, 0 },
-	{ 0x487c, 0x487c, 0x487c, 0 },
-	{ 0x4995, 0x4995, 0x4995, 0 },
-	{ 0x4ab1, 0x4ab1, 0x4ab1, 0 },
-	{ 0x4bcf, 0x4bcf, 0x4bcf, 0 },
-	{ 0x4cf0, 0x4cf0, 0x4cf0, 0 },
-	{ 0x4e13, 0x4e13, 0x4e13, 0 },
-	{ 0x4f39, 0x4f39, 0x4f39, 0 },
-	{ 0x5061, 0x5061, 0x5061, 0 },
-	{ 0x518b, 0x518b, 0x518b, 0 },
-	{ 0x52b9, 0x52b9, 0x52b9, 0 },
-	{ 0x53e8, 0x53e8, 0x53e8, 0 },
-	{ 0x551b, 0x551b, 0x551b, 0 },
-	{ 0x5650, 0x5650, 0x5650, 0 },
-	{ 0x5787, 0x5787, 0x5787, 0 },
-	{ 0x58c1, 0x58c1, 0x58c1, 0 },
-	{ 0x59fd, 0x59fd, 0x59fd, 0 },
-	{ 0x5b3c, 0x5b3c, 0x5b3c, 0 },
-	{ 0x5c7e, 0x5c7e, 0x5c7e, 0 },
-	{ 0x5dc2, 0x5dc2, 0x5dc2, 0 },
-	{ 0x5f09, 0x5f09, 0x5f09, 0 },
-	{ 0x6052, 0x6052, 0x6052, 0 },
-	{ 0x619e, 0x619e, 0x619e, 0 },
-	{ 0x62ec, 0x62ec, 0x62ec, 0 },
-	{ 0x643d, 0x643d, 0x643d, 0 },
-	{ 0x6591, 0x6591, 0x6591, 0 },
-	{ 0x66e7, 0x66e7, 0x66e7, 0 },
-	{ 0x6840, 0x6840, 0x6840, 0 },
-	{ 0x699b, 0x699b, 0x699b, 0 },
-	{ 0x6afa, 0x6afa, 0x6afa, 0 },
-	{ 0x6c5a, 0x6c5a, 0x6c5a, 0 },
-	{ 0x6dbe, 0x6dbe, 0x6dbe, 0 },
-	{ 0x6f24, 0x6f24, 0x6f24, 0 },
-	{ 0x708c, 0x708c, 0x708c, 0 },
-	{ 0x71f8, 0x71f8, 0x71f8, 0 },
-	{ 0x7366, 0x7366, 0x7366, 0 },
-	{ 0x74d6, 0x74d6, 0x74d6, 0 },
-	{ 0x764a, 0x764a, 0x764a, 0 },
-	{ 0x77c0, 0x77c0, 0x77c0, 0 },
-	{ 0x7938, 0x7938, 0x7938, 0 },
-	{ 0x7ab4, 0x7ab4, 0x7ab4, 0 },
-	{ 0x7c32, 0x7c32, 0x7c32, 0 },
-	{ 0x7db3, 0x7db3, 0x7db3, 0 },
-	{ 0x7f36, 0x7f36, 0x7f36, 0 },
-	{ 0x80bc, 0x80bc, 0x80bc, 0 },
-	{ 0x8245, 0x8245, 0x8245, 0 },
-	{ 0x83d1, 0x83d1, 0x83d1, 0 },
-	{ 0x855f, 0x855f, 0x855f, 0 },
-	{ 0x86f0, 0x86f0, 0x86f0, 0 },
-	{ 0x8884, 0x8884, 0x8884, 0 },
-	{ 0x8a1a, 0x8a1a, 0x8a1a, 0 },
-	{ 0x8bb4, 0x8bb4, 0x8bb4, 0 },
-	{ 0x8d50, 0x8d50, 0x8d50, 0 },
-	{ 0x8eee, 0x8eee, 0x8eee, 0 },
-	{ 0x9090, 0x9090, 0x9090, 0 },
-	{ 0x9234, 0x9234, 0x9234, 0 },
-	{ 0x93db, 0x93db, 0x93db, 0 },
-	{ 0x9585, 0x9585, 0x9585, 0 },
-	{ 0x9732, 0x9732, 0x9732, 0 },
-	{ 0x98e1, 0x98e1, 0x98e1, 0 },
-	{ 0x9a93, 0x9a93, 0x9a93, 0 },
-	{ 0x9c48, 0x9c48, 0x9c48, 0 },
-	{ 0x9e00, 0x9e00, 0x9e00, 0 },
-	{ 0x9fbb, 0x9fbb, 0x9fbb, 0 },
-	{ 0xa178, 0xa178, 0xa178, 0 },
-	{ 0xa338, 0xa338, 0xa338, 0 },
-	{ 0xa4fb, 0xa4fb, 0xa4fb, 0 },
-	{ 0xa6c1, 0xa6c1, 0xa6c1, 0 },
-	{ 0xa88a, 0xa88a, 0xa88a, 0 },
-	{ 0xaa56, 0xaa56, 0xaa56, 0 },
-	{ 0xac24, 0xac24, 0xac24, 0 },
-	{ 0xadf5, 0xadf5, 0xadf5, 0 },
-	{ 0xafc9, 0xafc9, 0xafc9, 0 },
-	{ 0xb1a0, 0xb1a0, 0xb1a0, 0 },
-	{ 0xb37a, 0xb37a, 0xb37a, 0 },
-	{ 0xb557, 0xb557, 0xb557, 0 },
-	{ 0xb736, 0xb736, 0xb736, 0 },
-	{ 0xb919, 0xb919, 0xb919, 0 },
-	{ 0xbafe, 0xbafe, 0xbafe, 0 },
-	{ 0xbce6, 0xbce6, 0xbce6, 0 },
-	{ 0xbed2, 0xbed2, 0xbed2, 0 },
-	{ 0xc0c0, 0xc0c0, 0xc0c0, 0 },
-	{ 0xc2b0, 0xc2b0, 0xc2b0, 0 },
-	{ 0xc4a4, 0xc4a4, 0xc4a4, 0 },
-	{ 0xc69b, 0xc69b, 0xc69b, 0 },
-	{ 0xc895, 0xc895, 0xc895, 0 },
-	{ 0xca91, 0xca91, 0xca91, 0 },
-	{ 0xcc91, 0xcc91, 0xcc91, 0 },
-	{ 0xce93, 0xce93, 0xce93, 0 },
-	{ 0xd098, 0xd098, 0xd098, 0 },
-	{ 0xd2a1, 0xd2a1, 0xd2a1, 0 },
-	{ 0xd4ac, 0xd4ac, 0xd4ac, 0 },
-	{ 0xd6ba, 0xd6ba, 0xd6ba, 0 },
-	{ 0xd8cb, 0xd8cb, 0xd8cb, 0 },
-	{ 0xdadf, 0xdadf, 0xdadf, 0 },
-	{ 0xdcf7, 0xdcf7, 0xdcf7, 0 },
-	{ 0xdf11, 0xdf11, 0xdf11, 0 },
-	{ 0xe12e, 0xe12e, 0xe12e, 0 },
-	{ 0xe34e, 0xe34e, 0xe34e, 0 },
-	{ 0xe571, 0xe571, 0xe571, 0 },
-	{ 0xe796, 0xe796, 0xe796, 0 },
-	{ 0xe9bf, 0xe9bf, 0xe9bf, 0 },
-	{ 0xebeb, 0xebeb, 0xebeb, 0 },
-	{ 0xee1a, 0xee1a, 0xee1a, 0 },
-	{ 0xf04c, 0xf04c, 0xf04c, 0 },
-	{ 0xf281, 0xf281, 0xf281, 0 },
-	{ 0xf4b9, 0xf4b9, 0xf4b9, 0 },
-	{ 0xf6f4, 0xf6f4, 0xf6f4, 0 },
-	{ 0xf932, 0xf932, 0xf932, 0 },
-	{ 0xfb73, 0xfb73, 0xfb73, 0 },
-	{ 0xfdb7, 0xfdb7, 0xfdb7, 0 },
-	{ 0xffff, 0xffff, 0xffff, 0 },
+	{ 0x0000, 0x0000, 0x0000, 0 },
+	{ 0x009e, 0x009e, 0x009e, 0 },
+	{ 0x00c6, 0x00c6, 0x00c6, 0 },
+	{ 0x00da, 0x00da, 0x00da, 0 },
+	{ 0x00f0, 0x00f0, 0x00f0, 0 },
+	{ 0x011e, 0x011e, 0x011e, 0 },
+	{ 0x0152, 0x0152, 0x0152, 0 },
+	{ 0x016d, 0x016d, 0x016d, 0 },
+	{ 0x018a, 0x018a, 0x018a, 0 },
+	{ 0x01a8, 0x01a8, 0x01a8, 0 },
+	{ 0x01c8, 0x01c8, 0x01c8, 0 },
+	{ 0x01e9, 0x01e9, 0x01e9, 0 },
+	{ 0x020b, 0x020b, 0x020b, 0 },
+	{ 0x022e, 0x022e, 0x022e, 0 },
+	{ 0x0253, 0x0253, 0x0253, 0 },
+	{ 0x0279, 0x0279, 0x0279, 0 },
+	{ 0x02a1, 0x02a1, 0x02a1, 0 },
+	{ 0x02ca, 0x02ca, 0x02ca, 0 },
+	{ 0x02f4, 0x02f4, 0x02f4, 0 },
+	{ 0x034d, 0x034d, 0x034d, 0 },
+	{ 0x03ac, 0x03ac, 0x03ac, 0 },
+	{ 0x0411, 0x0411, 0x0411, 0 },
+	{ 0x047c, 0x047c, 0x047c, 0 },
+	{ 0x04ed, 0x04ed, 0x04ed, 0 },
+	{ 0x0528, 0x0528, 0x0528, 0 },
+	{ 0x0565, 0x0565, 0x0565, 0 },
+	{ 0x05a3, 0x05a3, 0x05a3, 0 },
+	{ 0x05e3, 0x05e3, 0x05e3, 0 },
+	{ 0x0624, 0x0624, 0x0624, 0 },
+	{ 0x0667, 0x0667, 0x0667, 0 },
+	{ 0x06ab, 0x06ab, 0x06ab, 0 },
+	{ 0x06f1, 0x06f1, 0x06f1, 0 },
+	{ 0x0739, 0x0739, 0x0739, 0 },
+	{ 0x0783, 0x0783, 0x0783, 0 },
+	{ 0x07ce, 0x07ce, 0x07ce, 0 },
+	{ 0x081b, 0x081b, 0x081b, 0 },
+	{ 0x0869, 0x0869, 0x0869, 0 },
+	{ 0x08ba, 0x08ba, 0x08ba, 0 },
+	{ 0x090c, 0x090c, 0x090c, 0 },
+	{ 0x095f, 0x095f, 0x095f, 0 },
+	{ 0x09b5, 0x09b5, 0x09b5, 0 },
+	{ 0x0a0c, 0x0a0c, 0x0a0c, 0 },
+	{ 0x0a65, 0x0a65, 0x0a65, 0 },
+	{ 0x0ac0, 0x0ac0, 0x0ac0, 0 },
+	{ 0x0b1c, 0x0b1c, 0x0b1c, 0 },
+	{ 0x0b7b, 0x0b7b, 0x0b7b, 0 },
+	{ 0x0baa, 0x0baa, 0x0baa, 0 },
+	{ 0x0bdb, 0x0bdb, 0x0bdb, 0 },
+	{ 0x0c0b, 0x0c0b, 0x0c0b, 0 },
+	{ 0x0c3d, 0x0c3d, 0x0c3d, 0 },
+	{ 0x0ca0, 0x0ca0, 0x0ca0, 0 },
+	{ 0x0d06, 0x0d06, 0x0d06, 0 },
+	{ 0x0d6d, 0x0d6d, 0x0d6d, 0 },
+	{ 0x0dd7, 0x0dd7, 0x0dd7, 0 },
+	{ 0x0e0c, 0x0e0c, 0x0e0c, 0 },
+	{ 0x0e42, 0x0e42, 0x0e42, 0 },
+	{ 0x0e78, 0x0e78, 0x0e78, 0 },
+	{ 0x0eaf, 0x0eaf, 0x0eaf, 0 },
+	{ 0x0f1d, 0x0f1d, 0x0f1d, 0 },
+	{ 0x0f8e, 0x0f8e, 0x0f8e, 0 },
+	{ 0x1001, 0x1001, 0x1001, 0 },
+	{ 0x1075, 0x1075, 0x1075, 0 },
+	{ 0x10ec, 0x10ec, 0x10ec, 0 },
+	{ 0x1164, 0x1164, 0x1164, 0 },
+	{ 0x11de, 0x11de, 0x11de, 0 },
+	{ 0x125b, 0x125b, 0x125b, 0 },
+	{ 0x12d9, 0x12d9, 0x12d9, 0 },
+	{ 0x1359, 0x1359, 0x1359, 0 },
+	{ 0x13db, 0x13db, 0x13db, 0 },
+	{ 0x145f, 0x145f, 0x145f, 0 },
+	{ 0x14e5, 0x14e5, 0x14e5, 0 },
+	{ 0x156d, 0x156d, 0x156d, 0 },
+	{ 0x15f7, 0x15f7, 0x15f7, 0 },
+	{ 0x1683, 0x1683, 0x1683, 0 },
+	{ 0x1711, 0x1711, 0x1711, 0 },
+	{ 0x17a1, 0x17a1, 0x17a1, 0 },
+	{ 0x1833, 0x1833, 0x1833, 0 },
+	{ 0x18c7, 0x18c7, 0x18c7, 0 },
+	{ 0x195e, 0x195e, 0x195e, 0 },
+	{ 0x19f6, 0x19f6, 0x19f6, 0 },
+	{ 0x1a90, 0x1a90, 0x1a90, 0 },
+	{ 0x1b2c, 0x1b2c, 0x1b2c, 0 },
+	{ 0x1bcb, 0x1bcb, 0x1bcb, 0 },
+	{ 0x1c6b, 0x1c6b, 0x1c6b, 0 },
+	{ 0x1d0e, 0x1d0e, 0x1d0e, 0 },
+	{ 0x1db3, 0x1db3, 0x1db3, 0 },
+	{ 0x1e06, 0x1e06, 0x1e06, 0 },
+	{ 0x1e5a, 0x1e5a, 0x1e5a, 0 },
+	{ 0x1f02, 0x1f02, 0x1f02, 0 },
+	{ 0x1fae, 0x1fae, 0x1fae, 0 },
+	{ 0x2004, 0x2004, 0x2004, 0 },
+	{ 0x205b, 0x205b, 0x205b, 0 },
+	{ 0x210a, 0x210a, 0x210a, 0 },
+	{ 0x21bc, 0x21bc, 0x21bc, 0 },
+	{ 0x226f, 0x226f, 0x226f, 0 },
+	{ 0x2325, 0x2325, 0x2325, 0 },
+	{ 0x23dd, 0x23dd, 0x23dd, 0 },
+	{ 0x2497, 0x2497, 0x2497, 0 },
+	{ 0x2553, 0x2553, 0x2553, 0 },
+	{ 0x2612, 0x2612, 0x2612, 0 },
+	{ 0x26d3, 0x26d3, 0x26d3, 0 },
+	{ 0x2795, 0x2795, 0x2795, 0 },
+	{ 0x285b, 0x285b, 0x285b, 0 },
+	{ 0x2922, 0x2922, 0x2922, 0 },
+	{ 0x29eb, 0x29eb, 0x29eb, 0 },
+	{ 0x2ab7, 0x2ab7, 0x2ab7, 0 },
+	{ 0x2b85, 0x2b85, 0x2b85, 0 },
+	{ 0x2c56, 0x2c56, 0x2c56, 0 },
+	{ 0x2d28, 0x2d28, 0x2d28, 0 },
+	{ 0x2dfd, 0x2dfd, 0x2dfd, 0 },
+	{ 0x2ed4, 0x2ed4, 0x2ed4, 0 },
+	{ 0x2fad, 0x2fad, 0x2fad, 0 },
+	{ 0x3089, 0x3089, 0x3089, 0 },
+	{ 0x3167, 0x3167, 0x3167, 0 },
+	{ 0x3247, 0x3247, 0x3247, 0 },
+	{ 0x332a, 0x332a, 0x332a, 0 },
+	{ 0x340e, 0x340e, 0x340e, 0 },
+	{ 0x34f5, 0x34f5, 0x34f5, 0 },
+	{ 0x35df, 0x35df, 0x35df, 0 },
+	{ 0x36cb, 0x36cb, 0x36cb, 0 },
+	{ 0x37b9, 0x37b9, 0x37b9, 0 },
+	{ 0x38a9, 0x38a9, 0x38a9, 0 },
+	{ 0x399c, 0x399c, 0x399c, 0 },
+	{ 0x3a91, 0x3a91, 0x3a91, 0 },
+	{ 0x3b89, 0x3b89, 0x3b89, 0 },
+	{ 0x3c83, 0x3c83, 0x3c83, 0 },
+	{ 0x3d7f, 0x3d7f, 0x3d7f, 0 },
+	{ 0x3e7e, 0x3e7e, 0x3e7e, 0 },
+	{ 0x3efe, 0x3efe, 0x3efe, 0 },
+	{ 0x3f7f, 0x3f7f, 0x3f7f, 0 },
+	{ 0x4082, 0x4082, 0x4082, 0 },
+	{ 0x4188, 0x4188, 0x4188, 0 },
+	{ 0x4290, 0x4290, 0x4290, 0 },
+	{ 0x439b, 0x439b, 0x439b, 0 },
+	{ 0x4421, 0x4421, 0x4421, 0 },
+	{ 0x44a8, 0x44a8, 0x44a8, 0 },
+	{ 0x45b7, 0x45b7, 0x45b7, 0 },
+	{ 0x46c9, 0x46c9, 0x46c9, 0 },
+	{ 0x47dd, 0x47dd, 0x47dd, 0 },
+	{ 0x48f4, 0x48f4, 0x48f4, 0 },
+	{ 0x4a0d, 0x4a0d, 0x4a0d, 0 },
+	{ 0x4b29, 0x4b29, 0x4b29, 0 },
+	{ 0x4c47, 0x4c47, 0x4c47, 0 },
+	{ 0x4d68, 0x4d68, 0x4d68, 0 },
+	{ 0x4e8b, 0x4e8b, 0x4e8b, 0 },
+	{ 0x4fb1, 0x4fb1, 0x4fb1, 0 },
+	{ 0x5044, 0x5044, 0x5044, 0 },
+	{ 0x50d9, 0x50d9, 0x50d9, 0 },
+	{ 0x5203, 0x5203, 0x5203, 0 },
+	{ 0x5330, 0x5330, 0x5330, 0 },
+	{ 0x5460, 0x5460, 0x5460, 0 },
+	{ 0x5592, 0x5592, 0x5592, 0 },
+	{ 0x56c6, 0x56c6, 0x56c6, 0 },
+	{ 0x57fe, 0x57fe, 0x57fe, 0 },
+	{ 0x5937, 0x5937, 0x5937, 0 },
+	{ 0x5a73, 0x5a73, 0x5a73, 0 },
+	{ 0x5bb2, 0x5bb2, 0x5bb2, 0 },
+	{ 0x5cf3, 0x5cf3, 0x5cf3, 0 },
+	{ 0x5e37, 0x5e37, 0x5e37, 0 },
+	{ 0x5f7d, 0x5f7d, 0x5f7d, 0 },
+	{ 0x60c6, 0x60c6, 0x60c6, 0 },
+	{ 0x6212, 0x6212, 0x6212, 0 },
+	{ 0x6360, 0x6360, 0x6360, 0 },
+	{ 0x64b0, 0x64b0, 0x64b0, 0 },
+	{ 0x6604, 0x6604, 0x6604, 0 },
+	{ 0x6759, 0x6759, 0x6759, 0 },
+	{ 0x68b2, 0x68b2, 0x68b2, 0 },
+	{ 0x6a0d, 0x6a0d, 0x6a0d, 0 },
+	{ 0x6b6a, 0x6b6a, 0x6b6a, 0 },
+	{ 0x6ccb, 0x6ccb, 0x6ccb, 0 },
+	{ 0x6e2d, 0x6e2d, 0x6e2d, 0 },
+	{ 0x6f93, 0x6f93, 0x6f93, 0 },
+	{ 0x70fb, 0x70fb, 0x70fb, 0 },
+	{ 0x7266, 0x7266, 0x7266, 0 },
+	{ 0x73d3, 0x73d3, 0x73d3, 0 },
+	{ 0x7543, 0x7543, 0x7543, 0 },
+	{ 0x76b6, 0x76b6, 0x76b6, 0 },
+	{ 0x782b, 0x782b, 0x782b, 0 },
+	{ 0x79a3, 0x79a3, 0x79a3, 0 },
+	{ 0x7a60, 0x7a60, 0x7a60, 0 },
+	{ 0x7b1e, 0x7b1e, 0x7b1e, 0 },
+	{ 0x7bdc, 0x7bdc, 0x7bdc, 0 },
+	{ 0x7c9b, 0x7c9b, 0x7c9b, 0 },
+	{ 0x7e1b, 0x7e1b, 0x7e1b, 0 },
+	{ 0x7f9d, 0x7f9d, 0x7f9d, 0 },
+	{ 0x8123, 0x8123, 0x8123, 0 },
+	{ 0x82ab, 0x82ab, 0x82ab, 0 },
+	{ 0x8436, 0x8436, 0x8436, 0 },
+	{ 0x85c3, 0x85c3, 0x85c3, 0 },
+	{ 0x8753, 0x8753, 0x8753, 0 },
+	{ 0x88e6, 0x88e6, 0x88e6, 0 },
+	{ 0x8a7c, 0x8a7c, 0x8a7c, 0 },
+	{ 0x8c14, 0x8c14, 0x8c14, 0 },
+	{ 0x8daf, 0x8daf, 0x8daf, 0 },
+	{ 0x8f4d, 0x8f4d, 0x8f4d, 0 },
+	{ 0x90ed, 0x90ed, 0x90ed, 0 },
+	{ 0x9291, 0x9291, 0x9291, 0 },
+	{ 0x9363, 0x9363, 0x9363, 0 },
+	{ 0x9437, 0x9437, 0x9437, 0 },
+	{ 0x95df, 0x95df, 0x95df, 0 },
+	{ 0x978b, 0x978b, 0x978b, 0 },
+	{ 0x9939, 0x9939, 0x9939, 0 },
+	{ 0x9aea, 0x9aea, 0x9aea, 0 },
+	{ 0x9c9e, 0x9c9e, 0x9c9e, 0 },
+	{ 0x9e55, 0x9e55, 0x9e55, 0 },
+	{ 0xa00e, 0xa00e, 0xa00e, 0 },
+	{ 0xa1ca, 0xa1ca, 0xa1ca, 0 },
+	{ 0xa389, 0xa389, 0xa389, 0 },
+	{ 0xa54b, 0xa54b, 0xa54b, 0 },
+	{ 0xa710, 0xa710, 0xa710, 0 },
+	{ 0xa8d7, 0xa8d7, 0xa8d7, 0 },
+	{ 0xaaa2, 0xaaa2, 0xaaa2, 0 },
+	{ 0xab88, 0xab88, 0xab88, 0 },
+	{ 0xac6f, 0xac6f, 0xac6f, 0 },
+	{ 0xae3e, 0xae3e, 0xae3e, 0 },
+	{ 0xb011, 0xb011, 0xb011, 0 },
+	{ 0xb1e7, 0xb1e7, 0xb1e7, 0 },
+	{ 0xb3bf, 0xb3bf, 0xb3bf, 0 },
+	{ 0xb59a, 0xb59a, 0xb59a, 0 },
+	{ 0xb779, 0xb779, 0xb779, 0 },
+	{ 0xb959, 0xb959, 0xb959, 0 },
+	{ 0xbb3d, 0xbb3d, 0xbb3d, 0 },
+	{ 0xbd24, 0xbd24, 0xbd24, 0 },
+	{ 0xbf0d, 0xbf0d, 0xbf0d, 0 },
+	{ 0xc0fa, 0xc0fa, 0xc0fa, 0 },
+	{ 0xc2e9, 0xc2e9, 0xc2e9, 0 },
+	{ 0xc4db, 0xc4db, 0xc4db, 0 },
+	{ 0xc6d0, 0xc6d0, 0xc6d0, 0 },
+	{ 0xc8c8, 0xc8c8, 0xc8c8, 0 },
+	{ 0xcac3, 0xcac3, 0xcac3, 0 },
+	{ 0xccc1, 0xccc1, 0xccc1, 0 },
+	{ 0xcec2, 0xcec2, 0xcec2, 0 },
+	{ 0xd0c5, 0xd0c5, 0xd0c5, 0 },
+	{ 0xd2cc, 0xd2cc, 0xd2cc, 0 },
+	{ 0xd4d5, 0xd4d5, 0xd4d5, 0 },
+	{ 0xd6e2, 0xd6e2, 0xd6e2, 0 },
+	{ 0xd8f1, 0xd8f1, 0xd8f1, 0 },
+	{ 0xdb03, 0xdb03, 0xdb03, 0 },
+	{ 0xdd18, 0xdd18, 0xdd18, 0 },
+	{ 0xdf31, 0xdf31, 0xdf31, 0 },
+	{ 0xe03e, 0xe03e, 0xe03e, 0 },
+	{ 0xe14c, 0xe14c, 0xe14c, 0 },
+	{ 0xe25a, 0xe25a, 0xe25a, 0 },
+	{ 0xe36a, 0xe36a, 0xe36a, 0 },
+	{ 0xe47a, 0xe47a, 0xe47a, 0 },
+	{ 0xe58b, 0xe58b, 0xe58b, 0 },
+	{ 0xe69c, 0xe69c, 0xe69c, 0 },
+	{ 0xe7af, 0xe7af, 0xe7af, 0 },
+	{ 0xe9d5, 0xe9d5, 0xe9d5, 0 },
+	{ 0xebff, 0xebff, 0xebff, 0 },
+	{ 0xee2c, 0xee2c, 0xee2c, 0 },
+	{ 0xf05c, 0xf05c, 0xf05c, 0 },
+	{ 0xf28f, 0xf28f, 0xf28f, 0 },
+	{ 0xf4c5, 0xf4c5, 0xf4c5, 0 },
+	{ 0xf5e1, 0xf5e1, 0xf5e1, 0 },
+	{ 0xf6fe, 0xf6fe, 0xf6fe, 0 },
+	{ 0xf939, 0xf939, 0xf939, 0 },
+	{ 0xfb78, 0xfb78, 0xfb78, 0 },
+	{ 0xfdba, 0xfdba, 0xfdba, 0 },
+	{ 0xffff, 0xffff, 0xffff, 0 }
 };
 
-static_assert(ARRAY_SIZE(linear_x) == ARRAY_SIZE(srgb_y),
+static_assert(ARRAY_SIZE(srgb_x) == ARRAY_SIZE(srgb_y),
 	      "srgb x and y must have the same number of entries");
 
 const struct vkms_color_lut srgb_eotf = {
-	.x = linear_x,
+	.x = srgb_x,
 	.y = srgb_y,
 	.lut_length = ARRAY_SIZE(srgb_y)
 };
 EXPORT_SYMBOL(srgb_eotf);
 
+static u16 srgb_inv_x[] = {
+	0x0000, 0x007f, 0x00bf, 0x00cf, 0x00d7, 0x00df, 0x00ef, 0x00ff,
+	0x010f, 0x011f, 0x012f, 0x013f, 0x014f, 0x015f, 0x016f, 0x017f,
+	0x018f, 0x019f, 0x01af, 0x01bf, 0x01cf, 0x01df, 0x01ef, 0x01ff,
+	0x020f, 0x021f, 0x022f, 0x023f, 0x025f, 0x027f, 0x029f, 0x02af,
+	0x02bf, 0x02df, 0x02ff, 0x030f, 0x031f, 0x032f, 0x033f, 0x034f,
+	0x035f, 0x037f, 0x038f, 0x039f, 0x03af, 0x03bf, 0x03cf, 0x03df,
+	0x03ff, 0x041f, 0x043f, 0x045f, 0x047f, 0x049f, 0x04bf, 0x04df,
+	0x04ff, 0x051f, 0x053f, 0x055f, 0x057f, 0x059f, 0x05bf, 0x05ff,
+	0x063f, 0x065f, 0x067f, 0x069f, 0x06bf, 0x06ff, 0x071f, 0x073f,
+	0x075f, 0x077f, 0x07bf, 0x07ff, 0x083f, 0x087f, 0x08bf, 0x08ff,
+	0x093f, 0x097f, 0x099f, 0x09bf, 0x09ff, 0x0a3f, 0x0a7f, 0x0abf,
+	0x0aff, 0x0b3f, 0x0b7f, 0x0bbf, 0x0bff, 0x0c3f, 0x0c7f, 0x0cbf,
+	0x0cff, 0x0d3f, 0x0d7f, 0x0dbf, 0x0dff, 0x0e3f, 0x0e7f, 0x0ebf,
+	0x0eff, 0x0f3f, 0x0f7f, 0x0fbf, 0x0fff, 0x103f, 0x107f, 0x10bf,
+	0x10ff, 0x117f, 0x11ff, 0x127f, 0x12ff, 0x133f, 0x137f, 0x13bf,
+	0x13ff, 0x143f, 0x147f, 0x14bf, 0x14ff, 0x153f, 0x157f, 0x15ff,
+	0x167f, 0x16ff, 0x177f, 0x17ff, 0x187f, 0x18ff, 0x193f, 0x197f,
+	0x19bf, 0x19ff, 0x1a7f, 0x1aff, 0x1b7f, 0x1bff, 0x1c7f, 0x1cff,
+	0x1d7f, 0x1dff, 0x1e7f, 0x1eff, 0x1f7f, 0x1fff, 0x207f, 0x20ff,
+	0x217f, 0x21ff, 0x22ff, 0x237f, 0x23ff, 0x247f, 0x24ff, 0x25ff,
+	0x26ff, 0x277f, 0x27ff, 0x287f, 0x28ff, 0x297f, 0x29ff, 0x2a7f,
+	0x2aff, 0x2b7f, 0x2bff, 0x2c7f, 0x2cff, 0x2dff, 0x2eff, 0x2fff,
+	0x30ff, 0x31ff, 0x32ff, 0x33ff, 0x347f, 0x34ff, 0x357f, 0x35ff,
+	0x367f, 0x36ff, 0x377f, 0x37ff, 0x38ff, 0x39ff, 0x3aff, 0x3bff,
+	0x3c7f, 0x3cff, 0x3d7f, 0x3dff, 0x3eff, 0x3fff, 0x40ff, 0x41ff,
+	0x42ff, 0x43ff, 0x44ff, 0x45ff, 0x46ff, 0x47ff, 0x48ff, 0x49ff,
+	0x4aff, 0x4bff, 0x4cff, 0x4dff, 0x4eff, 0x4fff, 0x50ff, 0x51ff,
+	0x527f, 0x52ff, 0x53ff, 0x55ff, 0x56ff, 0x57ff, 0x58ff, 0x59ff,
+	0x5aff, 0x5bff, 0x5dff, 0x5eff, 0x5fff, 0x60ff, 0x61ff, 0x62ff,
+	0x63ff, 0x64ff, 0x65ff, 0x66ff, 0x677f, 0x67ff, 0x68ff, 0x69ff,
+	0x6aff, 0x6bff, 0x6dff, 0x6fff, 0x71ff, 0x73ff, 0x75ff, 0x76ff,
+	0x77ff, 0x78ff, 0x79ff, 0x7bff, 0x7dff, 0x7fff, 0x80ff, 0x81ff,
+	0x83ff, 0x85ff, 0x86ff, 0x87ff, 0x88ff, 0x89ff, 0x8aff, 0x8bff,
+	0x8dff, 0x8fff, 0x91ff, 0x92ff, 0x93ff, 0x94ff, 0x95ff, 0x97ff,
+	0x99ff, 0x9bff, 0x9dff, 0x9fff, 0xa0ff, 0xa1ff, 0xa3ff, 0xa5ff,
+	0xa7ff, 0xa9ff, 0xaaff, 0xabff, 0xadff, 0xafff, 0xb1ff, 0xb2ff,
+	0xb3ff, 0xb5ff, 0xb7ff, 0xb9ff, 0xbbff, 0xbdff, 0xbfff, 0xc1ff,
+	0xc3ff, 0xc5ff, 0xc7ff, 0xc9ff, 0xcbff, 0xcdff, 0xcfff, 0xd1ff,
+	0xd3ff, 0xd5ff, 0xd7ff, 0xd9ff, 0xdbff, 0xddff, 0xdeff, 0xdfff,
+	0xe1ff, 0xe3ff, 0xe4ff, 0xe5ff, 0xe6ff, 0xe7ff, 0xe87f, 0xe8ff,
+	0xe9ff, 0xebff, 0xefff, 0xf3ff, 0xf5ff, 0xf7ff, 0xf9ff, 0xfbff,
+	0xfdff, 0xffff
+};
+
 static struct drm_color_lut srgb_inv_y[] = {
-	{ 0x0, 0x0, 0x0, 0 },
-	{ 0xcc2, 0xcc2, 0xcc2, 0 },
-	{ 0x15be, 0x15be, 0x15be, 0 },
-	{ 0x1c56, 0x1c56, 0x1c56, 0 },
-	{ 0x21bd, 0x21bd, 0x21bd, 0 },
-	{ 0x2666, 0x2666, 0x2666, 0 },
-	{ 0x2a8a, 0x2a8a, 0x2a8a, 0 },
-	{ 0x2e4c, 0x2e4c, 0x2e4c, 0 },
-	{ 0x31c0, 0x31c0, 0x31c0, 0 },
-	{ 0x34f6, 0x34f6, 0x34f6, 0 },
-	{ 0x37f9, 0x37f9, 0x37f9, 0 },
-	{ 0x3acf, 0x3acf, 0x3acf, 0 },
-	{ 0x3d80, 0x3d80, 0x3d80, 0 },
-	{ 0x4010, 0x4010, 0x4010, 0 },
-	{ 0x4284, 0x4284, 0x4284, 0 },
-	{ 0x44dd, 0x44dd, 0x44dd, 0 },
-	{ 0x4720, 0x4720, 0x4720, 0 },
-	{ 0x494e, 0x494e, 0x494e, 0 },
-	{ 0x4b69, 0x4b69, 0x4b69, 0 },
-	{ 0x4d73, 0x4d73, 0x4d73, 0 },
-	{ 0x4f6e, 0x4f6e, 0x4f6e, 0 },
-	{ 0x5159, 0x5159, 0x5159, 0 },
-	{ 0x5337, 0x5337, 0x5337, 0 },
-	{ 0x5509, 0x5509, 0x5509, 0 },
-	{ 0x56cf, 0x56cf, 0x56cf, 0 },
-	{ 0x588a, 0x588a, 0x588a, 0 },
-	{ 0x5a3b, 0x5a3b, 0x5a3b, 0 },
-	{ 0x5be2, 0x5be2, 0x5be2, 0 },
-	{ 0x5d80, 0x5d80, 0x5d80, 0 },
-	{ 0x5f16, 0x5f16, 0x5f16, 0 },
-	{ 0x60a4, 0x60a4, 0x60a4, 0 },
-	{ 0x6229, 0x6229, 0x6229, 0 },
-	{ 0x63a8, 0x63a8, 0x63a8, 0 },
-	{ 0x6520, 0x6520, 0x6520, 0 },
-	{ 0x6691, 0x6691, 0x6691, 0 },
-	{ 0x67fc, 0x67fc, 0x67fc, 0 },
-	{ 0x6961, 0x6961, 0x6961, 0 },
-	{ 0x6ac0, 0x6ac0, 0x6ac0, 0 },
-	{ 0x6c19, 0x6c19, 0x6c19, 0 },
-	{ 0x6d6e, 0x6d6e, 0x6d6e, 0 },
-	{ 0x6ebd, 0x6ebd, 0x6ebd, 0 },
-	{ 0x7008, 0x7008, 0x7008, 0 },
-	{ 0x714d, 0x714d, 0x714d, 0 },
-	{ 0x728f, 0x728f, 0x728f, 0 },
-	{ 0x73cc, 0x73cc, 0x73cc, 0 },
-	{ 0x7504, 0x7504, 0x7504, 0 },
-	{ 0x7639, 0x7639, 0x7639, 0 },
-	{ 0x776a, 0x776a, 0x776a, 0 },
-	{ 0x7897, 0x7897, 0x7897, 0 },
-	{ 0x79c1, 0x79c1, 0x79c1, 0 },
-	{ 0x7ae7, 0x7ae7, 0x7ae7, 0 },
-	{ 0x7c09, 0x7c09, 0x7c09, 0 },
-	{ 0x7d28, 0x7d28, 0x7d28, 0 },
-	{ 0x7e44, 0x7e44, 0x7e44, 0 },
-	{ 0x7f5d, 0x7f5d, 0x7f5d, 0 },
-	{ 0x8073, 0x8073, 0x8073, 0 },
-	{ 0x8186, 0x8186, 0x8186, 0 },
-	{ 0x8296, 0x8296, 0x8296, 0 },
-	{ 0x83a4, 0x83a4, 0x83a4, 0 },
-	{ 0x84ae, 0x84ae, 0x84ae, 0 },
-	{ 0x85b6, 0x85b6, 0x85b6, 0 },
-	{ 0x86bc, 0x86bc, 0x86bc, 0 },
-	{ 0x87bf, 0x87bf, 0x87bf, 0 },
-	{ 0x88bf, 0x88bf, 0x88bf, 0 },
-	{ 0x89be, 0x89be, 0x89be, 0 },
-	{ 0x8ab9, 0x8ab9, 0x8ab9, 0 },
-	{ 0x8bb3, 0x8bb3, 0x8bb3, 0 },
-	{ 0x8cab, 0x8cab, 0x8cab, 0 },
-	{ 0x8da0, 0x8da0, 0x8da0, 0 },
-	{ 0x8e93, 0x8e93, 0x8e93, 0 },
-	{ 0x8f84, 0x8f84, 0x8f84, 0 },
-	{ 0x9073, 0x9073, 0x9073, 0 },
-	{ 0x9161, 0x9161, 0x9161, 0 },
-	{ 0x924c, 0x924c, 0x924c, 0 },
-	{ 0x9335, 0x9335, 0x9335, 0 },
-	{ 0x941d, 0x941d, 0x941d, 0 },
-	{ 0x9503, 0x9503, 0x9503, 0 },
-	{ 0x95e7, 0x95e7, 0x95e7, 0 },
-	{ 0x96c9, 0x96c9, 0x96c9, 0 },
-	{ 0x97aa, 0x97aa, 0x97aa, 0 },
-	{ 0x9889, 0x9889, 0x9889, 0 },
-	{ 0x9966, 0x9966, 0x9966, 0 },
-	{ 0x9a42, 0x9a42, 0x9a42, 0 },
-	{ 0x9b1c, 0x9b1c, 0x9b1c, 0 },
-	{ 0x9bf5, 0x9bf5, 0x9bf5, 0 },
-	{ 0x9ccc, 0x9ccc, 0x9ccc, 0 },
-	{ 0x9da1, 0x9da1, 0x9da1, 0 },
-	{ 0x9e76, 0x9e76, 0x9e76, 0 },
-	{ 0x9f49, 0x9f49, 0x9f49, 0 },
-	{ 0xa01a, 0xa01a, 0xa01a, 0 },
-	{ 0xa0ea, 0xa0ea, 0xa0ea, 0 },
-	{ 0xa1b9, 0xa1b9, 0xa1b9, 0 },
-	{ 0xa286, 0xa286, 0xa286, 0 },
-	{ 0xa352, 0xa352, 0xa352, 0 },
-	{ 0xa41d, 0xa41d, 0xa41d, 0 },
-	{ 0xa4e7, 0xa4e7, 0xa4e7, 0 },
-	{ 0xa5af, 0xa5af, 0xa5af, 0 },
-	{ 0xa676, 0xa676, 0xa676, 0 },
-	{ 0xa73c, 0xa73c, 0xa73c, 0 },
-	{ 0xa801, 0xa801, 0xa801, 0 },
-	{ 0xa8c5, 0xa8c5, 0xa8c5, 0 },
-	{ 0xa987, 0xa987, 0xa987, 0 },
-	{ 0xaa48, 0xaa48, 0xaa48, 0 },
-	{ 0xab09, 0xab09, 0xab09, 0 },
-	{ 0xabc8, 0xabc8, 0xabc8, 0 },
-	{ 0xac86, 0xac86, 0xac86, 0 },
-	{ 0xad43, 0xad43, 0xad43, 0 },
-	{ 0xadff, 0xadff, 0xadff, 0 },
-	{ 0xaeba, 0xaeba, 0xaeba, 0 },
-	{ 0xaf74, 0xaf74, 0xaf74, 0 },
-	{ 0xb02d, 0xb02d, 0xb02d, 0 },
-	{ 0xb0e5, 0xb0e5, 0xb0e5, 0 },
-	{ 0xb19c, 0xb19c, 0xb19c, 0 },
-	{ 0xb252, 0xb252, 0xb252, 0 },
-	{ 0xb307, 0xb307, 0xb307, 0 },
-	{ 0xb3bb, 0xb3bb, 0xb3bb, 0 },
-	{ 0xb46f, 0xb46f, 0xb46f, 0 },
-	{ 0xb521, 0xb521, 0xb521, 0 },
-	{ 0xb5d3, 0xb5d3, 0xb5d3, 0 },
-	{ 0xb683, 0xb683, 0xb683, 0 },
-	{ 0xb733, 0xb733, 0xb733, 0 },
-	{ 0xb7e2, 0xb7e2, 0xb7e2, 0 },
-	{ 0xb890, 0xb890, 0xb890, 0 },
-	{ 0xb93d, 0xb93d, 0xb93d, 0 },
-	{ 0xb9ea, 0xb9ea, 0xb9ea, 0 },
-	{ 0xba96, 0xba96, 0xba96, 0 },
-	{ 0xbb40, 0xbb40, 0xbb40, 0 },
-	{ 0xbbea, 0xbbea, 0xbbea, 0 },
-	{ 0xbc94, 0xbc94, 0xbc94, 0 },
-	{ 0xbd3c, 0xbd3c, 0xbd3c, 0 },
-	{ 0xbde4, 0xbde4, 0xbde4, 0 },
-	{ 0xbe8b, 0xbe8b, 0xbe8b, 0 },
-	{ 0xbf31, 0xbf31, 0xbf31, 0 },
-	{ 0xbfd7, 0xbfd7, 0xbfd7, 0 },
-	{ 0xc07b, 0xc07b, 0xc07b, 0 },
-	{ 0xc120, 0xc120, 0xc120, 0 },
-	{ 0xc1c3, 0xc1c3, 0xc1c3, 0 },
-	{ 0xc266, 0xc266, 0xc266, 0 },
-	{ 0xc308, 0xc308, 0xc308, 0 },
-	{ 0xc3a9, 0xc3a9, 0xc3a9, 0 },
-	{ 0xc449, 0xc449, 0xc449, 0 },
-	{ 0xc4e9, 0xc4e9, 0xc4e9, 0 },
-	{ 0xc589, 0xc589, 0xc589, 0 },
-	{ 0xc627, 0xc627, 0xc627, 0 },
-	{ 0xc6c5, 0xc6c5, 0xc6c5, 0 },
-	{ 0xc763, 0xc763, 0xc763, 0 },
-	{ 0xc7ff, 0xc7ff, 0xc7ff, 0 },
-	{ 0xc89b, 0xc89b, 0xc89b, 0 },
-	{ 0xc937, 0xc937, 0xc937, 0 },
-	{ 0xc9d2, 0xc9d2, 0xc9d2, 0 },
-	{ 0xca6c, 0xca6c, 0xca6c, 0 },
-	{ 0xcb06, 0xcb06, 0xcb06, 0 },
-	{ 0xcb9f, 0xcb9f, 0xcb9f, 0 },
-	{ 0xcc37, 0xcc37, 0xcc37, 0 },
-	{ 0xcccf, 0xcccf, 0xcccf, 0 },
-	{ 0xcd66, 0xcd66, 0xcd66, 0 },
-	{ 0xcdfd, 0xcdfd, 0xcdfd, 0 },
-	{ 0xce93, 0xce93, 0xce93, 0 },
-	{ 0xcf29, 0xcf29, 0xcf29, 0 },
-	{ 0xcfbe, 0xcfbe, 0xcfbe, 0 },
-	{ 0xd053, 0xd053, 0xd053, 0 },
-	{ 0xd0e7, 0xd0e7, 0xd0e7, 0 },
-	{ 0xd17a, 0xd17a, 0xd17a, 0 },
-	{ 0xd20d, 0xd20d, 0xd20d, 0 },
-	{ 0xd2a0, 0xd2a0, 0xd2a0, 0 },
-	{ 0xd331, 0xd331, 0xd331, 0 },
-	{ 0xd3c3, 0xd3c3, 0xd3c3, 0 },
-	{ 0xd454, 0xd454, 0xd454, 0 },
-	{ 0xd4e4, 0xd4e4, 0xd4e4, 0 },
-	{ 0xd574, 0xd574, 0xd574, 0 },
-	{ 0xd603, 0xd603, 0xd603, 0 },
-	{ 0xd692, 0xd692, 0xd692, 0 },
-	{ 0xd720, 0xd720, 0xd720, 0 },
-	{ 0xd7ae, 0xd7ae, 0xd7ae, 0 },
-	{ 0xd83c, 0xd83c, 0xd83c, 0 },
-	{ 0xd8c9, 0xd8c9, 0xd8c9, 0 },
-	{ 0xd955, 0xd955, 0xd955, 0 },
-	{ 0xd9e1, 0xd9e1, 0xd9e1, 0 },
-	{ 0xda6d, 0xda6d, 0xda6d, 0 },
-	{ 0xdaf8, 0xdaf8, 0xdaf8, 0 },
-	{ 0xdb83, 0xdb83, 0xdb83, 0 },
-	{ 0xdc0d, 0xdc0d, 0xdc0d, 0 },
-	{ 0xdc97, 0xdc97, 0xdc97, 0 },
-	{ 0xdd20, 0xdd20, 0xdd20, 0 },
-	{ 0xdda9, 0xdda9, 0xdda9, 0 },
-	{ 0xde31, 0xde31, 0xde31, 0 },
-	{ 0xdeb9, 0xdeb9, 0xdeb9, 0 },
-	{ 0xdf41, 0xdf41, 0xdf41, 0 },
-	{ 0xdfc8, 0xdfc8, 0xdfc8, 0 },
-	{ 0xe04f, 0xe04f, 0xe04f, 0 },
-	{ 0xe0d5, 0xe0d5, 0xe0d5, 0 },
-	{ 0xe15b, 0xe15b, 0xe15b, 0 },
-	{ 0xe1e0, 0xe1e0, 0xe1e0, 0 },
-	{ 0xe266, 0xe266, 0xe266, 0 },
-	{ 0xe2ea, 0xe2ea, 0xe2ea, 0 },
-	{ 0xe36f, 0xe36f, 0xe36f, 0 },
-	{ 0xe3f3, 0xe3f3, 0xe3f3, 0 },
-	{ 0xe476, 0xe476, 0xe476, 0 },
-	{ 0xe4f9, 0xe4f9, 0xe4f9, 0 },
-	{ 0xe57c, 0xe57c, 0xe57c, 0 },
-	{ 0xe5fe, 0xe5fe, 0xe5fe, 0 },
-	{ 0xe680, 0xe680, 0xe680, 0 },
-	{ 0xe702, 0xe702, 0xe702, 0 },
-	{ 0xe783, 0xe783, 0xe783, 0 },
-	{ 0xe804, 0xe804, 0xe804, 0 },
-	{ 0xe884, 0xe884, 0xe884, 0 },
-	{ 0xe905, 0xe905, 0xe905, 0 },
-	{ 0xe984, 0xe984, 0xe984, 0 },
-	{ 0xea04, 0xea04, 0xea04, 0 },
-	{ 0xea83, 0xea83, 0xea83, 0 },
-	{ 0xeb02, 0xeb02, 0xeb02, 0 },
-	{ 0xeb80, 0xeb80, 0xeb80, 0 },
-	{ 0xebfe, 0xebfe, 0xebfe, 0 },
-	{ 0xec7b, 0xec7b, 0xec7b, 0 },
-	{ 0xecf9, 0xecf9, 0xecf9, 0 },
-	{ 0xed76, 0xed76, 0xed76, 0 },
-	{ 0xedf2, 0xedf2, 0xedf2, 0 },
-	{ 0xee6f, 0xee6f, 0xee6f, 0 },
-	{ 0xeeeb, 0xeeeb, 0xeeeb, 0 },
-	{ 0xef66, 0xef66, 0xef66, 0 },
-	{ 0xefe2, 0xefe2, 0xefe2, 0 },
-	{ 0xf05d, 0xf05d, 0xf05d, 0 },
-	{ 0xf0d7, 0xf0d7, 0xf0d7, 0 },
-	{ 0xf152, 0xf152, 0xf152, 0 },
-	{ 0xf1cc, 0xf1cc, 0xf1cc, 0 },
-	{ 0xf245, 0xf245, 0xf245, 0 },
-	{ 0xf2bf, 0xf2bf, 0xf2bf, 0 },
-	{ 0xf338, 0xf338, 0xf338, 0 },
-	{ 0xf3b0, 0xf3b0, 0xf3b0, 0 },
-	{ 0xf429, 0xf429, 0xf429, 0 },
-	{ 0xf4a1, 0xf4a1, 0xf4a1, 0 },
-	{ 0xf519, 0xf519, 0xf519, 0 },
-	{ 0xf590, 0xf590, 0xf590, 0 },
-	{ 0xf608, 0xf608, 0xf608, 0 },
-	{ 0xf67e, 0xf67e, 0xf67e, 0 },
-	{ 0xf6f5, 0xf6f5, 0xf6f5, 0 },
-	{ 0xf76b, 0xf76b, 0xf76b, 0 },
-	{ 0xf7e1, 0xf7e1, 0xf7e1, 0 },
-	{ 0xf857, 0xf857, 0xf857, 0 },
-	{ 0xf8cd, 0xf8cd, 0xf8cd, 0 },
-	{ 0xf942, 0xf942, 0xf942, 0 },
-	{ 0xf9b7, 0xf9b7, 0xf9b7, 0 },
-	{ 0xfa2b, 0xfa2b, 0xfa2b, 0 },
-	{ 0xfaa0, 0xfaa0, 0xfaa0, 0 },
-	{ 0xfb14, 0xfb14, 0xfb14, 0 },
-	{ 0xfb88, 0xfb88, 0xfb88, 0 },
-	{ 0xfbfb, 0xfbfb, 0xfbfb, 0 },
-	{ 0xfc6e, 0xfc6e, 0xfc6e, 0 },
-	{ 0xfce1, 0xfce1, 0xfce1, 0 },
-	{ 0xfd54, 0xfd54, 0xfd54, 0 },
-	{ 0xfdc6, 0xfdc6, 0xfdc6, 0 },
-	{ 0xfe39, 0xfe39, 0xfe39, 0 },
-	{ 0xfeaa, 0xfeaa, 0xfeaa, 0 },
-	{ 0xff1c, 0xff1c, 0xff1c, 0 },
-	{ 0xff8d, 0xff8d, 0xff8d, 0 },
-	{ 0xffff, 0xffff, 0xffff, 0 },
+	{ 0x0000, 0x0000, 0x0000, 0 },
+	{ 0x0669, 0x0669, 0x0669, 0 },
+	{ 0x09a4, 0x09a4, 0x09a4, 0 },
+	{ 0x0a72, 0x0a72, 0x0a72, 0 },
+	{ 0x0ad6, 0x0ad6, 0x0ad6, 0 },
+	{ 0x0b38, 0x0b38, 0x0b38, 0 },
+	{ 0x0bf6, 0x0bf6, 0x0bf6, 0 },
+	{ 0x0cac, 0x0cac, 0x0cac, 0 },
+	{ 0x0d5c, 0x0d5c, 0x0d5c, 0 },
+	{ 0x0e06, 0x0e06, 0x0e06, 0 },
+	{ 0x0eaa, 0x0eaa, 0x0eaa, 0 },
+	{ 0x0f4a, 0x0f4a, 0x0f4a, 0 },
+	{ 0x0fe5, 0x0fe5, 0x0fe5, 0 },
+	{ 0x107b, 0x107b, 0x107b, 0 },
+	{ 0x110e, 0x110e, 0x110e, 0 },
+	{ 0x119d, 0x119d, 0x119d, 0 },
+	{ 0x1228, 0x1228, 0x1228, 0 },
+	{ 0x12b1, 0x12b1, 0x12b1, 0 },
+	{ 0x1336, 0x1336, 0x1336, 0 },
+	{ 0x13b8, 0x13b8, 0x13b8, 0 },
+	{ 0x1438, 0x1438, 0x1438, 0 },
+	{ 0x14b5, 0x14b5, 0x14b5, 0 },
+	{ 0x1530, 0x1530, 0x1530, 0 },
+	{ 0x15a9, 0x15a9, 0x15a9, 0 },
+	{ 0x161f, 0x161f, 0x161f, 0 },
+	{ 0x1693, 0x1693, 0x1693, 0 },
+	{ 0x1705, 0x1705, 0x1705, 0 },
+	{ 0x1776, 0x1776, 0x1776, 0 },
+	{ 0x1851, 0x1851, 0x1851, 0 },
+	{ 0x1926, 0x1926, 0x1926, 0 },
+	{ 0x19f4, 0x19f4, 0x19f4, 0 },
+	{ 0x1a59, 0x1a59, 0x1a59, 0 },
+	{ 0x1abd, 0x1abd, 0x1abd, 0 },
+	{ 0x1b81, 0x1b81, 0x1b81, 0 },
+	{ 0x1c3f, 0x1c3f, 0x1c3f, 0 },
+	{ 0x1c9d, 0x1c9d, 0x1c9d, 0 },
+	{ 0x1cf9, 0x1cf9, 0x1cf9, 0 },
+	{ 0x1d55, 0x1d55, 0x1d55, 0 },
+	{ 0x1daf, 0x1daf, 0x1daf, 0 },
+	{ 0x1e09, 0x1e09, 0x1e09, 0 },
+	{ 0x1e61, 0x1e61, 0x1e61, 0 },
+	{ 0x1f0f, 0x1f0f, 0x1f0f, 0 },
+	{ 0x1f65, 0x1f65, 0x1f65, 0 },
+	{ 0x1fb9, 0x1fb9, 0x1fb9, 0 },
+	{ 0x200d, 0x200d, 0x200d, 0 },
+	{ 0x2060, 0x2060, 0x2060, 0 },
+	{ 0x20b3, 0x20b3, 0x20b3, 0 },
+	{ 0x2104, 0x2104, 0x2104, 0 },
+	{ 0x21a5, 0x21a5, 0x21a5, 0 },
+	{ 0x2243, 0x2243, 0x2243, 0 },
+	{ 0x22de, 0x22de, 0x22de, 0 },
+	{ 0x2376, 0x2376, 0x2376, 0 },
+	{ 0x240c, 0x240c, 0x240c, 0 },
+	{ 0x24a0, 0x24a0, 0x24a0, 0 },
+	{ 0x2531, 0x2531, 0x2531, 0 },
+	{ 0x25c0, 0x25c0, 0x25c0, 0 },
+	{ 0x264c, 0x264c, 0x264c, 0 },
+	{ 0x26d7, 0x26d7, 0x26d7, 0 },
+	{ 0x2760, 0x2760, 0x2760, 0 },
+	{ 0x27e7, 0x27e7, 0x27e7, 0 },
+	{ 0x286c, 0x286c, 0x286c, 0 },
+	{ 0x28ef, 0x28ef, 0x28ef, 0 },
+	{ 0x2971, 0x2971, 0x2971, 0 },
+	{ 0x2a70, 0x2a70, 0x2a70, 0 },
+	{ 0x2b68, 0x2b68, 0x2b68, 0 },
+	{ 0x2be2, 0x2be2, 0x2be2, 0 },
+	{ 0x2c5a, 0x2c5a, 0x2c5a, 0 },
+	{ 0x2cd2, 0x2cd2, 0x2cd2, 0 },
+	{ 0x2d48, 0x2d48, 0x2d48, 0 },
+	{ 0x2e30, 0x2e30, 0x2e30, 0 },
+	{ 0x2ea2, 0x2ea2, 0x2ea2, 0 },
+	{ 0x2f13, 0x2f13, 0x2f13, 0 },
+	{ 0x2f83, 0x2f83, 0x2f83, 0 },
+	{ 0x2ff2, 0x2ff2, 0x2ff2, 0 },
+	{ 0x30cd, 0x30cd, 0x30cd, 0 },
+	{ 0x31a3, 0x31a3, 0x31a3, 0 },
+	{ 0x3276, 0x3276, 0x3276, 0 },
+	{ 0x3345, 0x3345, 0x3345, 0 },
+	{ 0x3410, 0x3410, 0x3410, 0 },
+	{ 0x34d8, 0x34d8, 0x34d8, 0 },
+	{ 0x359d, 0x359d, 0x359d, 0 },
+	{ 0x365e, 0x365e, 0x365e, 0 },
+	{ 0x36be, 0x36be, 0x36be, 0 },
+	{ 0x371d, 0x371d, 0x371d, 0 },
+	{ 0x37d9, 0x37d9, 0x37d9, 0 },
+	{ 0x3892, 0x3892, 0x3892, 0 },
+	{ 0x3949, 0x3949, 0x3949, 0 },
+	{ 0x39fd, 0x39fd, 0x39fd, 0 },
+	{ 0x3aaf, 0x3aaf, 0x3aaf, 0 },
+	{ 0x3b5e, 0x3b5e, 0x3b5e, 0 },
+	{ 0x3c0b, 0x3c0b, 0x3c0b, 0 },
+	{ 0x3cb6, 0x3cb6, 0x3cb6, 0 },
+	{ 0x3d5f, 0x3d5f, 0x3d5f, 0 },
+	{ 0x3e05, 0x3e05, 0x3e05, 0 },
+	{ 0x3eaa, 0x3eaa, 0x3eaa, 0 },
+	{ 0x3f4d, 0x3f4d, 0x3f4d, 0 },
+	{ 0x3fee, 0x3fee, 0x3fee, 0 },
+	{ 0x408d, 0x408d, 0x408d, 0 },
+	{ 0x412a, 0x412a, 0x412a, 0 },
+	{ 0x41c6, 0x41c6, 0x41c6, 0 },
+	{ 0x4260, 0x4260, 0x4260, 0 },
+	{ 0x42f9, 0x42f9, 0x42f9, 0 },
+	{ 0x4390, 0x4390, 0x4390, 0 },
+	{ 0x4425, 0x4425, 0x4425, 0 },
+	{ 0x44b9, 0x44b9, 0x44b9, 0 },
+	{ 0x454c, 0x454c, 0x454c, 0 },
+	{ 0x45dd, 0x45dd, 0x45dd, 0 },
+	{ 0x466d, 0x466d, 0x466d, 0 },
+	{ 0x46fb, 0x46fb, 0x46fb, 0 },
+	{ 0x4788, 0x4788, 0x4788, 0 },
+	{ 0x4814, 0x4814, 0x4814, 0 },
+	{ 0x489f, 0x489f, 0x489f, 0 },
+	{ 0x4928, 0x4928, 0x4928, 0 },
+	{ 0x4a38, 0x4a38, 0x4a38, 0 },
+	{ 0x4b43, 0x4b43, 0x4b43, 0 },
+	{ 0x4c49, 0x4c49, 0x4c49, 0 },
+	{ 0x4d4c, 0x4d4c, 0x4d4c, 0 },
+	{ 0x4dcc, 0x4dcc, 0x4dcc, 0 },
+	{ 0x4e4a, 0x4e4a, 0x4e4a, 0 },
+	{ 0x4ec8, 0x4ec8, 0x4ec8, 0 },
+	{ 0x4f45, 0x4f45, 0x4f45, 0 },
+	{ 0x4fc1, 0x4fc1, 0x4fc1, 0 },
+	{ 0x503c, 0x503c, 0x503c, 0 },
+	{ 0x50b7, 0x50b7, 0x50b7, 0 },
+	{ 0x5130, 0x5130, 0x5130, 0 },
+	{ 0x51a9, 0x51a9, 0x51a9, 0 },
+	{ 0x5220, 0x5220, 0x5220, 0 },
+	{ 0x530e, 0x530e, 0x530e, 0 },
+	{ 0x53f8, 0x53f8, 0x53f8, 0 },
+	{ 0x54df, 0x54df, 0x54df, 0 },
+	{ 0x55c3, 0x55c3, 0x55c3, 0 },
+	{ 0x56a4, 0x56a4, 0x56a4, 0 },
+	{ 0x5782, 0x5782, 0x5782, 0 },
+	{ 0x585e, 0x585e, 0x585e, 0 },
+	{ 0x58cb, 0x58cb, 0x58cb, 0 },
+	{ 0x5937, 0x5937, 0x5937, 0 },
+	{ 0x59a3, 0x59a3, 0x59a3, 0 },
+	{ 0x5a0e, 0x5a0e, 0x5a0e, 0 },
+	{ 0x5ae3, 0x5ae3, 0x5ae3, 0 },
+	{ 0x5bb5, 0x5bb5, 0x5bb5, 0 },
+	{ 0x5c85, 0x5c85, 0x5c85, 0 },
+	{ 0x5d52, 0x5d52, 0x5d52, 0 },
+	{ 0x5e1e, 0x5e1e, 0x5e1e, 0 },
+	{ 0x5ee8, 0x5ee8, 0x5ee8, 0 },
+	{ 0x5faf, 0x5faf, 0x5faf, 0 },
+	{ 0x6074, 0x6074, 0x6074, 0 },
+	{ 0x6138, 0x6138, 0x6138, 0 },
+	{ 0x61fa, 0x61fa, 0x61fa, 0 },
+	{ 0x62ba, 0x62ba, 0x62ba, 0 },
+	{ 0x6378, 0x6378, 0x6378, 0 },
+	{ 0x6434, 0x6434, 0x6434, 0 },
+	{ 0x64ef, 0x64ef, 0x64ef, 0 },
+	{ 0x65a8, 0x65a8, 0x65a8, 0 },
+	{ 0x6660, 0x6660, 0x6660, 0 },
+	{ 0x67ca, 0x67ca, 0x67ca, 0 },
+	{ 0x687d, 0x687d, 0x687d, 0 },
+	{ 0x692e, 0x692e, 0x692e, 0 },
+	{ 0x69de, 0x69de, 0x69de, 0 },
+	{ 0x6a8d, 0x6a8d, 0x6a8d, 0 },
+	{ 0x6be6, 0x6be6, 0x6be6, 0 },
+	{ 0x6d3a, 0x6d3a, 0x6d3a, 0 },
+	{ 0x6de2, 0x6de2, 0x6de2, 0 },
+	{ 0x6e88, 0x6e88, 0x6e88, 0 },
+	{ 0x6f2e, 0x6f2e, 0x6f2e, 0 },
+	{ 0x6fd2, 0x6fd2, 0x6fd2, 0 },
+	{ 0x7076, 0x7076, 0x7076, 0 },
+	{ 0x7118, 0x7118, 0x7118, 0 },
+	{ 0x71b9, 0x71b9, 0x71b9, 0 },
+	{ 0x7258, 0x7258, 0x7258, 0 },
+	{ 0x72f7, 0x72f7, 0x72f7, 0 },
+	{ 0x7395, 0x7395, 0x7395, 0 },
+	{ 0x7432, 0x7432, 0x7432, 0 },
+	{ 0x74cd, 0x74cd, 0x74cd, 0 },
+	{ 0x7602, 0x7602, 0x7602, 0 },
+	{ 0x7732, 0x7732, 0x7732, 0 },
+	{ 0x785f, 0x785f, 0x785f, 0 },
+	{ 0x7988, 0x7988, 0x7988, 0 },
+	{ 0x7aad, 0x7aad, 0x7aad, 0 },
+	{ 0x7bcf, 0x7bcf, 0x7bcf, 0 },
+	{ 0x7cee, 0x7cee, 0x7cee, 0 },
+	{ 0x7d7c, 0x7d7c, 0x7d7c, 0 },
+	{ 0x7e09, 0x7e09, 0x7e09, 0 },
+	{ 0x7e96, 0x7e96, 0x7e96, 0 },
+	{ 0x7f22, 0x7f22, 0x7f22, 0 },
+	{ 0x7fad, 0x7fad, 0x7fad, 0 },
+	{ 0x8037, 0x8037, 0x8037, 0 },
+	{ 0x80c1, 0x80c1, 0x80c1, 0 },
+	{ 0x814a, 0x814a, 0x814a, 0 },
+	{ 0x825a, 0x825a, 0x825a, 0 },
+	{ 0x8367, 0x8367, 0x8367, 0 },
+	{ 0x8471, 0x8471, 0x8471, 0 },
+	{ 0x8578, 0x8578, 0x8578, 0 },
+	{ 0x85fb, 0x85fb, 0x85fb, 0 },
+	{ 0x867d, 0x867d, 0x867d, 0 },
+	{ 0x86ff, 0x86ff, 0x86ff, 0 },
+	{ 0x8780, 0x8780, 0x8780, 0 },
+	{ 0x8880, 0x8880, 0x8880, 0 },
+	{ 0x897e, 0x897e, 0x897e, 0 },
+	{ 0x8a7a, 0x8a7a, 0x8a7a, 0 },
+	{ 0x8b73, 0x8b73, 0x8b73, 0 },
+	{ 0x8c6a, 0x8c6a, 0x8c6a, 0 },
+	{ 0x8d5f, 0x8d5f, 0x8d5f, 0 },
+	{ 0x8e52, 0x8e52, 0x8e52, 0 },
+	{ 0x8f42, 0x8f42, 0x8f42, 0 },
+	{ 0x9031, 0x9031, 0x9031, 0 },
+	{ 0x911e, 0x911e, 0x911e, 0 },
+	{ 0x9209, 0x9209, 0x9209, 0 },
+	{ 0x92f2, 0x92f2, 0x92f2, 0 },
+	{ 0x93d9, 0x93d9, 0x93d9, 0 },
+	{ 0x94bf, 0x94bf, 0x94bf, 0 },
+	{ 0x95a2, 0x95a2, 0x95a2, 0 },
+	{ 0x9684, 0x9684, 0x9684, 0 },
+	{ 0x9764, 0x9764, 0x9764, 0 },
+	{ 0x9843, 0x9843, 0x9843, 0 },
+	{ 0x9920, 0x9920, 0x9920, 0 },
+	{ 0x99fb, 0x99fb, 0x99fb, 0 },
+	{ 0x9a69, 0x9a69, 0x9a69, 0 },
+	{ 0x9ad5, 0x9ad5, 0x9ad5, 0 },
+	{ 0x9bae, 0x9bae, 0x9bae, 0 },
+	{ 0x9d5a, 0x9d5a, 0x9d5a, 0 },
+	{ 0x9e2e, 0x9e2e, 0x9e2e, 0 },
+	{ 0x9f00, 0x9f00, 0x9f00, 0 },
+	{ 0x9fd1, 0x9fd1, 0x9fd1, 0 },
+	{ 0xa0a1, 0xa0a1, 0xa0a1, 0 },
+	{ 0xa16f, 0xa16f, 0xa16f, 0 },
+	{ 0xa23d, 0xa23d, 0xa23d, 0 },
+	{ 0xa3d3, 0xa3d3, 0xa3d3, 0 },
+	{ 0xa49c, 0xa49c, 0xa49c, 0 },
+	{ 0xa564, 0xa564, 0xa564, 0 },
+	{ 0xa62b, 0xa62b, 0xa62b, 0 },
+	{ 0xa6f1, 0xa6f1, 0xa6f1, 0 },
+	{ 0xa7b5, 0xa7b5, 0xa7b5, 0 },
+	{ 0xa878, 0xa878, 0xa878, 0 },
+	{ 0xa93b, 0xa93b, 0xa93b, 0 },
+	{ 0xa9fc, 0xa9fc, 0xa9fc, 0 },
+	{ 0xaabb, 0xaabb, 0xaabb, 0 },
+	{ 0xab1b, 0xab1b, 0xab1b, 0 },
+	{ 0xab7a, 0xab7a, 0xab7a, 0 },
+	{ 0xac38, 0xac38, 0xac38, 0 },
+	{ 0xacf5, 0xacf5, 0xacf5, 0 },
+	{ 0xadb1, 0xadb1, 0xadb1, 0 },
+	{ 0xae6b, 0xae6b, 0xae6b, 0 },
+	{ 0xafde, 0xafde, 0xafde, 0 },
+	{ 0xb14c, 0xb14c, 0xb14c, 0 },
+	{ 0xb2b7, 0xb2b7, 0xb2b7, 0 },
+	{ 0xb41e, 0xb41e, 0xb41e, 0 },
+	{ 0xb581, 0xb581, 0xb581, 0 },
+	{ 0xb631, 0xb631, 0xb631, 0 },
+	{ 0xb6e1, 0xb6e1, 0xb6e1, 0 },
+	{ 0xb790, 0xb790, 0xb790, 0 },
+	{ 0xb83d, 0xb83d, 0xb83d, 0 },
+	{ 0xb997, 0xb997, 0xb997, 0 },
+	{ 0xbaed, 0xbaed, 0xbaed, 0 },
+	{ 0xbc3f, 0xbc3f, 0xbc3f, 0 },
+	{ 0xbce8, 0xbce8, 0xbce8, 0 },
+	{ 0xbd8f, 0xbd8f, 0xbd8f, 0 },
+	{ 0xbedc, 0xbedc, 0xbedc, 0 },
+	{ 0xc025, 0xc025, 0xc025, 0 },
+	{ 0xc0c9, 0xc0c9, 0xc0c9, 0 },
+	{ 0xc16c, 0xc16c, 0xc16c, 0 },
+	{ 0xc20f, 0xc20f, 0xc20f, 0 },
+	{ 0xc2b0, 0xc2b0, 0xc2b0, 0 },
+	{ 0xc352, 0xc352, 0xc352, 0 },
+	{ 0xc3f2, 0xc3f2, 0xc3f2, 0 },
+	{ 0xc531, 0xc531, 0xc531, 0 },
+	{ 0xc66d, 0xc66d, 0xc66d, 0 },
+	{ 0xc7a6, 0xc7a6, 0xc7a6, 0 },
+	{ 0xc842, 0xc842, 0xc842, 0 },
+	{ 0xc8dd, 0xc8dd, 0xc8dd, 0 },
+	{ 0xc978, 0xc978, 0xc978, 0 },
+	{ 0xca12, 0xca12, 0xca12, 0 },
+	{ 0xcb44, 0xcb44, 0xcb44, 0 },
+	{ 0xcc74, 0xcc74, 0xcc74, 0 },
+	{ 0xcda2, 0xcda2, 0xcda2, 0 },
+	{ 0xcecd, 0xcecd, 0xcecd, 0 },
+	{ 0xcff6, 0xcff6, 0xcff6, 0 },
+	{ 0xd08a, 0xd08a, 0xd08a, 0 },
+	{ 0xd11d, 0xd11d, 0xd11d, 0 },
+	{ 0xd242, 0xd242, 0xd242, 0 },
+	{ 0xd365, 0xd365, 0xd365, 0 },
+	{ 0xd486, 0xd486, 0xd486, 0 },
+	{ 0xd5a4, 0xd5a4, 0xd5a4, 0 },
+	{ 0xd633, 0xd633, 0xd633, 0 },
+	{ 0xd6c1, 0xd6c1, 0xd6c1, 0 },
+	{ 0xd7dc, 0xd7dc, 0xd7dc, 0 },
+	{ 0xd8f5, 0xd8f5, 0xd8f5, 0 },
+	{ 0xda0c, 0xda0c, 0xda0c, 0 },
+	{ 0xda97, 0xda97, 0xda97, 0 },
+	{ 0xdb21, 0xdb21, 0xdb21, 0 },
+	{ 0xdc35, 0xdc35, 0xdc35, 0 },
+	{ 0xdd47, 0xdd47, 0xdd47, 0 },
+	{ 0xde57, 0xde57, 0xde57, 0 },
+	{ 0xdf65, 0xdf65, 0xdf65, 0 },
+	{ 0xe072, 0xe072, 0xe072, 0 },
+	{ 0xe17d, 0xe17d, 0xe17d, 0 },
+	{ 0xe286, 0xe286, 0xe286, 0 },
+	{ 0xe38e, 0xe38e, 0xe38e, 0 },
+	{ 0xe494, 0xe494, 0xe494, 0 },
+	{ 0xe599, 0xe599, 0xe599, 0 },
+	{ 0xe69c, 0xe69c, 0xe69c, 0 },
+	{ 0xe79e, 0xe79e, 0xe79e, 0 },
+	{ 0xe89e, 0xe89e, 0xe89e, 0 },
+	{ 0xe99d, 0xe99d, 0xe99d, 0 },
+	{ 0xea9a, 0xea9a, 0xea9a, 0 },
+	{ 0xeb96, 0xeb96, 0xeb96, 0 },
+	{ 0xec90, 0xec90, 0xec90, 0 },
+	{ 0xed8a, 0xed8a, 0xed8a, 0 },
+	{ 0xee82, 0xee82, 0xee82, 0 },
+	{ 0xef78, 0xef78, 0xef78, 0 },
+	{ 0xf06d, 0xf06d, 0xf06d, 0 },
+	{ 0xf0e7, 0xf0e7, 0xf0e7, 0 },
+	{ 0xf161, 0xf161, 0xf161, 0 },
+	{ 0xf254, 0xf254, 0xf254, 0 },
+	{ 0xf345, 0xf345, 0xf345, 0 },
+	{ 0xf3be, 0xf3be, 0xf3be, 0 },
+	{ 0xf435, 0xf435, 0xf435, 0 },
+	{ 0xf4ad, 0xf4ad, 0xf4ad, 0 },
+	{ 0xf524, 0xf524, 0xf524, 0 },
+	{ 0xf560, 0xf560, 0xf560, 0 },
+	{ 0xf59b, 0xf59b, 0xf59b, 0 },
+	{ 0xf612, 0xf612, 0xf612, 0 },
+	{ 0xf6ff, 0xf6ff, 0xf6ff, 0 },
+	{ 0xf8d5, 0xf8d5, 0xf8d5, 0 },
+	{ 0xfaa6, 0xfaa6, 0xfaa6, 0 },
+	{ 0xfb8d, 0xfb8d, 0xfb8d, 0 },
+	{ 0xfc72, 0xfc72, 0xfc72, 0 },
+	{ 0xfd57, 0xfd57, 0xfd57, 0 },
+	{ 0xfe3b, 0xfe3b, 0xfe3b, 0 },
+	{ 0xff1d, 0xff1d, 0xff1d, 0 },
+	{ 0xffff, 0xffff, 0xffff, 0 }
 };
 
-static_assert(ARRAY_SIZE(linear_x) == ARRAY_SIZE(srgb_inv_y),
+static_assert(ARRAY_SIZE(srgb_inv_x) == ARRAY_SIZE(srgb_inv_y),
 	      "srgb_inv x and y must have the same number of entries");
 
 const struct vkms_color_lut srgb_inv_eotf = {
-	.x = linear_x,
+	.x = srgb_inv_x,
 	.y = srgb_inv_y,
 	.lut_length = ARRAY_SIZE(srgb_inv_y)
 };
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 00/11] drm/vkms: improve color curve LUTs precision
  2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
                   ` (10 preceding siblings ...)
  2026-08-04 20:33 ` [PATCH 11/11] drm/vkms: replace uniform sRGB LUT and its inverse with optimal ones Leandro Ribeiro
@ 2026-11-04  9:15 ` Robert Mader
  11 siblings, 0 replies; 13+ messages in thread
From: Robert Mader @ 2026-11-04  9:15 UTC (permalink / raw)
  To: Leandro Ribeiro, louis.chauvet
  Cc: airlied, alex.hung, daniel, hamohammed.sa, harry.wentland,
	maarten.lankhorst, melissa.srw, mripard, pekka.paalanen, simona,
	tzimmermann, dri-devel, linux-kernel

Thanks a lot for the series!

I'm using it as part of a branch [1] to test my/our Weston post-blend 
color-op pipeline implementation [2] - including additional commits 
using the new script to add gamma 2.2 and 2.4 [3] curves. The Weston 
tests compare the vkms output against the GL renderer for e.g. NV12 
bt709-limited-range bt1886/gamma2.4 content - successfully (i.e. the 
results match very closely).

I also went through the commits and didn't spot any more issues. Thus 
with the 32bit issues raised by the bot addressed the whole series is:

Reviewed-by: Robert Mader <robert.mader@collabora.com>

Tested-by: Robert Mader <robert.mader@collabora.com>

Best regards,

Robert

---

1: https://gitlab.com/rmader/linux/-/commits/post-blend-2026-11-02

2: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/2157

3: For SDR video, see 
https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/539

On 04.08.26 22:33, Leandro Ribeiro wrote:
> Currently VKMS only supports uniform LUTs. Besides the userspace
> provided LUTs, it also represents color curves with LUTs, as it can't
> simply implement the continuous curve formulas (floating-point would be
> required).
>
> This adds support to represent color curves with non-uniform LUTs,
> improving the precision significantly. It also provides a script
> that was used to create these LUTs.
>
> The optimized LUTs use more samples in regions where the interpolation
> and quantization errors are higher (usually in regions with a steeper
> slope). The LUT generator script compares different strategies and uses
> greedy subdivision, which outperformed the other methods, to generate
> the optimized LUTs for VKMS.
>
> Leandro Ribeiro (11):
>    drm/vkms: rename VKMS_LUT_SIZE to VKMS_GAMMA_LUT_SIZE
>    drm/vkms: allow color curve LUTs to have different sizes
>    drm/vkms: remove TEST_LUT_SIZE
>    drm/vkms: remove linear_eotf
>    drm/vkms: improve the way in which we access LUT member
>    drm/vkms: rename struct vkms_color_lut::base to y
>    drm/vkms: rename get_lut_index() to get_uniform_lut_index()
>    drm/vkms: add support to non-uniform LUT for internal color curves
>    drm/vkms: test sRGB and inverse sRGB LUTs using more samples
>    drm/vkms: add script to create optimized LUTs for color curves
>    drm/vkms: replace uniform sRGB LUT and its inverse with optimal ones
>
>   .../gpu/drm/vkms/scripts/color-curve-lut.py   |  561 +++++++
>   drivers/gpu/drm/vkms/tests/vkms_color_test.c  |   46 +-
>   drivers/gpu/drm/vkms/vkms_composer.c          |  127 +-
>   drivers/gpu/drm/vkms/vkms_composer.h          |    2 +-
>   drivers/gpu/drm/vkms/vkms_crtc.c              |    4 +-
>   drivers/gpu/drm/vkms/vkms_drv.c               |    2 +-
>   drivers/gpu/drm/vkms/vkms_drv.h               |   13 +-
>   drivers/gpu/drm/vkms/vkms_luts.c              | 1469 ++++++++---------
>   drivers/gpu/drm/vkms/vkms_luts.h              |    3 -
>   9 files changed, 1383 insertions(+), 844 deletions(-)
>   create mode 100644 drivers/gpu/drm/vkms/scripts/color-curve-lut.py
>
-- 
Robert Mader
Consultant Software Developer

Collabora Ltd.
Platinum Building, St John's Innovation Park, Cambridge CB4 0DS, UK
Registered in England & Wales, no. 5513718


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-11-04  9:16 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-04 20:33 [PATCH 00/11] drm/vkms: improve color curve LUTs precision Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 01/11] drm/vkms: rename VKMS_LUT_SIZE to VKMS_GAMMA_LUT_SIZE Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 02/11] drm/vkms: allow color curve LUTs to have different sizes Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 03/11] drm/vkms: remove TEST_LUT_SIZE Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 04/11] drm/vkms: remove linear_eotf Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 05/11] drm/vkms: improve the way in which we access LUT member Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 06/11] drm/vkms: rename struct vkms_color_lut::base to y Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 07/11] drm/vkms: rename get_lut_index() to get_uniform_lut_index() Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 08/11] drm/vkms: add support to non-uniform LUT for internal color curves Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 09/11] drm/vkms: test sRGB and inverse sRGB LUTs using more samples Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 10/11] drm/vkms: add script to create optimized LUTs for color curves Leandro Ribeiro
2026-08-04 20:33 ` [PATCH 11/11] drm/vkms: replace uniform sRGB LUT and its inverse with optimal ones Leandro Ribeiro
2026-11-04  9:15 ` [PATCH 00/11] drm/vkms: improve color curve LUTs precision Robert Mader

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®