Revision faa0fcf943d2d53992b3c73752df9a0547cb4797 authored by James Jones on 30 July 2020, 23:58:23 UTC, committed by Dave Airlie on 31 July 2020, 02:24:51 UTC
Accept the DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK()
family of modifiers to handle broken userspace
Xorg modesetting and Mesa drivers. Existing Mesa
drivers are still aware of only these older
format modifiers which do not differentiate
between different variations of the block linear
layout. When the format modifier support flag was
flipped in the nouveau kernel driver, the X.org
modesetting driver began attempting to use its
format modifier-enabled framebuffer path. Because
the set of format modifiers advertised by the
kernel prior to this change do not intersect with
the set of format modifiers advertised by Mesa,
allocating GBM buffers using format modifiers
fails and the modesetting driver falls back to
non-modifier allocation. However, it still later
queries the modifier of the GBM buffer when
creating its DRM-KMS framebuffer object, receives
the old-format modifier from Mesa, and attempts
to create a framebuffer with it. Since the kernel
is still not aware of these formats, this fails.

Userspace should not be attempting to query format
modifiers of GBM buffers allocated with a non-
format-modifier-aware allocation path, but to
avoid breaking existing userspace behavior, this
change accepts the old-style format modifiers when
creating framebuffers and applying them to planes
by translating them to the equivalent new-style
modifier. To accomplish this, some layout
parameters must be assumed to match properties of
the device targeted by the relevant ioctls. To
avoid perpetuating misuse of the old-style
modifiers, this change does not advertise support
for them. Doing so would imply compatibility
between devices with incompatible memory layouts.

Tested with Xorg 1.20 modesetting driver,
weston@c46c70dac84a4b3030cd05b380f9f410536690fc,
gnome & KDE wayland desktops from Ubuntu 18.04,
and sway 1.5

Reported-by: Kirill A. Shutemov <kirill@shutemov.name>
Fixes: fa4f4c213f5f ("drm/nouveau/kms: Support NVIDIA format modifiers")
Link: https://lkml.org/lkml/2020/6/30/1251
Signed-off-by: James Jones <jajones@nvidia.com>
Acked-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
1 parent a4a2739
Raw File
ranges.c
// SPDX-License-Identifier: GPL-2.0
/*
 * ranges.c: Handle ranges in newer proms for obio/sbus.
 *
 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
 * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
 */

#include <linux/init.h>
#include <linux/module.h>

#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/types.h>

static struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
static int num_obio_ranges;

/* Adjust register values based upon the ranges parameters. */
static void prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
			     struct linux_prom_ranges *rangep, int nranges)
{
	int regc, rngc;

	for (regc = 0; regc < nregs; regc++) {
		for (rngc = 0; rngc < nranges; rngc++)
			if (regp[regc].which_io == rangep[rngc].ot_child_space)
				break; /* Fount it */
		if (rngc == nranges) /* oops */
			prom_printf("adjust_regs: Could not find range with matching bus type...\n");
		regp[regc].which_io = rangep[rngc].ot_parent_space;
		regp[regc].phys_addr -= rangep[rngc].ot_child_base;
		regp[regc].phys_addr += rangep[rngc].ot_parent_base;
	}
}

static void prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1,
			       struct linux_prom_ranges *ranges2, int nranges2)
{
	int rng1c, rng2c;

	for (rng1c = 0; rng1c < nranges1; rng1c++) {
		for (rng2c = 0; rng2c < nranges2; rng2c++)
			if (ranges1[rng1c].ot_parent_space == ranges2[rng2c].ot_child_space &&
			   ranges1[rng1c].ot_parent_base >= ranges2[rng2c].ot_child_base &&
			   ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base > 0U)
			break;
		if (rng2c == nranges2) /* oops */
			prom_printf("adjust_ranges: Could not find matching bus type...\n");
		else if (ranges1[rng1c].ot_parent_base + ranges1[rng1c].or_size > ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size)
			ranges1[rng1c].or_size = ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base;
		ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
		ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
	}
}

/* Apply probed obio ranges to registers passed, if no ranges return. */
void prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs)
{
	if (num_obio_ranges)
		prom_adjust_regs(regs, nregs, promlib_obio_ranges, num_obio_ranges);
}
EXPORT_SYMBOL(prom_apply_obio_ranges);

void __init prom_ranges_init(void)
{
	phandle node, obio_node;
	int success;

	num_obio_ranges = 0;

	/* Check for obio and sbus ranges. */
	node = prom_getchild(prom_root_node);
	obio_node = prom_searchsiblings(node, "obio");

	if (obio_node) {
		success = prom_getproperty(obio_node, "ranges",
					   (char *) promlib_obio_ranges,
					   sizeof(promlib_obio_ranges));
		if (success != -1)
			num_obio_ranges = (success / sizeof(struct linux_prom_ranges));
	}

	if (num_obio_ranges)
		prom_printf("PROMLIB: obio_ranges %d\n", num_obio_ranges);
}

void prom_apply_generic_ranges(phandle node, phandle parent,
			       struct linux_prom_registers *regs, int nregs)
{
	int success;
	int num_ranges;
	struct linux_prom_ranges ranges[PROMREG_MAX];

	success = prom_getproperty(node, "ranges",
				   (char *) ranges,
				   sizeof(ranges));
	if (success != -1) {
		num_ranges = (success / sizeof(struct linux_prom_ranges));
		if (parent) {
			struct linux_prom_ranges parent_ranges[PROMREG_MAX];
			int num_parent_ranges;

			success = prom_getproperty(parent, "ranges",
						   (char *) parent_ranges,
						   sizeof(parent_ranges));
			if (success != -1) {
				num_parent_ranges = (success / sizeof(struct linux_prom_ranges));
				prom_adjust_ranges(ranges, num_ranges, parent_ranges, num_parent_ranges);
			}
		}
		prom_adjust_regs(regs, nregs, ranges, num_ranges);
	}
}
back to top