diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/core/event.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/core/event.c | 119 |
1 files changed, 86 insertions, 33 deletions
diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c index 7eb81c1b6fab..3f3c76581a9e 100644 --- a/drivers/gpu/drm/nouveau/core/core/event.c +++ b/drivers/gpu/drm/nouveau/core/core/event.c @@ -23,62 +23,114 @@ #include <core/os.h> #include <core/event.h> -static void -nouveau_event_put_locked(struct nouveau_event *event, int index, - struct nouveau_eventh *handler) +void +nouveau_event_put(struct nouveau_eventh *handler) { - if (!--event->index[index].refs) { - if (event->disable) - event->disable(event, index); + struct nouveau_event *event = handler->event; + unsigned long flags; + if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + if (!--event->index[handler->index].refs) { + if (event->disable) + event->disable(event, handler->index); + } + spin_unlock_irqrestore(&event->refs_lock, flags); } - list_del(&handler->head); } void -nouveau_event_put(struct nouveau_event *event, int index, - struct nouveau_eventh *handler) +nouveau_event_get(struct nouveau_eventh *handler) { + struct nouveau_event *event = handler->event; unsigned long flags; + if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + if (!event->index[handler->index].refs++) { + if (event->enable) + event->enable(event, handler->index); + } + spin_unlock_irqrestore(&event->refs_lock, flags); + } +} - spin_lock_irqsave(&event->lock, flags); - if (index < event->index_nr) - nouveau_event_put_locked(event, index, handler); - spin_unlock_irqrestore(&event->lock, flags); +static void +nouveau_event_fini(struct nouveau_eventh *handler) +{ + struct nouveau_event *event = handler->event; + unsigned long flags; + nouveau_event_put(handler); + spin_lock_irqsave(&event->list_lock, flags); + list_del(&handler->head); + spin_unlock_irqrestore(&event->list_lock, flags); } -void -nouveau_event_get(struct nouveau_event *event, int index, - struct nouveau_eventh *handler) +static int +nouveau_event_init(struct nouveau_event *event, int index, + int (*func)(void *, int), void *priv, + struct nouveau_eventh *handler) { unsigned long flags; - spin_lock_irqsave(&event->lock, flags); - if (index < event->index_nr) { - list_add(&handler->head, &event->index[index].list); - if (!event->index[index].refs++) { - if (event->enable) - event->enable(event, index); - } + if (index >= event->index_nr) + return -EINVAL; + + handler->event = event; + handler->flags = 0; + handler->index = index; + handler->func = func; + handler->priv = priv; + + spin_lock_irqsave(&event->list_lock, flags); + list_add_tail(&handler->head, &event->index[index].list); + spin_unlock_irqrestore(&event->list_lock, flags); + return 0; +} + +int +nouveau_event_new(struct nouveau_event *event, int index, + int (*func)(void *, int), void *priv, + struct nouveau_eventh **phandler) +{ + struct nouveau_eventh *handler; + int ret = -ENOMEM; + + handler = *phandler = kmalloc(sizeof(*handler), GFP_KERNEL); + if (handler) { + ret = nouveau_event_init(event, index, func, priv, handler); + if (ret) + kfree(handler); } - spin_unlock_irqrestore(&event->lock, flags); + + return ret; +} + +void +nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref) +{ + BUG_ON(handler != NULL); + if (*ref) { + nouveau_event_fini(*ref); + kfree(*ref); + } + *ref = handler; } void nouveau_event_trigger(struct nouveau_event *event, int index) { - struct nouveau_eventh *handler, *temp; + struct nouveau_eventh *handler; unsigned long flags; - if (index >= event->index_nr) + if (WARN_ON(index >= event->index_nr)) return; - spin_lock_irqsave(&event->lock, flags); - list_for_each_entry_safe(handler, temp, &event->index[index].list, head) { - if (handler->func(handler, index) == NVKM_EVENT_DROP) { - nouveau_event_put_locked(event, index, handler); - } + spin_lock_irqsave(&event->list_lock, flags); + list_for_each_entry(handler, &event->index[index].list, head) { + if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) && + handler->func(handler->priv, index) == NVKM_EVENT_DROP) + nouveau_event_put(handler); } - spin_unlock_irqrestore(&event->lock, flags); + spin_unlock_irqrestore(&event->list_lock, flags); } void @@ -102,7 +154,8 @@ nouveau_event_create(int index_nr, struct nouveau_event **pevent) if (!event) return -ENOMEM; - spin_lock_init(&event->lock); + spin_lock_init(&event->list_lock); + spin_lock_init(&event->refs_lock); for (i = 0; i < index_nr; i++) INIT_LIST_HEAD(&event->index[i].list); event->index_nr = index_nr; |