linux/drivers/gpu/drm/drm_flip_work.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 Red Hat
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice (including the next
  12 * paragraph) shall be included in all copies or substantial portions of the
  13 * Software.
  14 *
  15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 * SOFTWARE.
  22 */
  23
  24#include "drmP.h"
  25#include "drm_flip_work.h"
  26
  27/**
  28 * drm_flip_work_allocate_task - allocate a flip-work task
  29 * @data: data associated to the task
  30 * @flags: allocator flags
  31 *
  32 * Allocate a drm_flip_task object and attach private data to it.
  33 */
  34struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags)
  35{
  36        struct drm_flip_task *task;
  37
  38        task = kzalloc(sizeof(*task), flags);
  39        if (task)
  40                task->data = data;
  41
  42        return task;
  43}
  44EXPORT_SYMBOL(drm_flip_work_allocate_task);
  45
  46/**
  47 * drm_flip_work_queue_task - queue a specific task
  48 * @work: the flip-work
  49 * @task: the task to handle
  50 *
  51 * Queues task, that will later be run (passed back to drm_flip_func_t
  52 * func) on a work queue after drm_flip_work_commit() is called.
  53 */
  54void drm_flip_work_queue_task(struct drm_flip_work *work,
  55                              struct drm_flip_task *task)
  56{
  57        unsigned long flags;
  58
  59        spin_lock_irqsave(&work->lock, flags);
  60        list_add_tail(&task->node, &work->queued);
  61        spin_unlock_irqrestore(&work->lock, flags);
  62}
  63EXPORT_SYMBOL(drm_flip_work_queue_task);
  64
  65/**
  66 * drm_flip_work_queue - queue work
  67 * @work: the flip-work
  68 * @val: the value to queue
  69 *
  70 * Queues work, that will later be run (passed back to drm_flip_func_t
  71 * func) on a work queue after drm_flip_work_commit() is called.
  72 */
  73void drm_flip_work_queue(struct drm_flip_work *work, void *val)
  74{
  75        struct drm_flip_task *task;
  76
  77        task = drm_flip_work_allocate_task(val,
  78                                drm_can_sleep() ? GFP_KERNEL : GFP_ATOMIC);
  79        if (task) {
  80                drm_flip_work_queue_task(work, task);
  81        } else {
  82                DRM_ERROR("%s could not allocate task!\n", work->name);
  83                work->func(work, val);
  84        }
  85}
  86EXPORT_SYMBOL(drm_flip_work_queue);
  87
  88/**
  89 * drm_flip_work_commit - commit queued work
  90 * @work: the flip-work
  91 * @wq: the work-queue to run the queued work on
  92 *
  93 * Trigger work previously queued by drm_flip_work_queue() to run
  94 * on a workqueue.  The typical usage would be to queue work (via
  95 * drm_flip_work_queue()) at any point (from vblank irq and/or
  96 * prior), and then from vblank irq commit the queued work.
  97 */
  98void drm_flip_work_commit(struct drm_flip_work *work,
  99                struct workqueue_struct *wq)
 100{
 101        unsigned long flags;
 102
 103        spin_lock_irqsave(&work->lock, flags);
 104        list_splice_tail(&work->queued, &work->commited);
 105        INIT_LIST_HEAD(&work->queued);
 106        spin_unlock_irqrestore(&work->lock, flags);
 107        queue_work(wq, &work->worker);
 108}
 109EXPORT_SYMBOL(drm_flip_work_commit);
 110
 111static void flip_worker(struct work_struct *w)
 112{
 113        struct drm_flip_work *work = container_of(w, struct drm_flip_work, worker);
 114        struct list_head tasks;
 115        unsigned long flags;
 116
 117        while (1) {
 118                struct drm_flip_task *task, *tmp;
 119
 120                INIT_LIST_HEAD(&tasks);
 121                spin_lock_irqsave(&work->lock, flags);
 122                list_splice_tail(&work->commited, &tasks);
 123                INIT_LIST_HEAD(&work->commited);
 124                spin_unlock_irqrestore(&work->lock, flags);
 125
 126                if (list_empty(&tasks))
 127                        break;
 128
 129                list_for_each_entry_safe(task, tmp, &tasks, node) {
 130                        work->func(work, task->data);
 131                        kfree(task);
 132                }
 133        }
 134}
 135
 136/**
 137 * drm_flip_work_init - initialize flip-work
 138 * @work: the flip-work to initialize
 139 * @name: debug name
 140 * @func: the callback work function
 141 *
 142 * Initializes/allocates resources for the flip-work
 143 */
 144void drm_flip_work_init(struct drm_flip_work *work,
 145                const char *name, drm_flip_func_t func)
 146{
 147        work->name = name;
 148        INIT_LIST_HEAD(&work->queued);
 149        INIT_LIST_HEAD(&work->commited);
 150        spin_lock_init(&work->lock);
 151        work->func = func;
 152
 153        INIT_WORK(&work->worker, flip_worker);
 154}
 155EXPORT_SYMBOL(drm_flip_work_init);
 156
 157/**
 158 * drm_flip_work_cleanup - cleans up flip-work
 159 * @work: the flip-work to cleanup
 160 *
 161 * Destroy resources allocated for the flip-work
 162 */
 163void drm_flip_work_cleanup(struct drm_flip_work *work)
 164{
 165        WARN_ON(!list_empty(&work->queued) || !list_empty(&work->commited));
 166}
 167EXPORT_SYMBOL(drm_flip_work_cleanup);
 168