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