Skip to main content

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}