Commit deb879fa authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'drm-next-2025-12-05' of https://gitlab.freedesktop.org/drm/kernel

Pull more drm updates from Dave Airlie:
 "There was some additional intel code for color operations we wanted to
  land. However I discovered I missed a pull for the xe vfio driver
  which I had sorted into 6.20 in my brain, until Thomas mentioned it.

  This contains the xe vfio code, a bunch of xe fixes that were waiting
  and the i915 color management support. I'd like to include it as part
  of keeping the two main vendors on the same page and giving a good
  cross-driver experience for userspace when it starts using it.

  vfio:
   - add a vfio_pci variant driver for Intel

  xe/i915 display:
   - add plane color management support

  xe:
   - Add scope-based cleanup helper for runtime PM
   - vfio xe driver prerequisites and exports
   - fix vfio link error
   - Fix a memory leak
   - Fix a 64-bit division
   - vf migration fix
   - LRC pause fix"

* tag 'drm-next-2025-12-05' of https://gitlab.freedesktop.org/drm/kernel: (25 commits)
  drm/i915/color: Enable Plane Color Pipelines
  drm/i915/color: Add 3D LUT to color pipeline
  drm/i915/color: Add registers for 3D LUT
  drm/i915/color: Program Plane Post CSC Registers
  drm/i915/color: Program Pre-CSC registers
  drm/i915/color: Add framework to program PRE/POST CSC LUT
  drm/i915: Add register definitions for Plane Post CSC
  drm/i915: Add register definitions for Plane Degamma
  drm/i915/color: Add plane CTM callback for D12 and beyond
  drm/i915/color: Preserve sign bit when int_bits is Zero
  drm/i915/color: Add framework to program CSC
  drm/i915/color: Create a transfer function color pipeline
  drm/i915/color: Add helper to create intel colorop
  drm/i915: Add intel_color_op
  drm/i915/display: Add identifiers for driver specific blocks
  drm/xe/pf: fix VFIO link error
  drm/xe: Protect against unset LRC when pausing submissions
  drm/xe/vf: Start re-emission from first unsignaled job during VF migration
  drm/xe/pf: Use div_u64 when calculating GGTT profile
  drm/xe: Fix memory leak when handling pagefault vma
  ...
parents 028bd4a1 c7685d11
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -27221,6 +27221,13 @@ L: virtualization@lists.linux.dev
S:	Maintained
F:	drivers/vfio/pci/virtio
VFIO XE PCI DRIVER
M:	Michał Winiarski <michal.winiarski@intel.com>
L:	kvm@vger.kernel.org
L:	intel-xe@lists.freedesktop.org
S:	Supported
F:	drivers/vfio/pci/xe
VGA_SWITCHEROO
R:	Lukas Wunner <lukas@wunner.de>
S:	Maintained
+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);
}
Loading