root/include/uapi/drm/asahi_drm.h
/* SPDX-License-Identifier: MIT */
/*
 * Copyright (C) The Asahi Linux Contributors
 * Copyright (C) 2018-2023 Collabora Ltd.
 * Copyright (C) 2014-2018 Broadcom
 */
#ifndef _ASAHI_DRM_H_
#define _ASAHI_DRM_H_

#include "drm.h"

#if defined(__cplusplus)
extern "C" {
#endif

/**
 * DOC: Introduction to the Asahi UAPI
 *
 * This documentation describes the Asahi IOCTLs.
 *
 * Just a few generic rules about the data passed to the Asahi IOCTLs (cribbed
 * from Panthor):
 *
 * - Structures must be aligned on 64-bit/8-byte. If the object is not
 *   naturally aligned, a padding field must be added.
 * - Fields must be explicitly aligned to their natural type alignment with
 *   pad[0..N] fields.
 * - All padding fields will be checked by the driver to make sure they are
 *   zeroed.
 * - Flags can be added, but not removed/replaced.
 * - New fields can be added to the main structures (the structures
 *   directly passed to the ioctl). Those fields can be added at the end of
 *   the structure, or replace existing padding fields. Any new field being
 *   added must preserve the behavior that existed before those fields were
 *   added when a value of zero is passed.
 * - New fields can be added to indirect objects (objects pointed by the
 *   main structure), iff those objects are passed a size to reflect the
 *   size known by the userspace driver (see
 *   drm_asahi_cmd_header::size).
 * - If the kernel driver is too old to know some fields, those will be
 *   ignored if zero, and otherwise rejected (and so will be zero on output).
 * - If userspace is too old to know some fields, those will be zeroed
 *   (input) before the structure is parsed by the kernel driver.
 * - Each new flag/field addition must come with a driver version update so
 *   the userspace driver doesn't have to guess which flags are supported.
 * - Structures should not contain unions, as this would defeat the
 *   extensibility of such structures.
 * - IOCTLs can't be removed or replaced. New IOCTL IDs should be placed
 *   at the end of the drm_asahi_ioctl_id enum.
 */

/**
 * enum drm_asahi_ioctl_id - IOCTL IDs
 *
 * Place new ioctls at the end, don't re-order, don't replace or remove entries.
 *
 * These IDs are not meant to be used directly. Use the DRM_IOCTL_ASAHI_xxx
 * definitions instead.
 */
enum drm_asahi_ioctl_id {
        /** @DRM_ASAHI_GET_PARAMS: Query device properties. */
        DRM_ASAHI_GET_PARAMS = 0,

        /** @DRM_ASAHI_GET_TIME: Query device time. */
        DRM_ASAHI_GET_TIME,

        /** @DRM_ASAHI_VM_CREATE: Create a GPU VM address space. */
        DRM_ASAHI_VM_CREATE,

        /** @DRM_ASAHI_VM_DESTROY: Destroy a VM. */
        DRM_ASAHI_VM_DESTROY,

        /** @DRM_ASAHI_VM_BIND: Bind/unbind memory to a VM. */
        DRM_ASAHI_VM_BIND,

        /** @DRM_ASAHI_GEM_CREATE: Create a buffer object. */
        DRM_ASAHI_GEM_CREATE,

        /**
         * @DRM_ASAHI_GEM_MMAP_OFFSET: Get offset to pass to mmap() to map a
         * given GEM handle.
         */
        DRM_ASAHI_GEM_MMAP_OFFSET,

        /** @DRM_ASAHI_GEM_BIND_OBJECT: Bind memory as a special object */
        DRM_ASAHI_GEM_BIND_OBJECT,

        /** @DRM_ASAHI_QUEUE_CREATE: Create a scheduling queue. */
        DRM_ASAHI_QUEUE_CREATE,

        /** @DRM_ASAHI_QUEUE_DESTROY: Destroy a scheduling queue. */
        DRM_ASAHI_QUEUE_DESTROY,

        /** @DRM_ASAHI_SUBMIT: Submit commands to a queue. */
        DRM_ASAHI_SUBMIT,
};

#define DRM_ASAHI_MAX_CLUSTERS  64

/**
 * struct drm_asahi_params_global - Global parameters.
 *
 * This struct may be queried by drm_asahi_get_params.
 */
struct drm_asahi_params_global {
        /** @features: Feature bits from drm_asahi_feature */
        __u64 features;

        /** @gpu_generation: GPU generation, e.g. 13 for G13G */
        __u32 gpu_generation;

        /** @gpu_variant: GPU variant as a character, e.g. 'C' for G13C */
        __u32 gpu_variant;

        /**
         * @gpu_revision: GPU revision in BCD, e.g. 0x00 for 'A0' or
         * 0x21 for 'C1'
         */
        __u32 gpu_revision;

        /** @chip_id: Chip ID in BCD, e.g. 0x8103 for T8103 */
        __u32 chip_id;

        /** @num_dies: Number of dies in the SoC */
        __u32 num_dies;

        /** @num_clusters_total: Number of GPU clusters (across all dies) */
        __u32 num_clusters_total;

        /**
         * @num_cores_per_cluster: Number of logical cores per cluster
         * (including inactive/nonexistent)
         */
        __u32 num_cores_per_cluster;

        /** @max_frequency_khz: Maximum GPU core clock frequency */
        __u32 max_frequency_khz;

        /** @core_masks: Bitmask of present/enabled cores per cluster */
        __u64 core_masks[DRM_ASAHI_MAX_CLUSTERS];

        /**
         * @vm_start: VM range start VMA. Together with @vm_end, this defines
         * the window of valid GPU VAs. Userspace is expected to subdivide VAs
         * out of this window.
         *
         * This window contains all virtual addresses that userspace needs to
         * know about. There may be kernel-internal GPU VAs outside this range,
         * but that detail is not relevant here.
         */
        __u64 vm_start;

        /** @vm_end: VM range end VMA */
        __u64 vm_end;

        /**
         * @vm_kernel_min_size: Minimum kernel VMA window size.
         *
         * When creating a VM, userspace is required to carve out a section of
         * virtual addresses (within the range given by @vm_start and
         * @vm_end). The kernel will allocate various internal structures
         * within the specified VA range.
         *
         * Allowing userspace to choose the VA range for the kernel, rather than
         * the kernel reserving VAs and requiring userspace to cope, can assist
         * in implementing SVM.
         */
        __u64 vm_kernel_min_size;

        /**
         * @max_commands_per_submission: Maximum number of supported commands
         * per submission. This mirrors firmware limits. Userspace must split up
         * larger command buffers, which may require inserting additional
         * synchronization.
         */
        __u32 max_commands_per_submission;

        /**
         * @max_attachments: Maximum number of drm_asahi_attachment's per
         * command
         */
        __u32 max_attachments;

        /**
         * @command_timestamp_frequency_hz: Timebase frequency for timestamps
         * written during command execution, specified via drm_asahi_timestamp
         * structures. As this rate is controlled by the firmware, it is a
         * queryable parameter.
         *
         * Userspace must divide by this frequency to convert timestamps to
         * seconds, rather than hardcoding a particular firmware's rate.
         */
        __u64 command_timestamp_frequency_hz;
};

/**
 * enum drm_asahi_feature - Feature bits
 *
 * This covers only features that userspace cannot infer from the architecture
 * version. Most features don't need to be here.
 */
enum drm_asahi_feature {
        /**
         * @DRM_ASAHI_FEATURE_SOFT_FAULTS: GPU has "soft fault" enabled. Shader
         * loads of unmapped memory will return zero. Shader stores to unmapped
         * memory will be silently discarded. Note that only shader load/store
         * is affected. Other hardware units are not affected, notably including
         * texture sampling.
         *
         * Soft fault is set when initializing the GPU and cannot be runtime
         * toggled. Therefore, it is exposed as a feature bit and not a
         * userspace-settable flag on the VM. When soft fault is enabled,
         * userspace can speculate memory accesses more aggressively.
         */
        DRM_ASAHI_FEATURE_SOFT_FAULTS = (1UL) << 0,
};

/**
 * struct drm_asahi_get_params - Arguments passed to DRM_IOCTL_ASAHI_GET_PARAMS
 */
struct drm_asahi_get_params {
        /** @param_group: Parameter group to fetch (MBZ) */
        __u32 param_group;

        /** @pad: MBZ */
        __u32 pad;

        /** @pointer: User pointer to write parameter struct */
        __u64 pointer;

        /**
         * @size: Size of the user buffer. In case of older userspace, this may
         * be less than sizeof(struct drm_asahi_params_global). The kernel will
         * not write past the length specified here, allowing extensibility.
         */
        __u64 size;
};

/**
 * struct drm_asahi_vm_create - Arguments passed to DRM_IOCTL_ASAHI_VM_CREATE
 */
struct drm_asahi_vm_create {
        /**
         * @kernel_start: Start of the kernel-reserved address range. See
         * drm_asahi_params_global::vm_kernel_min_size.
         *
         * Both @kernel_start and @kernel_end must be within the range of
         * valid VAs given by drm_asahi_params_global::vm_start and
         * drm_asahi_params_global::vm_end. The size of the kernel range
         * (@kernel_end - @kernel_start) must be at least
         * drm_asahi_params_global::vm_kernel_min_size.
         *
         * Userspace must not bind any memory on this VM into this reserved
         * range, it is for kernel use only.
         */
        __u64 kernel_start;

        /**
         * @kernel_end: End of the kernel-reserved address range. See
         * @kernel_start.
         */
        __u64 kernel_end;

        /** @vm_id: Returned VM ID */
        __u32 vm_id;

        /** @pad: MBZ */
        __u32 pad;
};

/**
 * struct drm_asahi_vm_destroy - Arguments passed to DRM_IOCTL_ASAHI_VM_DESTROY
 */
struct drm_asahi_vm_destroy {
        /** @vm_id: VM ID to be destroyed */
        __u32 vm_id;

        /** @pad: MBZ */
        __u32 pad;
};

/**
 * enum drm_asahi_gem_flags - Flags for GEM creation
 */
enum drm_asahi_gem_flags {
        /**
         * @DRM_ASAHI_GEM_WRITEBACK: BO should be CPU-mapped as writeback.
         *
         * Map as writeback instead of write-combine. This optimizes for CPU
         * reads.
         */
        DRM_ASAHI_GEM_WRITEBACK = (1L << 0),

        /**
         * @DRM_ASAHI_GEM_VM_PRIVATE: BO is private to this GPU VM (no exports).
         */
        DRM_ASAHI_GEM_VM_PRIVATE = (1L << 1),
};

/**
 * struct drm_asahi_gem_create - Arguments passed to DRM_IOCTL_ASAHI_GEM_CREATE
 */
struct drm_asahi_gem_create {
        /** @size: Size of the BO */
        __u64 size;

        /** @flags: Combination of drm_asahi_gem_flags flags. */
        __u32 flags;

        /**
         * @vm_id: VM ID to assign to the BO, if DRM_ASAHI_GEM_VM_PRIVATE is set
         */
        __u32 vm_id;

        /** @handle: Returned GEM handle for the BO */
        __u32 handle;

        /** @pad: MBZ */
        __u32 pad;
};

/**
 * struct drm_asahi_gem_mmap_offset - Arguments passed to
 * DRM_IOCTL_ASAHI_GEM_MMAP_OFFSET
 */
struct drm_asahi_gem_mmap_offset {
        /** @handle: Handle for the object being mapped. */
        __u32 handle;

        /** @flags: Must be zero */
        __u32 flags;

        /** @offset: The fake offset to use for subsequent mmap call */
        __u64 offset;
};

/**
 * enum drm_asahi_bind_flags - Flags for GEM binding
 */
enum drm_asahi_bind_flags {
        /**
         * @DRM_ASAHI_BIND_UNBIND: Instead of binding a GEM object to the range,
         * simply unbind the GPU VMA range.
         */
        DRM_ASAHI_BIND_UNBIND = (1L << 0),

        /** @DRM_ASAHI_BIND_READ: Map BO with GPU read permission */
        DRM_ASAHI_BIND_READ = (1L << 1),

        /** @DRM_ASAHI_BIND_WRITE: Map BO with GPU write permission */
        DRM_ASAHI_BIND_WRITE = (1L << 2),

        /**
         * @DRM_ASAHI_BIND_SINGLE_PAGE: Map a single page of the BO repeatedly
         * across the VA range.
         *
         * This is useful to fill a VA range with scratch pages or zero pages.
         * It is intended as a mechanism to accelerate sparse.
         */
        DRM_ASAHI_BIND_SINGLE_PAGE = (1L << 3),
};

/**
 * struct drm_asahi_gem_bind_op - Description of a single GEM bind operation.
 */
struct drm_asahi_gem_bind_op {
        /** @flags: Combination of drm_asahi_bind_flags flags. */
        __u32 flags;

        /** @handle: GEM object to bind (except for UNBIND) */
        __u32 handle;

        /**
         * @offset: Offset into the object (except for UNBIND).
         *
         * For a regular bind, this is the beginning of the region of the GEM
         * object to bind.
         *
         * For a single-page bind, this is the offset to the single page that
         * will be repeatedly bound.
         *
         * Must be page-size aligned.
         */
        __u64 offset;

        /**
         * @range: Number of bytes to bind/unbind to @addr.
         *
         * Must be page-size aligned.
         */
        __u64 range;

        /**
         * @addr: Address to bind to.
         *
         * Must be page-size aligned.
         */
        __u64 addr;
};

/**
 * struct drm_asahi_vm_bind - Arguments passed to
 * DRM_IOCTL_ASAHI_VM_BIND
 */
struct drm_asahi_vm_bind {
        /** @vm_id: The ID of the VM to bind to */
        __u32 vm_id;

        /** @num_binds: number of binds in this IOCTL. */
        __u32 num_binds;

        /**
         * @stride: Stride in bytes between consecutive binds. This allows
         * extensibility of drm_asahi_gem_bind_op.
         */
        __u32 stride;

        /** @pad: MBZ */
        __u32 pad;

        /**
         * @userptr: User pointer to an array of @num_binds structures of type
         * @drm_asahi_gem_bind_op and size @stride bytes.
         */
        __u64 userptr;
};

/**
 * enum drm_asahi_bind_object_op - Special object bind operation
 */
enum drm_asahi_bind_object_op {
        /** @DRM_ASAHI_BIND_OBJECT_OP_BIND: Bind a BO as a special GPU object */
        DRM_ASAHI_BIND_OBJECT_OP_BIND = 0,

        /** @DRM_ASAHI_BIND_OBJECT_OP_UNBIND: Unbind a special GPU object */
        DRM_ASAHI_BIND_OBJECT_OP_UNBIND = 1,
};

/**
 * enum drm_asahi_bind_object_flags - Special object bind flags
 */
enum drm_asahi_bind_object_flags {
        /**
         * @DRM_ASAHI_BIND_OBJECT_USAGE_TIMESTAMPS: Map a BO as a timestamp
         * buffer.
         */
        DRM_ASAHI_BIND_OBJECT_USAGE_TIMESTAMPS = (1L << 0),
};

/**
 * struct drm_asahi_gem_bind_object - Arguments passed to
 * DRM_IOCTL_ASAHI_GEM_BIND_OBJECT
 */
struct drm_asahi_gem_bind_object {
        /** @op: Bind operation (enum drm_asahi_bind_object_op) */
        __u32 op;

        /** @flags: Combination of drm_asahi_bind_object_flags flags. */
        __u32 flags;

        /** @handle: GEM object to bind/unbind (BIND) */
        __u32 handle;

        /** @vm_id: The ID of the VM to operate on (MBZ currently) */
        __u32 vm_id;

        /** @offset: Offset into the object (BIND only) */
        __u64 offset;

        /** @range: Number of bytes to bind/unbind (BIND only) */
        __u64 range;

        /** @object_handle: Object handle (out for BIND, in for UNBIND) */
        __u32 object_handle;

        /** @pad: MBZ */
        __u32 pad;
};

/**
 * enum drm_asahi_cmd_type - Command type
 */
enum drm_asahi_cmd_type {
        /**
         * @DRM_ASAHI_CMD_RENDER: Render command, executing on the render
         * subqueue. Combined vertex and fragment operation.
         *
         * Followed by a @drm_asahi_cmd_render payload.
         */
        DRM_ASAHI_CMD_RENDER = 0,

        /**
         * @DRM_ASAHI_CMD_COMPUTE: Compute command on the compute subqueue.
         *
         * Followed by a @drm_asahi_cmd_compute payload.
         */
        DRM_ASAHI_CMD_COMPUTE = 1,

        /**
         * @DRM_ASAHI_SET_VERTEX_ATTACHMENTS: Software command to set
         * attachments for subsequent vertex shaders in the same submit.
         *
         * Followed by (possibly multiple) @drm_asahi_attachment payloads.
         */
        DRM_ASAHI_SET_VERTEX_ATTACHMENTS = 2,

        /**
         * @DRM_ASAHI_SET_FRAGMENT_ATTACHMENTS: Software command to set
         * attachments for subsequent fragment shaders in the same submit.
         *
         * Followed by (possibly multiple) @drm_asahi_attachment payloads.
         */
        DRM_ASAHI_SET_FRAGMENT_ATTACHMENTS = 3,

        /**
         * @DRM_ASAHI_SET_COMPUTE_ATTACHMENTS: Software command to set
         * attachments for subsequent compute shaders in the same submit.
         *
         * Followed by (possibly multiple) @drm_asahi_attachment payloads.
         */
        DRM_ASAHI_SET_COMPUTE_ATTACHMENTS = 4,
};

/**
 * enum drm_asahi_priority - Scheduling queue priority.
 *
 * These priorities are forwarded to the firmware to influence firmware
 * scheduling. The exact policy is ultimately decided by firmware, but
 * these enums allow userspace to communicate the intentions.
 */
enum drm_asahi_priority {
        /** @DRM_ASAHI_PRIORITY_LOW: Low priority queue. */
        DRM_ASAHI_PRIORITY_LOW = 0,

        /** @DRM_ASAHI_PRIORITY_MEDIUM: Medium priority queue. */
        DRM_ASAHI_PRIORITY_MEDIUM = 1,

        /**
         * @DRM_ASAHI_PRIORITY_HIGH: High priority queue.
         *
         * Reserved for future extension.
         */
        DRM_ASAHI_PRIORITY_HIGH = 2,

        /**
         * @DRM_ASAHI_PRIORITY_REALTIME: Real-time priority queue.
         *
         * Reserved for future extension.
         */
        DRM_ASAHI_PRIORITY_REALTIME = 3,
};

/**
 * struct drm_asahi_queue_create - Arguments passed to
 * DRM_IOCTL_ASAHI_QUEUE_CREATE
 */
struct drm_asahi_queue_create {
        /** @flags: MBZ */
        __u32 flags;

        /** @vm_id: The ID of the VM this queue is bound to */
        __u32 vm_id;

        /** @priority: One of drm_asahi_priority */
        __u32 priority;

        /** @queue_id: The returned queue ID */
        __u32 queue_id;

        /**
         * @usc_exec_base: GPU base address for all USC binaries (shaders) on
         * this queue. USC addresses are 32-bit relative to this 64-bit base.
         *
         * This sets the following registers on all queue commands:
         *
         *      USC_EXEC_BASE_TA  (vertex)
         *      USC_EXEC_BASE_ISP (fragment)
         *      USC_EXEC_BASE_CP  (compute)
         *
         * While the hardware lets us configure these independently per command,
         * we do not have a use case for this. Instead, we expect userspace to
         * fix a 4GiB VA carveout for USC memory and pass its base address here.
         */
        __u64 usc_exec_base;
};

/**
 * struct drm_asahi_queue_destroy - Arguments passed to
 * DRM_IOCTL_ASAHI_QUEUE_DESTROY
 */
struct drm_asahi_queue_destroy {
        /** @queue_id: The queue ID to be destroyed */
        __u32 queue_id;

        /** @pad: MBZ */
        __u32 pad;
};

/**
 * enum drm_asahi_sync_type - Sync item type
 */
enum drm_asahi_sync_type {
        /** @DRM_ASAHI_SYNC_SYNCOBJ: Binary sync object */
        DRM_ASAHI_SYNC_SYNCOBJ = 0,

        /** @DRM_ASAHI_SYNC_TIMELINE_SYNCOBJ: Timeline sync object */
        DRM_ASAHI_SYNC_TIMELINE_SYNCOBJ = 1,
};

/**
 * struct drm_asahi_sync - Sync item
 */
struct drm_asahi_sync {
        /** @sync_type: One of drm_asahi_sync_type */
        __u32 sync_type;

        /** @handle: The sync object handle */
        __u32 handle;

        /** @timeline_value: Timeline value for timeline sync objects */
        __u64 timeline_value;
};

/**
 * define DRM_ASAHI_BARRIER_NONE - Command index for no barrier
 *
 * This special value may be passed in to drm_asahi_command::vdm_barrier or
 * drm_asahi_command::cdm_barrier to indicate that the respective subqueue
 * should not wait on any previous work.
 */
#define DRM_ASAHI_BARRIER_NONE (0xFFFFu)

/**
 * struct drm_asahi_cmd_header - Top level command structure
 *
 * This struct is core to the command buffer definition and therefore is not
 * extensible.
 */
struct drm_asahi_cmd_header {
        /** @cmd_type: One of drm_asahi_cmd_type */
        __u16 cmd_type;

        /**
         * @size: Size of this command, not including this header.
         *
         * For hardware commands, this enables extensibility of commands without
         * requiring extra command types. Passing a command that is shorter
         * than expected is explicitly allowed for backwards-compatibility.
         * Truncated fields will be zeroed.
         *
         * For the synthetic attachment setting commands, this implicitly
         * encodes the number of attachments. These commands take multiple
         * fixed-size @drm_asahi_attachment structures as their payload, so size
         * equals number of attachments * sizeof(struct drm_asahi_attachment).
         */
        __u16 size;

        /**
         * @vdm_barrier: VDM (render) command index to wait on.
         *
         * Barriers are indices relative to the beginning of a given submit. A
         * barrier of 0 waits on commands submitted to the respective subqueue
         * in previous submit ioctls. A barrier of N waits on N previous
         * commands on the subqueue within the current submit ioctl. As a
         * special case, passing @DRM_ASAHI_BARRIER_NONE avoids waiting on any
         * commands in the subqueue.
         *
         * Examples:
         *
         *   0: This waits on all previous work.
         *
         *   NONE: This does not wait for anything on this subqueue.
         *
         *   1: This waits on the first render command in the submit.
         *   This is valid only if there are multiple render commands in the
         *   same submit.
         *
         * Barriers are valid only for hardware commands. Synthetic software
         * commands to set attachments must pass NONE here.
         */
        __u16 vdm_barrier;

        /**
         * @cdm_barrier: CDM (compute) command index to wait on.
         *
         * See @vdm_barrier, and replace VDM/render with CDM/compute.
         */
        __u16 cdm_barrier;
};

/**
 * struct drm_asahi_submit - Arguments passed to DRM_IOCTL_ASAHI_SUBMIT
 */
struct drm_asahi_submit {
        /**
         * @syncs: An optional pointer to an array of drm_asahi_sync. The first
         * @in_sync_count elements are in-syncs, then the remaining
         * @out_sync_count elements are out-syncs. Using a single array with
         * explicit partitioning simplifies handling.
         */
        __u64 syncs;

        /**
         * @cmdbuf: Pointer to the command buffer to submit.
         *
         * This is a flat command buffer. By design, it contains no CPU
         * pointers, which makes it suitable for a virtgpu wire protocol without
         * requiring any serializing/deserializing step.
         *
         * It consists of a series of commands. Each command begins with a
         * fixed-size @drm_asahi_cmd_header header and is followed by a
         * variable-length payload according to the type and size in the header.
         *
         * The combined count of "real" hardware commands must be nonzero and at
         * most drm_asahi_params_global::max_commands_per_submission.
         */
        __u64 cmdbuf;

        /** @flags: Flags for command submission (MBZ) */
        __u32 flags;

        /** @queue_id: The queue ID to be submitted to */
        __u32 queue_id;

        /**
         * @in_sync_count: Number of sync objects to wait on before starting
         * this job.
         */
        __u32 in_sync_count;

        /**
         * @out_sync_count: Number of sync objects to signal upon completion of
         * this job.
         */
        __u32 out_sync_count;

        /** @cmdbuf_size: Command buffer size in bytes */
        __u32 cmdbuf_size;

        /** @pad: MBZ */
        __u32 pad;
};

/**
 * struct drm_asahi_attachment - Describe an "attachment".
 *
 * Attachments are any memory written by shaders, notably including render
 * target attachments written by the end-of-tile program. This is purely a hint
 * about the accessed memory regions. It is optional to specify, which is
 * fortunate as it cannot be specified precisely with bindless access anyway.
 * But where possible, it's probably a good idea for userspace to include these
 * hints, forwarded to the firmware.
 *
 * This struct is implicitly sized and therefore is not extensible.
 */
struct drm_asahi_attachment {
        /** @pointer: Base address of the attachment */
        __u64 pointer;

        /** @size: Size of the attachment in bytes */
        __u64 size;

        /** @pad: MBZ */
        __u32 pad;

        /** @flags: MBZ */
        __u32 flags;
};

enum drm_asahi_render_flags {
        /**
         * @DRM_ASAHI_RENDER_VERTEX_SCRATCH: A vertex stage shader uses scratch
         * memory.
         */
        DRM_ASAHI_RENDER_VERTEX_SCRATCH = (1U << 0),

        /**
         * @DRM_ASAHI_RENDER_PROCESS_EMPTY_TILES: Process even empty tiles.
         * This must be set when clearing render targets.
         */
        DRM_ASAHI_RENDER_PROCESS_EMPTY_TILES = (1U << 1),

        /**
         * @DRM_ASAHI_RENDER_NO_VERTEX_CLUSTERING: Run vertex stage on a single
         * cluster (on multi-cluster GPUs)
         *
         * This harms performance but can workaround certain sync/coherency
         * bugs, and therefore is useful for debugging.
         */
        DRM_ASAHI_RENDER_NO_VERTEX_CLUSTERING = (1U << 2),

        /**
         * @DRM_ASAHI_RENDER_DBIAS_IS_INT: Use integer depth bias formula.
         *
         * Graphics specifications contain two alternate formulas for depth
         * bias, a float formula used with floating-point depth buffers and an
         * integer formula using with unorm depth buffers. This flag specifies
         * that the integer formula should be used. If omitted, the float
         * formula is used instead.
         *
         * This corresponds to bit 18 of the relevant hardware control register,
         * so we match that here for efficiency.
         */
        DRM_ASAHI_RENDER_DBIAS_IS_INT = (1U << 18),
};

/**
 * struct drm_asahi_zls_buffer - Describe a depth or stencil buffer.
 *
 * These fields correspond to hardware registers in the ZLS (Z Load/Store) unit.
 * There are three hardware registers for each field respectively for loads,
 * stores, and partial renders. In practice, it makes sense to set all to the
 * same values, except in exceptional cases not yet implemented in userspace, so
 * we do not duplicate here for simplicity/efficiency.
 *
 * This struct is embedded in other structs and therefore is not extensible.
 */
struct drm_asahi_zls_buffer {
        /** @base: Base address of the buffer */
        __u64 base;

        /**
         * @comp_base: If the load buffer is compressed, address of the
         * compression metadata section.
         */
        __u64 comp_base;

        /**
         * @stride: If layered rendering is enabled, the number of bytes
         * between each layer of the buffer.
         */
        __u32 stride;

        /**
         * @comp_stride: If layered rendering is enabled, the number of bytes
         * between each layer of the compression metadata.
         */
        __u32 comp_stride;
};

/**
 * struct drm_asahi_timestamp - Describe a timestamp write.
 *
 * The firmware can optionally write the GPU timestamp at render pass
 * granularities, but it needs to be mapped specially via
 * DRM_IOCTL_ASAHI_GEM_BIND_OBJECT. This structure therefore describes where to
 * write as a handle-offset pair, rather than a GPU address like normal.
 *
 * This struct is embedded in other structs and therefore is not extensible.
 */
struct drm_asahi_timestamp {
        /**
         * @handle: Handle of the timestamp buffer, or 0 to skip this
         * timestamp. If nonzero, this must equal the value returned in
         * drm_asahi_gem_bind_object::object_handle.
         */
        __u32 handle;

        /** @offset: Offset to write into the timestamp buffer */
        __u32 offset;
};

/**
 * struct drm_asahi_timestamps - Describe timestamp writes.
 *
 * Each operation that can be timestamped, can be timestamped at the start and
 * end. Therefore, drm_asahi_timestamp structs always come in pairs, bundled
 * together into drm_asahi_timestamps.
 *
 * This struct is embedded in other structs and therefore is not extensible.
 */
struct drm_asahi_timestamps {
        /** @start: Timestamp recorded at the start of the operation */
        struct drm_asahi_timestamp start;

        /** @end: Timestamp recorded at the end of the operation */
        struct drm_asahi_timestamp end;
};

/**
 * struct drm_asahi_helper_program - Describe helper program configuration.
 *
 * The helper program is a compute-like kernel required for various hardware
 * functionality. Its most important role is dynamically allocating
 * scratch/stack memory for individual subgroups, by partitioning a static
 * allocation shared for the whole device. It is supplied by userspace via
 * drm_asahi_helper_program and internally dispatched by the hardware as needed.
 *
 * This struct is embedded in other structs and therefore is not extensible.
 */
struct drm_asahi_helper_program {
        /**
         * @binary: USC address to the helper program binary. This is a tagged
         * pointer with configuration in the bottom bits.
         */
        __u32 binary;

        /** @cfg: Additional configuration bits for the helper program. */
        __u32 cfg;

        /**
         * @data: Data passed to the helper program. This value is not
         * interpreted by the kernel, firmware, or hardware in any way. It is
         * simply a sideband for userspace, set with the submit ioctl and read
         * via special registers inside the helper program.
         *
         * In practice, userspace will pass a 64-bit GPU VA here pointing to the
         * actual arguments, which presumably don't fit in 64-bits.
         */
        __u64 data;
};

/**
 * struct drm_asahi_bg_eot - Describe a background or end-of-tile program.
 *
 * The background and end-of-tile programs are dispatched by the hardware at the
 * beginning and end of rendering. As the hardware "tilebuffer" is simply local
 * memory, these programs are necessary to implement API-level render targets.
 * The fragment-like background program is responsible for loading either the
 * clear colour or the existing render target contents, while the compute-like
 * end-of-tile program stores the tilebuffer contents to memory.
 *
 * This struct is embedded in other structs and therefore is not extensible.
 */
struct drm_asahi_bg_eot {
        /**
         * @usc: USC address of the hardware USC words binding resources
         * (including images and uniforms) and the program itself. Note this is
         * an additional layer of indirection compared to the helper program,
         * avoiding the need for a sideband for data. This is a tagged pointer
         * with additional configuration in the bottom bits.
         */
        __u32 usc;

        /**
         * @rsrc_spec: Resource specifier for the program. This is a packed
         * hardware data structure describing the required number of registers,
         * uniforms, bound textures, and bound samplers.
         */
        __u32 rsrc_spec;
};

/**
 * struct drm_asahi_cmd_render - Command to submit 3D
 *
 * This command submits a single render pass. The hardware control stream may
 * include many draws and subpasses, but within the command, the framebuffer
 * dimensions and attachments are fixed.
 *
 * The hardware requires the firmware to set a large number of Control Registers
 * setting up state at render pass granularity before each command rendering 3D.
 * The firmware bundles this state into data structures. Unfortunately, we
 * cannot expose either any of that directly to userspace, because the
 * kernel-firmware ABI is not stable. Although we can guarantee the firmware
 * updates in tandem with the kernel, we cannot break old userspace when
 * upgrading the firmware and kernel. Therefore, we need to abstract well the
 * data structures to avoid tying our hands with future firmwares.
 *
 * The bulk of drm_asahi_cmd_render therefore consists of values of hardware
 * control registers, marshalled via the firmware interface.
 *
 * The framebuffer/tilebuffer dimensions are also specified here. In addition to
 * being passed to the firmware/hardware, the kernel requires these dimensions
 * to calculate various essential tiling-related data structures. It is
 * unfortunate that our submits are heavier than on vendors with saner
 * hardware-software interfaces. The upshot is all of this information is
 * readily available to userspace with all current APIs.
 *
 * It looks odd - but it's not overly burdensome and it ensures we can remain
 * compatible with old userspace.
 */
struct drm_asahi_cmd_render {
        /** @flags: Combination of drm_asahi_render_flags flags. */
        __u32 flags;

        /**
         * @isp_zls_pixels: ISP_ZLS_PIXELS register value. This contains the
         * depth/stencil width/height, which may differ from the framebuffer
         * width/height.
         */
        __u32 isp_zls_pixels;

        /**
         * @vdm_ctrl_stream_base: VDM_CTRL_STREAM_BASE register value. GPU
         * address to the beginning of the VDM control stream.
         */
        __u64 vdm_ctrl_stream_base;

        /** @vertex_helper: Helper program used for the vertex shader */
        struct drm_asahi_helper_program vertex_helper;

        /** @fragment_helper: Helper program used for the fragment shader */
        struct drm_asahi_helper_program fragment_helper;

        /**
         * @isp_scissor_base: ISP_SCISSOR_BASE register value. GPU address of an
         * array of scissor descriptors indexed in the render pass.
         */
        __u64 isp_scissor_base;

        /**
         * @isp_dbias_base: ISP_DBIAS_BASE register value. GPU address of an
         * array of depth bias values indexed in the render pass.
         */
        __u64 isp_dbias_base;

        /**
         * @isp_oclqry_base: ISP_OCLQRY_BASE register value. GPU address of an
         * array of occlusion query results written by the render pass.
         */
        __u64 isp_oclqry_base;

        /** @depth: Depth buffer */
        struct drm_asahi_zls_buffer depth;

        /** @stencil: Stencil buffer */
        struct drm_asahi_zls_buffer stencil;

        /** @zls_ctrl: ZLS_CTRL register value */
        __u64 zls_ctrl;

        /** @ppp_multisamplectl: PPP_MULTISAMPLECTL register value */
        __u64 ppp_multisamplectl;

        /**
         * @sampler_heap: Base address of the sampler heap. This heap is used
         * for both vertex shaders and fragment shaders. The registers are
         * per-stage, but there is no known use case for separate heaps.
         */
        __u64 sampler_heap;

        /** @ppp_ctrl: PPP_CTRL register value */
        __u32 ppp_ctrl;

        /** @width_px: Framebuffer width in pixels */
        __u16 width_px;

        /** @height_px: Framebuffer height in pixels */
        __u16 height_px;

        /** @layers: Number of layers in the framebuffer */
        __u16 layers;

        /** @sampler_count: Number of samplers in the sampler heap. */
        __u16 sampler_count;

        /** @utile_width_px: Width of a logical tilebuffer tile in pixels */
        __u8 utile_width_px;

        /** @utile_height_px: Height of a logical tilebuffer tile in pixels */
        __u8 utile_height_px;

        /** @samples: # of samples in the framebuffer. Must be 1, 2, or 4. */
        __u8 samples;

        /** @sample_size_B: # of bytes in the tilebuffer required per sample. */
        __u8 sample_size_B;

        /**
         * @isp_merge_upper_x: 32-bit float used in the hardware triangle
         * merging. Calculate as: tan(60 deg) * width.
         *
         * Making these values UAPI avoids requiring floating-point calculations
         * in the kernel in the hot path.
         */
        __u32 isp_merge_upper_x;

        /**
         * @isp_merge_upper_y: 32-bit float. Calculate as: tan(60 deg) * height.
         * See @isp_merge_upper_x.
         */
        __u32 isp_merge_upper_y;

        /** @bg: Background program run for each tile at the start */
        struct drm_asahi_bg_eot bg;

        /** @eot: End-of-tile program ran for each tile at the end */
        struct drm_asahi_bg_eot eot;

        /**
         * @partial_bg: Background program ran at the start of each tile when
         * resuming the render pass during a partial render.
         */
        struct drm_asahi_bg_eot partial_bg;

        /**
         * @partial_eot: End-of-tile program ran at the end of each tile when
         * pausing the render pass during a partial render.
         */
        struct drm_asahi_bg_eot partial_eot;

        /**
         * @isp_bgobjdepth: ISP_BGOBJDEPTH register value. This is the depth
         * buffer clear value, encoded in the depth buffer's format: either a
         * 32-bit float or a 16-bit unorm (with upper bits zeroed).
         */
        __u32 isp_bgobjdepth;

        /**
         * @isp_bgobjvals: ISP_BGOBJVALS register value. The bottom 8-bits
         * contain the stencil buffer clear value.
         */
        __u32 isp_bgobjvals;

        /** @ts_vtx: Timestamps for the vertex portion of the render */
        struct drm_asahi_timestamps ts_vtx;

        /** @ts_frag: Timestamps for the fragment portion of the render */
        struct drm_asahi_timestamps ts_frag;
};

/**
 * struct drm_asahi_cmd_compute - Command to submit compute
 *
 * This command submits a control stream consisting of compute dispatches. There
 * is essentially no limit on how many compute dispatches may be included in a
 * single compute command, although timestamps are at command granularity.
 */
struct drm_asahi_cmd_compute {
        /** @flags: MBZ */
        __u32 flags;

        /** @sampler_count: Number of samplers in the sampler heap. */
        __u32 sampler_count;

        /**
         * @cdm_ctrl_stream_base: CDM_CTRL_STREAM_BASE register value. GPU
         * address to the beginning of the CDM control stream.
         */
        __u64 cdm_ctrl_stream_base;

        /**
         * @cdm_ctrl_stream_end: GPU base address to the end of the hardware
         * control stream. Note this only considers the first contiguous segment
         * of the control stream, as the stream might jump elsewhere.
         */
        __u64 cdm_ctrl_stream_end;

        /** @sampler_heap: Base address of the sampler heap. */
        __u64 sampler_heap;

        /** @helper: Helper program used for this compute command */
        struct drm_asahi_helper_program helper;

        /** @ts: Timestamps for the compute command */
        struct drm_asahi_timestamps ts;
};

/**
 * struct drm_asahi_get_time - Arguments passed to DRM_IOCTL_ASAHI_GET_TIME
 */
struct drm_asahi_get_time {
        /** @flags: MBZ. */
        __u64 flags;

        /** @gpu_timestamp: On return, the GPU timestamp in nanoseconds. */
        __u64 gpu_timestamp;
};

/**
 * DRM_IOCTL_ASAHI() - Build an Asahi IOCTL number
 * @__access: Access type. Must be R, W or RW.
 * @__id: One of the DRM_ASAHI_xxx id.
 * @__type: Suffix of the type being passed to the IOCTL.
 *
 * Don't use this macro directly, use the DRM_IOCTL_ASAHI_xxx
 * values instead.
 *
 * Return: An IOCTL number to be passed to ioctl() from userspace.
 */
#define DRM_IOCTL_ASAHI(__access, __id, __type) \
        DRM_IO ## __access(DRM_COMMAND_BASE + DRM_ASAHI_ ## __id, \
                           struct drm_asahi_ ## __type)

/* Note: this is an enum so that it can be resolved by Rust bindgen. */
enum {
        DRM_IOCTL_ASAHI_GET_PARAMS       = DRM_IOCTL_ASAHI(W, GET_PARAMS, get_params),
        DRM_IOCTL_ASAHI_GET_TIME         = DRM_IOCTL_ASAHI(WR, GET_TIME, get_time),
        DRM_IOCTL_ASAHI_VM_CREATE        = DRM_IOCTL_ASAHI(WR, VM_CREATE, vm_create),
        DRM_IOCTL_ASAHI_VM_DESTROY       = DRM_IOCTL_ASAHI(W, VM_DESTROY, vm_destroy),
        DRM_IOCTL_ASAHI_VM_BIND          = DRM_IOCTL_ASAHI(W, VM_BIND, vm_bind),
        DRM_IOCTL_ASAHI_GEM_CREATE       = DRM_IOCTL_ASAHI(WR, GEM_CREATE, gem_create),
        DRM_IOCTL_ASAHI_GEM_MMAP_OFFSET  = DRM_IOCTL_ASAHI(WR, GEM_MMAP_OFFSET, gem_mmap_offset),
        DRM_IOCTL_ASAHI_GEM_BIND_OBJECT  = DRM_IOCTL_ASAHI(WR, GEM_BIND_OBJECT, gem_bind_object),
        DRM_IOCTL_ASAHI_QUEUE_CREATE     = DRM_IOCTL_ASAHI(WR, QUEUE_CREATE, queue_create),
        DRM_IOCTL_ASAHI_QUEUE_DESTROY    = DRM_IOCTL_ASAHI(W, QUEUE_DESTROY, queue_destroy),
        DRM_IOCTL_ASAHI_SUBMIT           = DRM_IOCTL_ASAHI(W, SUBMIT, submit),
};

#if defined(__cplusplus)
}
#endif

#endif /* _ASAHI_DRM_H_ */