Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -1952,6 +1952,7 @@ L: dri-devel@lists.freedesktop.org
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: drivers/gpu/drm/apple/
F: tools/diagnostics/test-apple-external-vrr.py
F: tools/testing/selftests/drivers/gpu/apple-afkv2/

APPLE PCIE CONTROLLER DRIVER
Expand Down
6 changes: 6 additions & 0 deletions drivers/gpu/drm/apple/dcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2334,6 +2334,12 @@ int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)

crtc_state = drm_atomic_get_new_crtc_state(state, crtc);

/* External H17P does not program adaptive sync. */
if (dcp->hw.iomfb_method_profile ==
DCP_IOMFB_METHODS_H17P_EXTERNAL_EXPERIMENTAL &&
crtc_state->vrr_enabled)
return -EINVAL;

needs_modeset = drm_atomic_crtc_needs_modeset(crtc_state) ||
!READ_ONCE(dcp->mode_state.valid);
if (!needs_modeset && (!dcp->connector || !dcp->connector->connected)) {
Expand Down
8 changes: 6 additions & 2 deletions drivers/gpu/drm/apple/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -765,8 +765,12 @@ static int parse_mode(struct dcp_parse_ctx *handle,
out->vrr = true;
}

/* Refresh rates are reported by DCP as 16.16 fixed-point Hz. */
if (min_vrr && max_vrr > min_vrr) {
/*
* Refresh rates are reported by DCP as 16.16 fixed-point Hz.
* External H17P skips adaptive-sync programming: keep the fixed timing
* available, but do not advertise its sink VRR range as driver support.
*/
if (!external_sdr && min_vrr && max_vrr > min_vrr) {
out->min_vrr = min_vrr;
out->max_vrr = max_vrr;
out->vrr = true;
Expand Down
15 changes: 11 additions & 4 deletions tools/diagnostics/test-apple-external-sdr.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ def timing(colors, rate=60, identity=203, score=10):

cases = []

def case(name, timings, external=True, count=1, identity=9, depth=8, clock=148500):
def case(name, timings, external=True, count=1, identity=9, depth=8, clock=148500, vrr=False):
out = bytearray(struct.pack('<I', 0xd3))
wire(timings, out)
cases.append((name, bytes(out), external, count, identity, depth, clock))
cases.append((name, bytes(out), external, count, identity, depth, clock, vrr))


wide = color(84, 1000, Colorimetry=9, Depth=10)
Expand All @@ -91,7 +91,13 @@ def case(name, timings, external=True, count=1, identity=9, depth=8, clock=14850
case('default fallback retains firmware ID', [timing([color(123456, 4, Colorimetry=16)])], identity=123456)
case('ties preserve firmware order', [timing([srgb, color(12, 10, Colorimetry=1)])])
case('360Hz is not filtered by color policy', [timing([wide, srgb], rate=360)], clock=891000)
case('VRR-compatible high refresh color', [dict(timing([srgb], rate=240), MinimumVariableRefreshRate=48 << 16, MaximumVariableRefreshRate=240 << 16)], clock=594000)
adaptive = dict(timing([srgb], rate=240), MinimumVariableRefreshRate=48 << 16,
MaximumVariableRefreshRate=240 << 16)
case('external VRR sink retains fixed high refresh', [adaptive], clock=594000)
case('legacy VRR remains available', [adaptive], False, clock=594000, vrr=True)
case('internal VRR remains available', [adaptive], False, clock=594000, vrr=True)
case('internal ProMotion fallback remains available', [timing([srgb], rate=120)],
False, clock=297000, vrr=True)
for name, changes in [
('BT2020 RGB', dict(Colorimetry=9)), ('BT2020 YCC', dict(Colorimetry=7)),
('BT2020 cYCC', dict(Colorimetry=6)), ('P3 D65', dict(Colorimetry=14)),
Expand Down Expand Up @@ -220,7 +226,7 @@ def restricted(rate=60, unsafe=None, dsc=None, colors=None, before=False):
int main(void)
{
'''
for i, (name, blob, external, count, identity, depth, clock) in enumerate(cases):
for i, (name, blob, external, count, identity, depth, clock, vrr) in enumerate(cases):
suffix += f'''
{{
struct dcp_parse_ctx ctx;
Expand All @@ -238,6 +244,7 @@ def restricted(rate=60, unsafe=None, dsc=None, colors=None, before=False):
assert(modes[0].color_mode_id == {identity});
assert(modes[0].sdr_rgb.depth == {depth});
assert(modes[0].mode.clock == {clock});
assert(modes[0].vrr == {str(vrr).lower()});
assert(modes[0].mode.type & DRM_MODE_TYPE_PREFERRED);
'''
suffix += ' free(modes);\n }\n'
Expand Down
93 changes: 93 additions & 0 deletions tools/diagnostics/test-apple-external-vrr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only OR MIT
"""Exercise the production atomic admission check for external H17P VRR."""
from pathlib import Path
import os
import shlex
import subprocess
import tempfile

root = Path(__file__).resolve().parents[2]
source = (root / 'drivers/gpu/drm/apple/dcp.c').read_text()
start = source.index('int dcp_crtc_atomic_check(')
opening = source.index('{', start)
end, depth = opening + 1, 1
while depth:
depth += (source[end] == '{') - (source[end] == '}')
end += 1
check = source[start:end]
prefix = r"""
#undef NDEBUG
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#define DCP_IOMFB_METHODS_H17P_EXTERNAL_EXPERIMENTAL 2
#define READ_ONCE(x) (x)
#define dev_dbg(...) ((void)0)
struct apple_connector { bool connected; };
struct apple_dcp {
bool crashed;
struct { int iomfb_method_profile; } hw;
struct { bool valid; } mode_state;
struct apple_connector *connector;
void *dev;
};
struct platform_device { struct apple_dcp *data; };
struct drm_crtc { struct platform_device *dcp; };
struct drm_crtc_state { bool vrr_enabled, modeset; };
struct drm_atomic_state { struct drm_crtc_state crtc; };
#define to_apple_crtc(x) (x)
#define platform_get_drvdata(x) ((x)->data)
static struct drm_crtc_state *drm_atomic_get_new_crtc_state(
struct drm_atomic_state *s, struct drm_crtc *c)
{ (void)c; return &s->crtc; }
static bool drm_atomic_crtc_needs_modeset(struct drm_crtc_state *s)
{ return s->modeset; }
"""
suffix = r"""
int main(void)
{
struct apple_connector connector = { .connected = true };
struct apple_dcp dcp = { .connector = &connector };
struct platform_device pdev = { .data = &dcp };
struct drm_crtc crtc = { .dcp = &pdev };
struct drm_atomic_state state = {0};
unsigned int cases = 0;
/* Check initial modesets, plane-only commits and link recovery states. */
for (unsigned int profile = 0; profile < 3; profile++)
for (unsigned int valid = 0; valid < 2; valid++)
for (unsigned int modeset = 0; modeset < 2; modeset++)
for (unsigned int link = 0; link < 3; link++)
for (unsigned int vrr = 0; vrr < 2; vrr++) {
dcp.hw.iomfb_method_profile = profile;
dcp.mode_state.valid = valid;
state.crtc.modeset = modeset;
state.crtc.vrr_enabled = vrr;
dcp.connector = link == 2 ? NULL : &connector;
connector.connected = link == 1;
int expected = profile == DCP_IOMFB_METHODS_H17P_EXTERNAL_EXPERIMENTAL && vrr ? -EINVAL : 0;
int actual = dcp_crtc_atomic_check(&crtc, &state);
if (actual != expected) {
fprintf(stderr, "profile=%u valid=%u modeset=%u link=%u vrr=%u: got %d expected %d\n",
profile, valid, modeset, link, vrr, actual, expected);
return 1;
}
cases++;
}
dcp.crashed = true;
state.crtc.vrr_enabled = false;
assert(dcp_crtc_atomic_check(&crtc, &state) == -EINVAL);
printf("PASS: %u production atomic VRR admission cases and crashed-device rejection\n", cases);
return 0;
}
"""
with tempfile.TemporaryDirectory(prefix='apple-external-vrr-') as tmp:
tmp = Path(tmp)
unit = tmp / 'test.c'
unit.write_text(prefix + check + suffix)
command = shlex.split(os.environ.get('CC', 'clang'))
command += ['-std=gnu11', '-Wall', '-Wextra', '-Werror', '-O1', '-g',
'-fsanitize=address,undefined', '-fno-omit-frame-pointer']
subprocess.run(command + [str(unit), '-o', str(tmp / 'test')], check=True)
subprocess.run([str(tmp / 'test')], check=True)