Commit c7685d11 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'topic/drm-intel-plane-color-pipeline-2025-12-04' of...

Merge tag 'topic/drm-intel-plane-color-pipeline-2025-12-04' of https://gitlab.freedesktop.org/drm/i915/kernel

 into drm-next

drm/i915 topic pull request for v6.19:

Features and functionality:
- Add plane color management support (Uma, Chaitanya)

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
From: Jani Nikula <jani.nikula@intel.com>
Link: https://patch.msgid.link/e7129c6afd6208719d2f5124da86e810505e7a7b@intel.com
parents 86fafc58 860daa4b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -239,6 +239,8 @@ i915-y += \
	display/intel_cdclk.o \
	display/intel_cmtg.o \
	display/intel_color.o \
	display/intel_colorop.o \
	display/intel_color_pipeline.o \
	display/intel_combo_phy.o \
	display/intel_connector.o \
	display/intel_crtc.o \
+335 −0
Original line number Diff line number Diff line
@@ -32,6 +32,8 @@
#include "intel_display_utils.h"
#include "intel_dsb.h"
#include "intel_vrr.h"
#include "skl_universal_plane.h"
#include "skl_universal_plane_regs.h"

struct intel_color_funcs {
	int (*color_check)(struct intel_atomic_state *state,
@@ -87,6 +89,14 @@ struct intel_color_funcs {
	 * Read config other than LUTs and CSCs, before them. Optional.
	 */
	void (*get_config)(struct intel_crtc_state *crtc_state);

	/* Plane CSC*/
	void (*load_plane_csc_matrix)(struct intel_dsb *dsb,
				      const struct intel_plane_state *plane_state);

	/* Plane Pre/Post CSC */
	void (*load_plane_luts)(struct intel_dsb *dsb,
				const struct intel_plane_state *plane_state);
};

#define CTM_COEFF_SIGN	(1ULL << 63)
@@ -609,6 +619,8 @@ static u16 ctm_to_twos_complement(u64 coeff, int int_bits, int frac_bits)
	if (CTM_COEFF_NEGATIVE(coeff))
		c = -c;

	int_bits = max(int_bits, 1);

	c = clamp(c, -(s64)BIT(int_bits + frac_bits - 1),
		  (s64)(BIT(int_bits + frac_bits - 1) - 1));

@@ -3836,6 +3848,266 @@ static void icl_read_luts(struct intel_crtc_state *crtc_state)
	}
}

static void
xelpd_load_plane_csc_matrix(struct intel_dsb *dsb,
			    const struct intel_plane_state *plane_state)
{
	struct intel_display *display = to_intel_display(plane_state);
	const struct drm_plane_state *state = &plane_state->uapi;
	enum pipe pipe = to_intel_plane(state->plane)->pipe;
	enum plane_id plane = to_intel_plane(state->plane)->id;
	const struct drm_property_blob *blob = plane_state->hw.ctm;
	struct drm_color_ctm_3x4 *ctm;
	const u64 *input;
	u16 coeffs[9] = {};
	int i, j;

	if (!icl_is_hdr_plane(display, plane) || !blob)
		return;

	ctm = blob->data;
	input = ctm->matrix;

	/*
	 * Convert fixed point S31.32 input to format supported by the
	 * hardware.
	 */
	for (i = 0, j = 0; i < ARRAY_SIZE(coeffs); i++) {
		u64 abs_coeff = ((1ULL << 63) - 1) & input[j];

		/*
		 * Clamp input value to min/max supported by
		 * hardware.
		 */
		abs_coeff = clamp_val(abs_coeff, 0, CTM_COEFF_4_0 - 1);

		/* sign bit */
		if (CTM_COEFF_NEGATIVE(input[j]))
			coeffs[i] |= 1 << 15;

		if (abs_coeff < CTM_COEFF_0_125)
			coeffs[i] |= (3 << 12) |
				      ILK_CSC_COEFF_FP(abs_coeff, 12);
		else if (abs_coeff < CTM_COEFF_0_25)
			coeffs[i] |= (2 << 12) |
				      ILK_CSC_COEFF_FP(abs_coeff, 11);
		else if (abs_coeff < CTM_COEFF_0_5)
			coeffs[i] |= (1 << 12) |
				      ILK_CSC_COEFF_FP(abs_coeff, 10);
		else if (abs_coeff < CTM_COEFF_1_0)
			coeffs[i] |= ILK_CSC_COEFF_FP(abs_coeff, 9);
		else if (abs_coeff < CTM_COEFF_2_0)
			coeffs[i] |= (7 << 12) |
				      ILK_CSC_COEFF_FP(abs_coeff, 8);
		else
			coeffs[i] |= (6 << 12) |
				      ILK_CSC_COEFF_FP(abs_coeff, 7);

		/* Skip postoffs */
		if (!((j + 2) % 4))
			j += 2;
		else
			j++;
	}

	intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 0),
			   coeffs[0] << 16 | coeffs[1]);
	intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 1),
			   coeffs[2] << 16);

	intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 2),
			   coeffs[3] << 16 | coeffs[4]);
	intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 3),
			   coeffs[5] << 16);

	intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 4),
			   coeffs[6] << 16 | coeffs[7]);
	intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 5),
			   coeffs[8] << 16);

	intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane, 0), 0);
	intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane, 1), 0);
	intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane, 2), 0);

	/*
	 * Conversion from S31.32 to S0.12. BIT[12] is the signed bit
	 */
	intel_de_write_dsb(display, dsb,
			   PLANE_CSC_POSTOFF(pipe, plane, 0),
			   ctm_to_twos_complement(input[3], 0, 12));
	intel_de_write_dsb(display, dsb,
			   PLANE_CSC_POSTOFF(pipe, plane, 1),
			   ctm_to_twos_complement(input[7], 0, 12));
	intel_de_write_dsb(display, dsb,
			   PLANE_CSC_POSTOFF(pipe, plane, 2),
			   ctm_to_twos_complement(input[11], 0, 12));
}

static void
xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb,
				const struct intel_plane_state *plane_state)
{
	struct intel_display *display = to_intel_display(plane_state);
	const struct drm_plane_state *state = &plane_state->uapi;
	enum pipe pipe = to_intel_plane(state->plane)->pipe;
	enum plane_id plane = to_intel_plane(state->plane)->id;
	const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data;
	u32 i, lut_size;

	if (icl_is_hdr_plane(display, plane)) {
		lut_size = 128;

		intel_de_write_dsb(display, dsb,
				   PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
				   PLANE_PAL_PREC_AUTO_INCREMENT);

		if (pre_csc_lut) {
			for (i = 0; i < lut_size; i++) {
				u32 lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24);

				intel_de_write_dsb(display, dsb,
						   PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
						   lut_val);
			}

			/* Program the max register to clamp values > 1.0. */
			/* TODO: Restrict to 0x7ffffff */
			do {
				intel_de_write_dsb(display, dsb,
						   PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
						   (1 << 24));
			} while (i++ > 130);
		} else {
			for (i = 0; i < lut_size; i++) {
				u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);

				intel_de_write_dsb(display, dsb,
						   PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
			}

			do {
				intel_de_write_dsb(display, dsb,
						   PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
						   1 << 24);
			} while (i++ < 130);
		}

		intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
	}
}

static void
xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb,
				 const struct intel_plane_state *plane_state)
{
	struct intel_display *display = to_intel_display(plane_state);
	const struct drm_plane_state *state = &plane_state->uapi;
	enum pipe pipe = to_intel_plane(state->plane)->pipe;
	enum plane_id plane = to_intel_plane(state->plane)->id;
	const struct drm_color_lut32 *post_csc_lut = plane_state->hw.gamma_lut->data;
	u32 i, lut_size, lut_val;

	if (icl_is_hdr_plane(display, plane)) {
		intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
				   PLANE_PAL_PREC_AUTO_INCREMENT);
		/* TODO: Add macro */
		intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0),
				   PLANE_PAL_PREC_AUTO_INCREMENT);
		if (post_csc_lut) {
			lut_size = 32;
			for (i = 0; i < lut_size; i++) {
				lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24);

				intel_de_write_dsb(display, dsb,
						   PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
						   lut_val);
			}

			/* Segment 2 */
			do {
				intel_de_write_dsb(display, dsb,
						   PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
						   (1 << 24));
			} while (i++ < 34);
		} else {
			/*TODO: Add for segment 0 */
			lut_size = 32;
			for (i = 0; i < lut_size; i++) {
				u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);

				intel_de_write_dsb(display, dsb,
						   PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
			}

			do {
				intel_de_write_dsb(display, dsb,
						   PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
						   1 << 24);
			} while (i++ < 34);
		}

		intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
		intel_de_write_dsb(display, dsb,
				   PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0);
	}
}

static void
xelpd_plane_load_luts(struct intel_dsb *dsb, const struct intel_plane_state *plane_state)
{
	if (plane_state->hw.degamma_lut)
		xelpd_program_plane_pre_csc_lut(dsb, plane_state);

	if (plane_state->hw.gamma_lut)
		xelpd_program_plane_post_csc_lut(dsb, plane_state);
}

static u32 glk_3dlut_10(const struct drm_color_lut32 *color)
{
	return REG_FIELD_PREP(LUT_3D_DATA_RED_MASK, drm_color_lut32_extract(color->red, 10)) |
		REG_FIELD_PREP(LUT_3D_DATA_GREEN_MASK, drm_color_lut32_extract(color->green, 10)) |
		REG_FIELD_PREP(LUT_3D_DATA_BLUE_MASK, drm_color_lut32_extract(color->blue, 10));
}

static void glk_load_lut_3d(struct intel_dsb *dsb,
			    struct intel_crtc *crtc,
			    const struct drm_property_blob *blob)
{
	struct intel_display *display = to_intel_display(crtc->base.dev);
	const struct drm_color_lut32 *lut = blob->data;
	int i, lut_size = drm_color_lut32_size(blob);
	enum pipe pipe = crtc->pipe;

	if (!dsb && intel_de_read(display, LUT_3D_CTL(pipe)) & LUT_3D_READY) {
		drm_err(display->drm, "[CRTC:%d:%s] 3D LUT not ready, not loading LUTs\n",
			crtc->base.base.id, crtc->base.name);
		return;
	}

	intel_de_write_dsb(display, dsb, LUT_3D_INDEX(pipe), LUT_3D_AUTO_INCREMENT);
	for (i = 0; i < lut_size; i++)
		intel_de_write_dsb(display, dsb, LUT_3D_DATA(pipe), glk_3dlut_10(&lut[i]));
	intel_de_write_dsb(display, dsb, LUT_3D_INDEX(pipe), 0);
}

static void glk_lut_3d_commit(struct intel_dsb *dsb, struct intel_crtc *crtc, bool enable)
{
	struct intel_display *display = to_intel_display(crtc);
	enum pipe pipe = crtc->pipe;
	u32 val = 0;

	if (!dsb && intel_de_read(display, LUT_3D_CTL(pipe)) & LUT_3D_READY) {
		drm_err(display->drm, "[CRTC:%d:%s] 3D LUT not ready, not committing change\n",
			crtc->base.base.id, crtc->base.name);
		return;
	}

	if (enable)
		val = LUT_3D_ENABLE | LUT_3D_READY | LUT_3D_BIND_PLANE_1;

	intel_de_write_dsb(display, dsb, LUT_3D_CTL(pipe), val);
}

static const struct intel_color_funcs chv_color_funcs = {
	.color_check = chv_color_check,
	.color_commit_arm = i9xx_color_commit_arm,
@@ -3883,6 +4155,8 @@ static const struct intel_color_funcs tgl_color_funcs = {
	.lut_equal = icl_lut_equal,
	.read_csc = icl_read_csc,
	.get_config = skl_get_config,
	.load_plane_csc_matrix = xelpd_load_plane_csc_matrix,
	.load_plane_luts = xelpd_plane_load_luts,
};

static const struct intel_color_funcs icl_color_funcs = {
@@ -3963,6 +4237,67 @@ static const struct intel_color_funcs ilk_color_funcs = {
	.get_config = ilk_get_config,
};

void intel_color_plane_commit_arm(struct intel_dsb *dsb,
				  const struct intel_plane_state *plane_state)
{
	struct intel_display *display = to_intel_display(plane_state);
	struct intel_crtc *crtc = to_intel_crtc(plane_state->uapi.crtc);

	if (crtc && intel_color_crtc_has_3dlut(display, crtc->pipe))
		glk_lut_3d_commit(dsb, crtc, !!plane_state->hw.lut_3d);
}

static void
intel_color_load_plane_csc_matrix(struct intel_dsb *dsb,
				  const struct intel_plane_state *plane_state)
{
	struct intel_display *display = to_intel_display(plane_state);

	if (display->funcs.color->load_plane_csc_matrix)
		display->funcs.color->load_plane_csc_matrix(dsb, plane_state);
}

static void
intel_color_load_plane_luts(struct intel_dsb *dsb,
			    const struct intel_plane_state *plane_state)
{
	struct intel_display *display = to_intel_display(plane_state);

	if (display->funcs.color->load_plane_luts)
		display->funcs.color->load_plane_luts(dsb, plane_state);
}

bool
intel_color_crtc_has_3dlut(struct intel_display *display, enum pipe pipe)
{
	if (DISPLAY_VER(display) >= 12)
		return pipe == PIPE_A || pipe == PIPE_B;
	else
		return false;
}

static void
intel_color_load_3dlut(struct intel_dsb *dsb,
		       const struct intel_plane_state *plane_state)
{
	struct intel_display *display = to_intel_display(plane_state);
	struct intel_crtc *crtc = to_intel_crtc(plane_state->uapi.crtc);

	if (crtc && intel_color_crtc_has_3dlut(display, crtc->pipe))
		glk_load_lut_3d(dsb, crtc, plane_state->hw.lut_3d);
}

void intel_color_plane_program_pipeline(struct intel_dsb *dsb,
					const struct intel_plane_state *plane_state)
{
	if (plane_state->hw.ctm)
		intel_color_load_plane_csc_matrix(dsb, plane_state);
	if (plane_state->hw.degamma_lut || plane_state->hw.gamma_lut)
		intel_color_load_plane_luts(dsb, plane_state);
	if (plane_state->hw.lut_3d)
		intel_color_load_3dlut(dsb, plane_state);
}

void intel_color_crtc_init(struct intel_crtc *crtc)
{
	struct intel_display *display = to_intel_display(crtc);
+7 −1
Original line number Diff line number Diff line
@@ -13,7 +13,9 @@ struct intel_crtc_state;
struct intel_crtc;
struct intel_display;
struct intel_dsb;
struct intel_plane_state;
struct drm_property_blob;
enum pipe;

void intel_color_init_hooks(struct intel_display *display);
int intel_color_init(struct intel_display *display);
@@ -40,5 +42,9 @@ bool intel_color_lut_equal(const struct intel_crtc_state *crtc_state,
			   const struct drm_property_blob *blob2,
			   bool is_pre_csc_lut);
void intel_color_assert_luts(const struct intel_crtc_state *crtc_state);

void intel_color_plane_program_pipeline(struct intel_dsb *dsb,
					const struct intel_plane_state *plane_state);
void intel_color_plane_commit_arm(struct intel_dsb *dsb,
				  const struct intel_plane_state *plane_state);
bool intel_color_crtc_has_3dlut(struct intel_display *display, enum pipe pipe);
#endif /* __INTEL_COLOR_H__ */
+99 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2025 Intel Corporation
 */
#include "intel_color.h"
#include "intel_colorop.h"
#include "intel_color_pipeline.h"
#include "intel_de.h"
#include "intel_display_types.h"
#include "skl_universal_plane.h"

#define MAX_COLOR_PIPELINES 1
#define PLANE_DEGAMMA_SIZE 128
#define PLANE_GAMMA_SIZE 32

static
int _intel_color_pipeline_plane_init(struct drm_plane *plane, struct drm_prop_enum_list *list,
				     enum pipe pipe)
{
	struct drm_device *dev = plane->dev;
	struct intel_display *display = to_intel_display(dev);
	struct drm_colorop *prev_op;
	struct intel_colorop *colorop;
	int ret;

	colorop = intel_colorop_create(INTEL_PLANE_CB_PRE_CSC_LUT);

	ret = drm_plane_colorop_curve_1d_lut_init(dev, &colorop->base, plane,
						  PLANE_DEGAMMA_SIZE,
						  DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
						  DRM_COLOROP_FLAG_ALLOW_BYPASS);

	if (ret)
		return ret;

	list->type = colorop->base.base.id;
	list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", colorop->base.base.id);

	/* TODO: handle failures and clean up */
	prev_op = &colorop->base;

	if (DISPLAY_VER(display) >= 35 &&
	    intel_color_crtc_has_3dlut(display, pipe) &&
	    plane->type == DRM_PLANE_TYPE_PRIMARY) {
		colorop = intel_colorop_create(INTEL_PLANE_CB_3DLUT);

		ret = drm_plane_colorop_3dlut_init(dev, &colorop->base, plane, 17,
						   DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
						   true);
		if (ret)
			return ret;

		drm_colorop_set_next_property(prev_op, &colorop->base);

		prev_op = &colorop->base;
	}

	colorop = intel_colorop_create(INTEL_PLANE_CB_CSC);
	ret = drm_plane_colorop_ctm_3x4_init(dev, &colorop->base, plane,
					     DRM_COLOROP_FLAG_ALLOW_BYPASS);
	if (ret)
		return ret;

	drm_colorop_set_next_property(prev_op, &colorop->base);
	prev_op = &colorop->base;

	colorop = intel_colorop_create(INTEL_PLANE_CB_POST_CSC_LUT);
	ret = drm_plane_colorop_curve_1d_lut_init(dev, &colorop->base, plane,
						  PLANE_GAMMA_SIZE,
						  DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
						  DRM_COLOROP_FLAG_ALLOW_BYPASS);
	if (ret)
		return ret;

	drm_colorop_set_next_property(prev_op, &colorop->base);

	return 0;
}

int intel_color_pipeline_plane_init(struct drm_plane *plane, enum pipe pipe)
{
	struct drm_device *dev = plane->dev;
	struct intel_display *display = to_intel_display(dev);
	struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
	int len = 0;
	int ret;

	/* Currently expose pipeline only for HDR planes */
	if (!icl_is_hdr_plane(display, to_intel_plane(plane)->id))
		return 0;

	/* Add pipeline consisting of transfer functions */
	ret = _intel_color_pipeline_plane_init(plane, &pipelines[len], pipe);
	if (ret)
		return ret;
	len++;

	return drm_plane_create_color_pipeline_property(plane, pipelines, len);
}
+14 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: MIT */
/*
 * Copyright © 2025 Intel Corporation
 */

#ifndef __INTEL_COLOR_PIPELINE_H__
#define __INTEL_COLOR_PIPELINE_H__

struct drm_plane;
enum pipe;

int intel_color_pipeline_plane_init(struct drm_plane *plane, enum pipe pipe);

#endif /* __INTEL_COLOR_PIPELINE_H__ */
Loading