drmkit_fmt/family.rs
1// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
2// SPDX-License-Identifier: MIT
3
4//! Parameterized modifier families.
5//!
6//! Most modifiers match a plane's `IN_FORMATS` entry by equality. A few vendor
7//! families are *parameterized*: the plane advertises a base modifier, and a
8//! producer bakes a buffer-specific parameter into the value it hands over. The
9//! two are not equal, but the buffer is still scannable, because the kernel
10//! reads the parameter from the framebuffer rather than from `IN_FORMATS`.
11//!
12//! This module generalizes the special case introduced upstream in PR #231 for
13//! Broadcom SAND (plan ยง4.7). The C++ side spells that case out inline in
14//! `layer_fits_plane`; carrying it as a family keeps the next vendor from
15//! becoming a second inline branch.
16//!
17//! # Behavioral parity
18//!
19//! Generalizing the *mechanism* must not widen the *behavior*. Today exactly
20//! one family declares a non-exact match โ Broadcom SAND โ and it matches the
21//! upstream predicate byte for byte. Arm AFBC has no family: it is decoded only
22//! for description and classification, and its block-size and flag variants
23//! match **exactly**,
24//! because upstream requires exact matches for them and loosening that would
25//! let a plane accept a layout the display engine cannot fetch.
26//!
27//! Adding a family is a behavior change to plane eligibility. It needs the same
28//! evidence PR #231 carried: a driver that demonstrably accepts the parameter
29//! range on a plane advertising the base.
30
31use crate::modifier::{Modifier, broadcom, vendor};
32
33/// The family a modifier belongs to, for eligibility matching.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
35#[non_exhaustive]
36pub enum ModifierFamily {
37 /// The modifier matches only itself. Everything not named below.
38 Exact,
39 /// Broadcom SAND, one of `SAND64` / `SAND128` / `SAND256`.
40 ///
41 /// The low byte is the layout code; bits 8..56 carry the column height. A
42 /// plane advertises the base (column height 0); a producer bakes the coded
43 /// image height in. `rpi-hevc-dec` exporting `SAND128` with the column
44 /// height set is the case PR #231 fixed.
45 ///
46 /// `SAND32` (code 2) is deliberately **not** a member: the upstream commit
47 /// names `SAND64/128/256` explicitly, and widening the range here would be
48 /// a behavior change with no driver evidence behind it.
49 BroadcomSand {
50 /// The SAND layout code: 3, 4, or 5.
51 code: u64,
52 },
53}
54
55impl ModifierFamily {
56 /// The base modifier a plane advertises for this family, if the family is
57 /// parameterized.
58 ///
59 /// [`ModifierFamily::Exact`] has no base โ such a modifier is only ever
60 /// eligible against itself.
61 #[must_use]
62 pub const fn base(self) -> Option<Modifier> {
63 match self {
64 Self::Exact => None,
65 Self::BroadcomSand { code } => {
66 Some(Modifier(crate::modifier::mod_code(vendor::BROADCOM, code)))
67 }
68 }
69 }
70}
71
72impl Modifier {
73 /// Which family this modifier belongs to.
74 ///
75 /// ```
76 /// use drmkit_fmt::{Modifier, ModifierFamily, sand128_col_height};
77 ///
78 /// // A producer's SAND128 with a baked-in column height.
79 /// let produced = sand128_col_height(1080);
80 /// assert!(matches!(produced.family(), ModifierFamily::BroadcomSand { code: 4 }));
81 ///
82 /// // The base a plane advertises.
83 /// assert_eq!(produced.family().base(), Some(sand128_col_height(0)));
84 ///
85 /// // Everything else matches only itself.
86 /// assert_eq!(Modifier::LINEAR.family(), ModifierFamily::Exact);
87 /// assert_eq!(Modifier::LINEAR.family().base(), None);
88 /// ```
89 #[must_use]
90 pub const fn family(self) -> ModifierFamily {
91 if self.vendor() == vendor::BROADCOM {
92 let code = self.0 & 0xff;
93 // SAND64 / SAND128 / SAND256, matching the upstream predicate.
94 if code >= broadcom::SAND64 && code <= broadcom::SAND256 {
95 return ModifierFamily::BroadcomSand { code };
96 }
97 }
98 ModifierFamily::Exact
99 }
100}
101
102/// Build a Broadcom `SAND128` modifier with an explicit column height.
103///
104/// Column height 0 is the base a plane advertises in `IN_FORMATS`.
105#[must_use]
106pub const fn sand128_col_height(height: u64) -> Modifier {
107 Modifier(crate::modifier::mod_code(
108 vendor::BROADCOM,
109 (height << 8) | broadcom::SAND128,
110 ))
111}
112
113/// The column height baked into a `SAND64`, `SAND128` or `SAND256` modifier,
114/// or `None` for anything else, `SAND32` included.
115#[must_use]
116pub const fn sand_col_height(m: Modifier) -> Option<u64> {
117 match m.family() {
118 ModifierFamily::BroadcomSand { .. } => Some((m.0 >> 8) & ((1 << 48) - 1)),
119 ModifierFamily::Exact => None,
120 }
121}