root/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.c
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */

#include "i915_drv.h"
#include "i915_gem_object_frontbuffer.h"

static int frontbuffer_active(struct i915_active *ref)
{
        struct i915_frontbuffer *front =
                container_of(ref, typeof(*front), write);

        kref_get(&front->ref);
        return 0;
}

static void frontbuffer_retire(struct i915_active *ref)
{
        struct i915_frontbuffer *front =
                container_of(ref, typeof(*front), write);

        intel_frontbuffer_flush(&front->base, ORIGIN_CS);
        i915_gem_object_frontbuffer_put(front);
}

struct i915_frontbuffer *
i915_gem_object_frontbuffer_get(struct drm_i915_gem_object *obj)
{
        struct drm_i915_private *i915 = to_i915(obj->base.dev);
        struct i915_frontbuffer *front, *cur;

        front = i915_gem_object_frontbuffer_lookup(obj);
        if (front)
                return front;

        front = kmalloc_obj(*front);
        if (!front)
                return NULL;

        intel_frontbuffer_init(&front->base, &i915->drm);

        kref_init(&front->ref);
        i915_gem_object_get(obj);
        front->obj = obj;

        i915_active_init(&front->write,
                         frontbuffer_active,
                         frontbuffer_retire,
                         I915_ACTIVE_RETIRE_SLEEPS);

        spin_lock(&i915->frontbuffer_lock);
        if (rcu_access_pointer(obj->frontbuffer)) {
                cur = rcu_dereference_protected(obj->frontbuffer, true);
                kref_get(&cur->ref);
        } else {
                cur = front;
                rcu_assign_pointer(obj->frontbuffer, front);
        }
        spin_unlock(&i915->frontbuffer_lock);

        if (cur != front) {
                i915_gem_object_put(obj);
                intel_frontbuffer_fini(&front->base);
                kfree(front);
        }

        return cur;
}

void i915_gem_object_frontbuffer_ref(struct i915_frontbuffer *front)
{
        kref_get(&front->ref);
}

static void frontbuffer_release(struct kref *ref)
        __releases(&i915->frontbuffer_lock)
{
        struct i915_frontbuffer *front =
                container_of(ref, typeof(*front), ref);
        struct drm_i915_gem_object *obj = front->obj;
        struct drm_i915_private *i915 = to_i915(obj->base.dev);

        i915_ggtt_clear_scanout(obj);

        RCU_INIT_POINTER(obj->frontbuffer, NULL);

        spin_unlock(&i915->frontbuffer_lock);

        i915_active_fini(&front->write);

        i915_gem_object_put(obj);

        intel_frontbuffer_fini(&front->base);

        kfree_rcu(front, rcu);
}

void i915_gem_object_frontbuffer_put(struct i915_frontbuffer *front)
{
        struct drm_i915_private *i915 = to_i915(front->obj->base.dev);

        kref_put_lock(&front->ref, frontbuffer_release,
                      &i915->frontbuffer_lock);
}