#include <drm/drm_gem.h>
#include "i915_active.h"
#include "i915_vma.h"
#include "intel_bo.h"
#include "intel_display_trace.h"
#include "intel_display_types.h"
#include "intel_dp.h"
#include "intel_drrs.h"
#include "intel_fbc.h"
#include "intel_frontbuffer.h"
#include "intel_psr.h"
#include "intel_tdf.h"
static void frontbuffer_flush(struct intel_display *display,
unsigned int frontbuffer_bits,
enum fb_op_origin origin)
{
spin_lock(&display->fb_tracking.lock);
frontbuffer_bits &= ~display->fb_tracking.busy_bits;
spin_unlock(&display->fb_tracking.lock);
if (!frontbuffer_bits)
return;
trace_intel_frontbuffer_flush(display, frontbuffer_bits, origin);
might_sleep();
intel_td_flush(display);
intel_drrs_flush(display, frontbuffer_bits);
intel_psr_flush(display, frontbuffer_bits, origin);
intel_fbc_flush(display, frontbuffer_bits, origin);
}
void intel_frontbuffer_flip_prepare(struct intel_display *display,
unsigned frontbuffer_bits)
{
spin_lock(&display->fb_tracking.lock);
display->fb_tracking.flip_bits |= frontbuffer_bits;
display->fb_tracking.busy_bits &= ~frontbuffer_bits;
spin_unlock(&display->fb_tracking.lock);
}
void intel_frontbuffer_flip_complete(struct intel_display *display,
unsigned frontbuffer_bits)
{
spin_lock(&display->fb_tracking.lock);
frontbuffer_bits &= display->fb_tracking.flip_bits;
display->fb_tracking.flip_bits &= ~frontbuffer_bits;
spin_unlock(&display->fb_tracking.lock);
if (frontbuffer_bits)
frontbuffer_flush(display, frontbuffer_bits, ORIGIN_FLIP);
}
void intel_frontbuffer_flip(struct intel_display *display,
unsigned frontbuffer_bits)
{
spin_lock(&display->fb_tracking.lock);
display->fb_tracking.busy_bits &= ~frontbuffer_bits;
spin_unlock(&display->fb_tracking.lock);
frontbuffer_flush(display, frontbuffer_bits, ORIGIN_FLIP);
}
void __intel_fb_invalidate(struct intel_frontbuffer *front,
enum fb_op_origin origin,
unsigned int frontbuffer_bits)
{
struct intel_display *display = to_intel_display(front->obj->dev);
if (origin == ORIGIN_CS) {
spin_lock(&display->fb_tracking.lock);
display->fb_tracking.busy_bits |= frontbuffer_bits;
display->fb_tracking.flip_bits &= ~frontbuffer_bits;
spin_unlock(&display->fb_tracking.lock);
}
trace_intel_frontbuffer_invalidate(display, frontbuffer_bits, origin);
might_sleep();
intel_psr_invalidate(display, frontbuffer_bits, origin);
intel_drrs_invalidate(display, frontbuffer_bits);
intel_fbc_invalidate(display, frontbuffer_bits, origin);
}
void __intel_fb_flush(struct intel_frontbuffer *front,
enum fb_op_origin origin,
unsigned int frontbuffer_bits)
{
struct intel_display *display = to_intel_display(front->obj->dev);
if (origin == ORIGIN_CS) {
spin_lock(&display->fb_tracking.lock);
frontbuffer_bits &= display->fb_tracking.busy_bits;
display->fb_tracking.busy_bits &= ~frontbuffer_bits;
spin_unlock(&display->fb_tracking.lock);
}
if (frontbuffer_bits)
frontbuffer_flush(display, frontbuffer_bits, origin);
}
static void intel_frontbuffer_flush_work(struct work_struct *work)
{
struct intel_frontbuffer *front =
container_of(work, struct intel_frontbuffer, flush_work);
intel_bo_flush_if_display(front->obj);
intel_frontbuffer_flush(front, ORIGIN_DIRTYFB);
intel_frontbuffer_put(front);
}
void intel_frontbuffer_queue_flush(struct intel_frontbuffer *front)
{
if (!front)
return;
kref_get(&front->ref);
if (!schedule_work(&front->flush_work))
intel_frontbuffer_put(front);
}
static int frontbuffer_active(struct i915_active *ref)
{
struct intel_frontbuffer *front =
container_of(ref, typeof(*front), write);
kref_get(&front->ref);
return 0;
}
static void frontbuffer_retire(struct i915_active *ref)
{
struct intel_frontbuffer *front =
container_of(ref, typeof(*front), write);
intel_frontbuffer_flush(front, ORIGIN_CS);
intel_frontbuffer_put(front);
}
static void frontbuffer_release(struct kref *ref)
__releases(&to_intel_display(front->obj->dev)->fb_tracking.lock)
{
struct intel_frontbuffer *ret, *front =
container_of(ref, typeof(*front), ref);
struct drm_gem_object *obj = front->obj;
struct intel_display *display = to_intel_display(obj->dev);
drm_WARN_ON(display->drm, atomic_read(&front->bits));
i915_ggtt_clear_scanout(to_intel_bo(obj));
ret = intel_bo_set_frontbuffer(obj, NULL);
drm_WARN_ON(display->drm, ret);
spin_unlock(&display->fb_tracking.lock);
i915_active_fini(&front->write);
drm_gem_object_put(obj);
kfree_rcu(front, rcu);
}
struct intel_frontbuffer *
intel_frontbuffer_get(struct drm_gem_object *obj)
{
struct intel_display *display = to_intel_display(obj->dev);
struct intel_frontbuffer *front, *cur;
front = intel_bo_get_frontbuffer(obj);
if (front)
return front;
front = kmalloc(sizeof(*front), GFP_KERNEL);
if (!front)
return NULL;
drm_gem_object_get(obj);
front->obj = obj;
kref_init(&front->ref);
atomic_set(&front->bits, 0);
i915_active_init(&front->write,
frontbuffer_active,
frontbuffer_retire,
I915_ACTIVE_RETIRE_SLEEPS);
INIT_WORK(&front->flush_work, intel_frontbuffer_flush_work);
spin_lock(&display->fb_tracking.lock);
cur = intel_bo_set_frontbuffer(obj, front);
spin_unlock(&display->fb_tracking.lock);
if (cur != front) {
drm_gem_object_put(obj);
kfree(front);
}
return cur;
}
void intel_frontbuffer_put(struct intel_frontbuffer *front)
{
kref_put_lock(&front->ref,
frontbuffer_release,
&to_intel_display(front->obj->dev)->fb_tracking.lock);
}
void intel_frontbuffer_track(struct intel_frontbuffer *old,
struct intel_frontbuffer *new,
unsigned int frontbuffer_bits)
{
BUILD_BUG_ON(INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES >
BITS_PER_TYPE(atomic_t));
BUILD_BUG_ON(INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES > 32);
BUILD_BUG_ON(I915_MAX_PLANES > INTEL_FRONTBUFFER_BITS_PER_PIPE);
if (old) {
struct intel_display *display = to_intel_display(old->obj->dev);
drm_WARN_ON(display->drm,
!(atomic_read(&old->bits) & frontbuffer_bits));
atomic_andnot(frontbuffer_bits, &old->bits);
}
if (new) {
struct intel_display *display = to_intel_display(new->obj->dev);
drm_WARN_ON(display->drm,
atomic_read(&new->bits) & frontbuffer_bits);
atomic_or(frontbuffer_bits, &new->bits);
}
}