drm/nouveau/core: convert event handler apis to split create/enable semantics
authorBen Skeggs <bskeggs@redhat.com>
Wed, 2 Oct 2013 21:02:29 +0000 (07:02 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 8 Nov 2013 05:36:06 +0000 (15:36 +1000)
This is a necessary step towards being able to work with the insane locking
requirements of the DRM core's vblank routines, and a nice cleanup as a
side-effect.

This is similar in spirit to the interfaces that Peter Hurley arrived at
with his nouveau_event rcu conversion series.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
12 files changed:
drivers/gpu/drm/nouveau/core/core/event.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.h
drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
drivers/gpu/drm/nouveau/core/include/core/event.h
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_display.h
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drm.h
drivers/gpu/drm/nouveau/nouveau_fence.c

index 9393c37..313afcc 100644 (file)
@@ -37,39 +37,82 @@ nouveau_event_put_locked(struct nouveau_event *event, int index,
 }
 
 void
-nouveau_event_put(struct nouveau_event *event, int index,
-                 struct nouveau_eventh *handler)
+nouveau_event_put(struct nouveau_eventh *handler)
 {
+       struct nouveau_event *event = handler->event;
        unsigned long flags;
-
-       if (index >= event->index_nr)
-               return;
-
        spin_lock_irqsave(&event->lock, flags);
-       nouveau_event_put_locked(event, index, handler);
+       nouveau_event_put_locked(handler->event, handler->index, handler);
        spin_unlock_irqrestore(&event->lock, flags);
 }
 
 void
-nouveau_event_get(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 (index >= event->index_nr)
-               return;
-
        spin_lock_irqsave(&event->lock, flags);
        if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
-               list_add(&handler->head, &event->index[index].list);
-               if (!event->index[index].refs++) {
+               list_add(&handler->head, &event->index[handler->index].list);
+               if (!event->index[handler->index].refs++) {
                        if (event->enable)
-                               event->enable(event, index);
+                               event->enable(event, handler->index);
                }
        }
        spin_unlock_irqrestore(&event->lock, flags);
 }
 
+static void
+nouveau_event_fini(struct nouveau_eventh *handler)
+{
+       nouveau_event_put(handler);
+}
+
+static int
+nouveau_event_init(struct nouveau_event *event, int index,
+                  int (*func)(void *, int), void *priv,
+                  struct nouveau_eventh *handler)
+{
+       if (index >= event->index_nr)
+               return -EINVAL;
+       handler->event = event;
+       handler->flags = 0;
+       handler->index = index;
+       handler->func = func;
+       handler->priv = priv;
+       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);
+       }
+
+       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)
 {
@@ -81,7 +124,7 @@ nouveau_event_trigger(struct nouveau_event *event, int index)
 
        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) {
+               if (handler->func(handler->priv, index) == NVKM_EVENT_DROP) {
                        nouveau_event_put_locked(event, index, handler);
                }
        }
index 5236a14..b574dd4 100644 (file)
@@ -71,7 +71,7 @@ nv50_software_mthd_vblsem_offset(struct nouveau_object *object, u32 mthd,
        return 0;
 }
 
-static int
+int
 nv50_software_mthd_vblsem_value(struct nouveau_object *object, u32 mthd,
                                void *args, u32 size)
 {
@@ -80,21 +80,20 @@ nv50_software_mthd_vblsem_value(struct nouveau_object *object, u32 mthd,
        return 0;
 }
 
-static int
+int
 nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
                                  void *args, u32 size)
 {
        struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
-       struct nouveau_disp *disp = nouveau_disp(object);
-       u32 crtc = *(u32 *)args;
-       if (crtc > 1)
+       u32 head = *(u32 *)args;
+       if (head >= chan->vblank.nr_event)
                return -EINVAL;
 
-       nouveau_event_get(disp->vblank, crtc, &chan->vblank.event);
+       nouveau_event_get(chan->vblank.event[head]);
        return 0;
 }
 
-static int
+int
 nv50_software_mthd_flip(struct nouveau_object *object, u32 mthd,
                        void *args, u32 size)
 {
@@ -125,10 +124,9 @@ nv50_software_sclass[] = {
  ******************************************************************************/
 
 static int
-nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
+nv50_software_vblsem_release(void *data, int head)
 {
-       struct nv50_software_chan *chan =
-               container_of(event, typeof(*chan), vblank.event);
+       struct nv50_software_chan *chan = data;
        struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
        struct nouveau_bar *bar = nouveau_bar(priv);
 
@@ -147,23 +145,51 @@ nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
        return NVKM_EVENT_DROP;
 }
 
+void
+nv50_software_context_dtor(struct nouveau_object *object)
+{
+       struct nv50_software_chan *chan = (void *)object;
+       int i;
+
+       if (chan->vblank.event) {
+               for (i = 0; i < chan->vblank.nr_event; i++)
+                       nouveau_event_ref(NULL, &chan->vblank.event[i]);
+               kfree(chan->vblank.event);
+       }
+
+       nouveau_software_context_destroy(&chan->base);
+}
+
 int
 nv50_software_context_ctor(struct nouveau_object *parent,
                           struct nouveau_object *engine,
                           struct nouveau_oclass *oclass, void *data, u32 size,
                           struct nouveau_object **pobject)
 {
+       struct nouveau_disp *pdisp = nouveau_disp(parent);
        struct nv50_software_cclass *pclass = (void *)oclass;
        struct nv50_software_chan *chan;
-       int ret;
+       int ret, i;
 
        ret = nouveau_software_context_create(parent, engine, oclass, &chan);
        *pobject = nv_object(chan);
        if (ret)
                return ret;
 
+       chan->vblank.nr_event = pdisp->vblank->index_nr;
+       chan->vblank.event = kzalloc(chan->vblank.nr_event *
+                                    sizeof(*chan->vblank.event), GFP_KERNEL);
+       if (!chan->vblank.event)
+               return -ENOMEM;
+
+       for (i = 0; i < chan->vblank.nr_event; i++) {
+               ret = nouveau_event_new(pdisp->vblank, i, pclass->vblank,
+                                       chan, &chan->vblank.event[i]);
+               if (ret)
+                       return ret;
+       }
+
        chan->vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
-       chan->vblank.event.func = pclass->vblank;
        return 0;
 }
 
index 2c0b1b4..2de370c 100644 (file)
@@ -19,13 +19,14 @@ int  nv50_software_ctor(struct nouveau_object *, struct nouveau_object *,
 
 struct nv50_software_cclass {
        struct nouveau_oclass base;
-       int (*vblank)(struct nouveau_eventh *, int);
+       int (*vblank)(void *, int);
 };
 
 struct nv50_software_chan {
        struct nouveau_software_chan base;
        struct {
-               struct nouveau_eventh event;
+               struct nouveau_eventh **event;
+               int nr_event;
                u32 channel;
                u32 ctxdma;
                u64 offset;
@@ -37,5 +38,10 @@ int  nv50_software_context_ctor(struct nouveau_object *,
                                struct nouveau_object *,
                                struct nouveau_oclass *, void *, u32,
                                struct nouveau_object **);
+void nv50_software_context_dtor(struct nouveau_object *);
+
+int nv50_software_mthd_vblsem_value(struct nouveau_object *, u32, void *, u32);
+int nv50_software_mthd_vblsem_release(struct nouveau_object *, u32, void *, u32);
+int nv50_software_mthd_flip(struct nouveau_object *, u32, void *, u32);
 
 #endif
index 27e4723..f9430c1 100644 (file)
@@ -55,40 +55,6 @@ nvc0_software_mthd_vblsem_offset(struct nouveau_object *object, u32 mthd,
 }
 
 static int
-nvc0_software_mthd_vblsem_value(struct nouveau_object *object, u32 mthd,
-                               void *args, u32 size)
-{
-       struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
-       chan->vblank.value = *(u32 *)args;
-       return 0;
-}
-
-static int
-nvc0_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
-                                 void *args, u32 size)
-{
-       struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
-       struct nouveau_disp *disp = nouveau_disp(object);
-       u32 crtc = *(u32 *)args;
-
-       if ((nv_device(object)->card_type < NV_E0 && crtc > 1) || crtc > 3)
-               return -EINVAL;
-
-       nouveau_event_get(disp->vblank, crtc, &chan->vblank.event);
-       return 0;
-}
-
-static int
-nvc0_software_mthd_flip(struct nouveau_object *object, u32 mthd,
-                       void *args, u32 size)
-{
-       struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
-       if (chan->base.flip)
-               return chan->base.flip(chan->base.flip_data);
-       return -EINVAL;
-}
-
-static int
 nvc0_software_mthd_mp_control(struct nouveau_object *object, u32 mthd,
                               void *args, u32 size)
 {
@@ -118,9 +84,9 @@ static struct nouveau_omthds
 nvc0_software_omthds[] = {
        { 0x0400, 0x0400, nvc0_software_mthd_vblsem_offset },
        { 0x0404, 0x0404, nvc0_software_mthd_vblsem_offset },
-       { 0x0408, 0x0408, nvc0_software_mthd_vblsem_value },
-       { 0x040c, 0x040c, nvc0_software_mthd_vblsem_release },
-       { 0x0500, 0x0500, nvc0_software_mthd_flip },
+       { 0x0408, 0x0408, nv50_software_mthd_vblsem_value },
+       { 0x040c, 0x040c, nv50_software_mthd_vblsem_release },
+       { 0x0500, 0x0500, nv50_software_mthd_flip },
        { 0x0600, 0x0600, nvc0_software_mthd_mp_control },
        { 0x0644, 0x0644, nvc0_software_mthd_mp_control },
        { 0x06ac, 0x06ac, nvc0_software_mthd_mp_control },
@@ -138,10 +104,9 @@ nvc0_software_sclass[] = {
  ******************************************************************************/
 
 static int
-nvc0_software_vblsem_release(struct nouveau_eventh *event, int head)
+nvc0_software_vblsem_release(void *data, int head)
 {
-       struct nv50_software_chan *chan =
-               container_of(event, typeof(*chan), vblank.event);
+       struct nv50_software_chan *chan = data;
        struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
        struct nouveau_bar *bar = nouveau_bar(priv);
 
index cb2f632..b649c75 100644 (file)
@@ -9,10 +9,12 @@
 #define NVKM_EVENT_ENABLE 0
 
 struct nouveau_eventh {
+       struct nouveau_event *event;
        struct list_head head;
        unsigned long flags;
+       int index;
+       int (*func)(void *, int);
        void *priv;
-       int (*func)(struct nouveau_eventh *, int index);
 };
 
 struct nouveau_event {
@@ -33,9 +35,11 @@ int  nouveau_event_create(int index_nr, struct nouveau_event **);
 void nouveau_event_destroy(struct nouveau_event **);
 void nouveau_event_trigger(struct nouveau_event *, int index);
 
-void nouveau_event_get(struct nouveau_event *, int index,
-                      struct nouveau_eventh *);
-void nouveau_event_put(struct nouveau_event *, int index,
-                      struct nouveau_eventh *);
+int  nouveau_event_new(struct nouveau_event *, int index,
+                      int (*func)(void *, int), void *,
+                      struct nouveau_eventh **);
+void nouveau_event_ref(struct nouveau_eventh *, struct nouveau_eventh **);
+void nouveau_event_get(struct nouveau_eventh *);
+void nouveau_event_put(struct nouveau_eventh *);
 
 #endif
index 2136d00..1aa6824 100644 (file)
@@ -100,6 +100,7 @@ static void
 nouveau_connector_destroy(struct drm_connector *connector)
 {
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
+       nouveau_event_ref(NULL, &nv_connector->hpd_func);
        kfree(nv_connector->edid);
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
@@ -932,10 +933,9 @@ nouveau_connector_hotplug_work(struct work_struct *work)
 }
 
 static int
-nouveau_connector_hotplug(struct nouveau_eventh *event, int index)
+nouveau_connector_hotplug(void *data, int index)
 {
-       struct nouveau_connector *nv_connector =
-               container_of(event, struct nouveau_connector, hpd_func);
+       struct nouveau_connector *nv_connector = data;
        schedule_work(&nv_connector->hpd_work);
        return NVKM_EVENT_KEEP;
 }
@@ -1007,10 +1007,16 @@ nouveau_connector_create(struct drm_device *dev, int index)
 
                ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)],
                                 DCB_GPIO_UNUSED, &nv_connector->hpd);
-               nv_connector->hpd_func.func = nouveau_connector_hotplug;
                if (ret)
                        nv_connector->hpd.func = DCB_GPIO_UNUSED;
 
+               if (nv_connector->hpd.func != DCB_GPIO_UNUSED) {
+                       nouveau_event_new(gpio->events, nv_connector->hpd.line,
+                                         nouveau_connector_hotplug,
+                                         nv_connector,
+                                        &nv_connector->hpd_func);
+               }
+
                nv_connector->type = nv_connector->dcb[0];
                if (drm_conntype_from_dcb(nv_connector->type) ==
                                          DRM_MODE_CONNECTOR_Unknown) {
index 6e399aa..27c2be6 100644 (file)
@@ -69,7 +69,7 @@ struct nouveau_connector {
 
        struct dcb_gpio_func hpd;
        struct work_struct hpd_work;
-       struct nouveau_eventh hpd_func;
+       struct nouveau_eventh *hpd_func;
 
        int dithering_mode;
        int dithering_depth;
index bdd5cf7..510d13e 100644 (file)
 
 #include "nouveau_fence.h"
 
-#include <subdev/bios/gpio.h>
-#include <subdev/gpio.h>
 #include <engine/disp.h>
 
 #include <core/class.h>
 
+static int
+nouveau_display_vblank_handler(void *data, int head)
+{
+       struct nouveau_drm *drm = data;
+       drm_handle_vblank(drm->dev, head);
+       return NVKM_EVENT_KEEP;
+}
+
+int
+nouveau_display_vblank_enable(struct drm_device *dev, int head)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       if (disp) {
+               nouveau_event_get(disp->vblank[head]);
+               return 0;
+       }
+       return -EIO;
+}
+
+void
+nouveau_display_vblank_disable(struct drm_device *dev, int head)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       if (disp)
+               nouveau_event_put(disp->vblank[head]);
+}
+
+static void
+nouveau_display_vblank_fini(struct drm_device *dev)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       int i;
+
+       if (disp->vblank) {
+               for (i = 0; i < dev->mode_config.num_crtc; i++)
+                       nouveau_event_ref(NULL, &disp->vblank[i]);
+               kfree(disp->vblank);
+               disp->vblank = NULL;
+       }
+
+       drm_vblank_cleanup(dev);
+}
+
+static int
+nouveau_display_vblank_init(struct drm_device *dev)
+{
+       struct nouveau_display *disp = nouveau_display(dev);
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
+       int ret, i;
+
+       disp->vblank = kzalloc(dev->mode_config.num_crtc *
+                              sizeof(*disp->vblank), GFP_KERNEL);
+       if (!disp->vblank)
+               return -ENOMEM;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               ret = nouveau_event_new(pdisp->vblank, i,
+                                       nouveau_display_vblank_handler,
+                                       drm, &disp->vblank[i]);
+               if (ret) {
+                       nouveau_display_vblank_fini(dev);
+                       return ret;
+               }
+       }
+
+       ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+       if (ret) {
+               nouveau_display_vblank_fini(dev);
+               return ret;
+       }
+
+       return 0;
+}
+
 static void
 nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
 {
@@ -227,9 +300,7 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = {
 int
 nouveau_display_init(struct drm_device *dev)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_display *disp = nouveau_display(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
        struct drm_connector *connector;
        int ret;
 
@@ -243,10 +314,7 @@ nouveau_display_init(struct drm_device *dev)
        /* enable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
-                       nouveau_event_get(gpio->events, conn->hpd.line,
-                                        &conn->hpd_func);
-               }
+               if (conn->hpd_func) nouveau_event_get(conn->hpd_func);
        }
 
        return ret;
@@ -255,18 +323,13 @@ nouveau_display_init(struct drm_device *dev)
 void
 nouveau_display_fini(struct drm_device *dev)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_display *disp = nouveau_display(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
        struct drm_connector *connector;
 
        /* disable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
-                       nouveau_event_put(gpio->events, conn->hpd.line,
-                                        &conn->hpd_func);
-               }
+               if (conn->hpd_func) nouveau_event_put(conn->hpd_func);
        }
 
        drm_kms_helper_poll_disable(dev);
@@ -352,7 +415,7 @@ nouveau_display_create(struct drm_device *dev)
                goto disp_create_err;
 
        if (dev->mode_config.num_crtc) {
-               ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+               ret = nouveau_display_vblank_init(dev);
                if (ret)
                        goto vblank_err;
        }
@@ -374,7 +437,7 @@ nouveau_display_destroy(struct drm_device *dev)
        struct nouveau_display *disp = nouveau_display(dev);
 
        nouveau_backlight_exit(dev);
-       drm_vblank_cleanup(dev);
+       nouveau_display_vblank_fini(dev);
 
        drm_kms_helper_poll_fini(dev);
        drm_mode_config_cleanup(dev);
index 025c66f..8bc8bab 100644 (file)
@@ -36,6 +36,8 @@ struct nouveau_display {
        int  (*init)(struct drm_device *);
        void (*fini)(struct drm_device *);
 
+       struct nouveau_eventh **vblank;
+
        struct drm_property *dithering_mode;
        struct drm_property *dithering_depth;
        struct drm_property *underscan_property;
@@ -59,6 +61,8 @@ void nouveau_display_fini(struct drm_device *dev);
 int  nouveau_display_suspend(struct drm_device *dev);
 void nouveau_display_repin(struct drm_device *dev);
 void nouveau_display_resume(struct drm_device *dev);
+int  nouveau_display_vblank_enable(struct drm_device *, int);
+void nouveau_display_vblank_disable(struct drm_device *, int);
 
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                            struct drm_pending_vblank_event *event,
index 42e30e2..69bf82d 100644 (file)
@@ -78,37 +78,6 @@ module_param_named(runpm, nouveau_runtime_pm, int, 0400);
 
 static struct drm_driver driver;
 
-static int
-nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head)
-{
-       struct nouveau_drm *drm =
-               container_of(event, struct nouveau_drm, vblank[head]);
-       drm_handle_vblank(drm->dev, head);
-       return NVKM_EVENT_KEEP;
-}
-
-static int
-nouveau_drm_vblank_enable(struct drm_device *dev, int head)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
-
-       if (WARN_ON_ONCE(head >= ARRAY_SIZE(drm->vblank)))
-               return -EIO;
-       drm->vblank[head].func = nouveau_drm_vblank_handler;
-       nouveau_event_get(pdisp->vblank, head, &drm->vblank[head]);
-       return 0;
-}
-
-static void
-nouveau_drm_vblank_disable(struct drm_device *dev, int head)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
-
-       nouveau_event_put(pdisp->vblank, head, &drm->vblank[head]);
-}
-
 static u64
 nouveau_name(struct pci_dev *pdev)
 {
@@ -812,8 +781,8 @@ driver = {
 #endif
 
        .get_vblank_counter = drm_vblank_count,
-       .enable_vblank = nouveau_drm_vblank_enable,
-       .disable_vblank = nouveau_drm_vblank_disable,
+       .enable_vblank = nouveau_display_vblank_enable,
+       .disable_vblank = nouveau_display_vblank_disable,
 
        .ioctls = nouveau_ioctls,
        .num_ioctls = ARRAY_SIZE(nouveau_ioctls),
index 994fd6e..910448f 100644 (file)
@@ -127,7 +127,6 @@ struct nouveau_drm {
        struct nvbios vbios;
        struct nouveau_display *display;
        struct backlight_device *backlight;
-       struct nouveau_eventh vblank[4];
 
        /* power management */
        struct nouveau_pm *pm;
index c2e3167..34b8271 100644 (file)
@@ -166,9 +166,9 @@ nouveau_fence_done(struct nouveau_fence *fence)
 }
 
 static int
-nouveau_fence_wait_uevent_handler(struct nouveau_eventh *handler, int index)
+nouveau_fence_wait_uevent_handler(void *data, int index)
 {
-       struct nouveau_fence_priv *priv = handler->priv;
+       struct nouveau_fence_priv *priv = data;
        wake_up_all(&priv->waiting);
        return NVKM_EVENT_KEEP;
 }
@@ -180,13 +180,16 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
        struct nouveau_channel *chan = fence->channel;
        struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device);
        struct nouveau_fence_priv *priv = chan->drm->fence;
-       struct nouveau_eventh handler = {
-               .func = nouveau_fence_wait_uevent_handler,
-               .priv = priv,
-       };
+       struct nouveau_eventh *handler;
        int ret = 0;
 
-       nouveau_event_get(pfifo->uevent, 0, &handler);
+       ret = nouveau_event_new(pfifo->uevent, 0,
+                               nouveau_fence_wait_uevent_handler,
+                               priv, &handler);
+       if (ret)
+               return ret;
+
+       nouveau_event_get(handler);
 
        if (fence->timeout) {
                unsigned long timeout = fence->timeout - jiffies;
@@ -218,7 +221,7 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
                }
        }
 
-       nouveau_event_put(pfifo->uevent, 0, &handler);
+       nouveau_event_ref(NULL, &handler);
        if (unlikely(ret < 0))
                return ret;