Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/drm/core/drm_flip_work.c
- This file was added.
/* | |||||
* Copyright (C) 2013 Red Hat | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a | |||||
* copy of this software and associated documentation files (the "Software"), | |||||
* to deal in the Software without restriction, including without limitation | |||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||||
* and/or sell copies of the Software, and to permit persons to whom the | |||||
* Software is furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice and this permission notice (including the next | |||||
* paragraph) shall be included in all copies or substantial portions of the | |||||
* Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
* SOFTWARE. | |||||
*/ | |||||
#include <linux/slab.h> | |||||
/* FBSD: Linux header polution, we need list.h */ | |||||
#include <linux/list.h> | |||||
#include <drm/drm_flip_work.h> | |||||
#include <drm/drm_print.h> | |||||
#include <drm/drm_util.h> | |||||
/** | |||||
* drm_flip_work_allocate_task - allocate a flip-work task | |||||
* @data: data associated to the task | |||||
* @flags: allocator flags | |||||
* | |||||
* Allocate a drm_flip_task object and attach private data to it. | |||||
*/ | |||||
struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags) | |||||
{ | |||||
struct drm_flip_task *task; | |||||
task = kzalloc(sizeof(*task), flags); | |||||
if (task) | |||||
task->data = data; | |||||
return task; | |||||
} | |||||
EXPORT_SYMBOL(drm_flip_work_allocate_task); | |||||
/** | |||||
* drm_flip_work_queue_task - queue a specific task | |||||
* @work: the flip-work | |||||
* @task: the task to handle | |||||
* | |||||
* Queues task, that will later be run (passed back to drm_flip_func_t | |||||
* func) on a work queue after drm_flip_work_commit() is called. | |||||
*/ | |||||
void drm_flip_work_queue_task(struct drm_flip_work *work, | |||||
struct drm_flip_task *task) | |||||
{ | |||||
unsigned long flags; | |||||
spin_lock_irqsave(&work->lock, flags); | |||||
list_add_tail(&task->node, &work->queued); | |||||
spin_unlock_irqrestore(&work->lock, flags); | |||||
} | |||||
EXPORT_SYMBOL(drm_flip_work_queue_task); | |||||
/** | |||||
* drm_flip_work_queue - queue work | |||||
* @work: the flip-work | |||||
* @val: the value to queue | |||||
* | |||||
* Queues work, that will later be run (passed back to drm_flip_func_t | |||||
* func) on a work queue after drm_flip_work_commit() is called. | |||||
*/ | |||||
void drm_flip_work_queue(struct drm_flip_work *work, void *val) | |||||
{ | |||||
struct drm_flip_task *task; | |||||
task = drm_flip_work_allocate_task(val, | |||||
drm_can_sleep() ? GFP_KERNEL : GFP_ATOMIC); | |||||
if (task) { | |||||
drm_flip_work_queue_task(work, task); | |||||
} else { | |||||
DRM_ERROR("%s could not allocate task!\n", work->name); | |||||
work->func(work, val); | |||||
} | |||||
} | |||||
EXPORT_SYMBOL(drm_flip_work_queue); | |||||
/** | |||||
* drm_flip_work_commit - commit queued work | |||||
* @work: the flip-work | |||||
* @wq: the work-queue to run the queued work on | |||||
* | |||||
* Trigger work previously queued by drm_flip_work_queue() to run | |||||
* on a workqueue. The typical usage would be to queue work (via | |||||
* drm_flip_work_queue()) at any point (from vblank irq and/or | |||||
* prior), and then from vblank irq commit the queued work. | |||||
*/ | |||||
void drm_flip_work_commit(struct drm_flip_work *work, | |||||
struct workqueue_struct *wq) | |||||
{ | |||||
unsigned long flags; | |||||
spin_lock_irqsave(&work->lock, flags); | |||||
list_splice_tail(&work->queued, &work->commited); | |||||
INIT_LIST_HEAD(&work->queued); | |||||
spin_unlock_irqrestore(&work->lock, flags); | |||||
queue_work(wq, &work->worker); | |||||
} | |||||
EXPORT_SYMBOL(drm_flip_work_commit); | |||||
static void flip_worker(struct work_struct *w) | |||||
{ | |||||
struct drm_flip_work *work = container_of(w, struct drm_flip_work, worker); | |||||
struct list_head tasks; | |||||
unsigned long flags; | |||||
while (1) { | |||||
struct drm_flip_task *task, *tmp; | |||||
INIT_LIST_HEAD(&tasks); | |||||
spin_lock_irqsave(&work->lock, flags); | |||||
list_splice_tail(&work->commited, &tasks); | |||||
INIT_LIST_HEAD(&work->commited); | |||||
spin_unlock_irqrestore(&work->lock, flags); | |||||
if (list_empty(&tasks)) | |||||
break; | |||||
list_for_each_entry_safe(task, tmp, &tasks, node) { | |||||
work->func(work, task->data); | |||||
kfree(task); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* drm_flip_work_init - initialize flip-work | |||||
* @work: the flip-work to initialize | |||||
* @name: debug name | |||||
* @func: the callback work function | |||||
* | |||||
* Initializes/allocates resources for the flip-work | |||||
*/ | |||||
void drm_flip_work_init(struct drm_flip_work *work, | |||||
const char *name, drm_flip_func_t func) | |||||
{ | |||||
work->name = name; | |||||
INIT_LIST_HEAD(&work->queued); | |||||
INIT_LIST_HEAD(&work->commited); | |||||
spin_lock_init(&work->lock); | |||||
work->func = func; | |||||
INIT_WORK(&work->worker, flip_worker); | |||||
} | |||||
EXPORT_SYMBOL(drm_flip_work_init); | |||||
/** | |||||
* drm_flip_work_cleanup - cleans up flip-work | |||||
* @work: the flip-work to cleanup | |||||
* | |||||
* Destroy resources allocated for the flip-work | |||||
*/ | |||||
void drm_flip_work_cleanup(struct drm_flip_work *work) | |||||
{ | |||||
WARN_ON(!list_empty(&work->queued) || !list_empty(&work->commited)); | |||||
} | |||||
EXPORT_SYMBOL(drm_flip_work_cleanup); |