@@ -23,19 +23,60 @@
#include <core/os.h>
#include <core/event.h>
+void
+nouveau_event_handler_install(struct nouveau_event *event, int index,
+ int (*func)(struct nouveau_eventh*, int),
+ void *priv, struct nouveau_eventh *handler)
+{
+ unsigned long flags;
+
+ if (index >= event->index_nr)
+ return;
+
+ handler->func = func;
+ handler->priv = priv;
+
+ spin_lock_irqsave(&event->lock, flags);
+ list_add(&handler->head, &event->index[index].list);
+ spin_unlock_irqrestore(&event->lock, flags);
+}
+
+void
+nouveau_event_handler_remove(struct nouveau_event *event, int index,
+ struct nouveau_eventh *handler)
+{
+ unsigned long flags;
+
+ if (index >= event->index_nr)
+ return;
+
+ spin_lock_irqsave(&event->lock, flags);
+ list_del(&handler->head);
+ spin_unlock_irqrestore(&event->lock, flags);
+}
+
int
nouveau_event_handler_create(struct nouveau_event *event, int index,
int (*func)(struct nouveau_eventh*, int),
void *priv, struct nouveau_eventh **phandler)
{
struct nouveau_eventh *handler;
+ unsigned long flags;
handler = *phandler = kzalloc(sizeof(*handler), GFP_KERNEL);
if (!handler)
return -ENOMEM;
handler->func = func;
handler->priv = priv;
- nouveau_event_get(event, index, handler);
+ __set_bit(NVKM_EVENT_ENABLE, &handler->flags);
+
+ spin_lock_irqsave(&event->lock, flags);
+ list_add(&handler->head, &event->index[index].list);
+ if (!event->index[index].refs++) {
+ if (event->enable)
+ event->enable(event, index);
+ }
+ spin_unlock_irqrestore(&event->lock, flags);
return 0;
}
@@ -43,7 +84,18 @@ void
nouveau_event_handler_destroy(struct nouveau_event *event, int index,
struct nouveau_eventh *handler)
{
- nouveau_event_put(event, index, handler);
+ unsigned long flags;
+
+ if (index >= event->index_nr)
+ return;
+
+ spin_lock_irqsave(&event->lock, flags);
+ if (!--event->index[index].refs) {
+ if (event->disable)
+ event->disable(event, index);
+ }
+ list_del(&handler->head);
+ spin_unlock_irqrestore(&event->lock, flags);
kfree(handler);
}
@@ -56,7 +108,6 @@ nouveau_event_put_locked(struct nouveau_event *event, int index,
if (event->disable)
event->disable(event, index);
}
- list_del(&handler->head);
}
}
@@ -85,7 +136,6 @@ nouveau_event_get(struct nouveau_event *event, int index,
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++) {
if (event->enable)
event->enable(event, index);
@@ -105,8 +155,9 @@ 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) {
- nouveau_event_put_locked(event, index, handler);
+ if (test_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
+ if (handler->func(handler, index) == NVKM_EVENT_DROP)
+ nouveau_event_put_locked(event, index, handler);
}
}
spin_unlock_irqrestore(&event->lock, flags);
@@ -97,7 +97,7 @@ nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
if (crtc > 1)
return -EINVAL;
- nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
+ nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event[crtc]);
return 0;
}
@@ -135,7 +135,7 @@ static int
nv50_software_vblsem_release(struct nouveau_eventh *event, int head)
{
struct nouveau_software_chan *chan =
- container_of(event, struct nouveau_software_chan, vblank.event);
+ container_of(event, struct nouveau_software_chan, vblank.event[head]);
struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
struct nouveau_bar *bar = nouveau_bar(priv);
@@ -161,7 +161,8 @@ nv50_software_context_ctor(struct nouveau_object *parent,
struct nouveau_object **pobject)
{
struct nv50_software_chan *chan;
- int ret;
+ struct nouveau_disp *disp = nouveau_disp(engine);
+ int ret, i;
ret = nouveau_software_context_create(parent, engine, oclass, &chan);
*pobject = nv_object(chan);
@@ -169,16 +170,36 @@ nv50_software_context_ctor(struct nouveau_object *parent,
return ret;
chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
- chan->base.vblank.event.func = nv50_software_vblsem_release;
+ for (i = 0; i < ARRAY_SIZE(chan->base.vblank.event); i++) {
+ nouveau_event_handler_install(disp->vblank, i,
+ nv50_software_vblsem_release,
+ NULL,
+ &chan->base.vblank.event[i]);
+ }
return 0;
}
+void
+nv50_software_context_dtor(struct nouveau_object *object)
+{
+ struct nv50_software_chan *chan = (void *)object;
+ struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
+ struct nouveau_disp *disp = nouveau_disp(priv);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(chan->base.vblank.event); i++) {
+ nouveau_event_handler_remove(disp->vblank, i,
+ &chan->base.vblank.event[i]);
+ }
+ _nouveau_software_context_dtor(object);
+}
+
static struct nouveau_oclass
nv50_software_cclass = {
.handle = NV_ENGCTX(SW, 0x50),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv50_software_context_ctor,
- .dtor = _nouveau_software_context_dtor,
+ .dtor = nv50_software_context_dtor,
.init = _nouveau_software_context_init,
.fini = _nouveau_software_context_fini,
},
@@ -80,7 +80,7 @@ nvc0_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
if ((nv_device(object)->card_type < NV_E0 && crtc > 1) || crtc > 3)
return -EINVAL;
- nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event);
+ nouveau_event_get(disp->vblank, crtc, &chan->base.vblank.event[crtc]);
return 0;
}
@@ -147,7 +147,7 @@ static int
nvc0_software_vblsem_release(struct nouveau_eventh *event, int head)
{
struct nouveau_software_chan *chan =
- container_of(event, struct nouveau_software_chan, vblank.event);
+ container_of(event, struct nouveau_software_chan, vblank.event[head]);
struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine;
struct nouveau_bar *bar = nouveau_bar(priv);
@@ -167,7 +167,8 @@ nvc0_software_context_ctor(struct nouveau_object *parent,
struct nouveau_object **pobject)
{
struct nvc0_software_chan *chan;
- int ret;
+ struct nouveau_disp *disp = nouveau_disp(engine);
+ int ret, i;
ret = nouveau_software_context_create(parent, engine, oclass, &chan);
*pobject = nv_object(chan);
@@ -175,16 +176,36 @@ nvc0_software_context_ctor(struct nouveau_object *parent,
return ret;
chan->base.vblank.channel = nv_gpuobj(parent->parent)->addr >> 12;
- chan->base.vblank.event.func = nvc0_software_vblsem_release;
+ for (i = 0; i < ARRAY_SIZE(chan->base.vblank.event); i++) {
+ nouveau_event_handler_install(disp->vblank, i,
+ nvc0_software_vblsem_release,
+ NULL,
+ &chan->base.vblank.event[i]);
+ }
return 0;
}
+void
+nvc0_software_context_dtor(struct nouveau_object *object)
+{
+ struct nvc0_software_chan *chan = (void *)object;
+ struct nvc0_software_priv *priv = (void *)nv_object(chan)->engine;
+ struct nouveau_disp *disp = nouveau_disp(priv);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(chan->base.vblank.event); i++) {
+ nouveau_event_handler_remove(disp->vblank, i,
+ &chan->base.vblank.event[i]);
+ }
+ _nouveau_software_context_dtor(object);
+}
+
static struct nouveau_oclass
nvc0_software_cclass = {
.handle = NV_ENGCTX(SW, 0xc0),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvc0_software_context_ctor,
- .dtor = _nouveau_software_context_dtor,
+ .dtor = nvc0_software_context_dtor,
.init = _nouveau_software_context_init,
.fini = _nouveau_software_context_fini,
},
@@ -44,4 +44,10 @@ int nouveau_event_handler_create(struct nouveau_event *, int index,
void nouveau_event_handler_destroy(struct nouveau_event *, int index,
struct nouveau_eventh *);
+void nouveau_event_handler_install(struct nouveau_event *, int index,
+ int (*func)(struct nouveau_eventh*, int),
+ void *priv, struct nouveau_eventh *);
+void nouveau_event_handler_remove(struct nouveau_event *, int index,
+ struct nouveau_eventh *);
+
#endif
@@ -9,7 +9,7 @@ struct nouveau_software_chan {
struct nouveau_engctx base;
struct {
- struct nouveau_eventh event;
+ struct nouveau_eventh event[4];
u32 channel;
u32 ctxdma;
u64 offset;
@@ -98,6 +98,11 @@ static void
nouveau_connector_destroy(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_drm *drm = nouveau_drm(connector->dev);
+ struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
+
+ nouveau_event_handler_remove(gpio->events, nv_connector->hpd.line,
+ &nv_connector->hpd_func);
kfree(nv_connector->edid);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
@@ -990,7 +995,10 @@ 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;
+ nouveau_event_handler_install(gpio->events,
+ nv_connector->hpd.line,
+ nouveau_connector_hotplug, NULL,
+ &nv_connector->hpd_func);
if (ret)
nv_connector->hpd.func = DCB_GPIO_UNUSED;
@@ -88,7 +88,6 @@ nouveau_drm_vblank_enable(struct drm_device *dev, int head)
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;
}
@@ -298,7 +297,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
struct pci_dev *pdev = dev->pdev;
struct nouveau_device *device;
struct nouveau_drm *drm;
- int ret;
+ struct nouveau_disp *disp;
+ int ret, i;
ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm);
if (ret)
@@ -352,6 +352,13 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
if (nv_device(drm->device)->chipset == 0xc1)
nv_mask(device, 0x00088080, 0x00000800, 0x00000000);
+ disp = nouveau_disp(device);
+ for (i = 0; i < ARRAY_SIZE(drm->vblank); i++) {
+ nouveau_event_handler_install(disp->vblank, i,
+ nouveau_drm_vblank_handler,
+ NULL, &drm->vblank[i]);
+ }
+
nouveau_vga_init(drm);
nouveau_agp_init(drm);
@@ -404,6 +411,8 @@ static int
nouveau_drm_unload(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_disp *disp = nouveau_disp(drm->device);
+ int i;
nouveau_fbcon_fini(dev);
nouveau_accel_fini(drm);
@@ -420,6 +429,10 @@ nouveau_drm_unload(struct drm_device *dev)
nouveau_agp_fini(drm);
nouveau_vga_fini(drm);
+ for (i = 0; i < ARRAY_SIZE(drm->vblank); i++)
+ nouveau_event_handler_remove(disp->vblank, i,
+ &drm->vblank[i]);
+
nouveau_cli_destroy(&drm->client);
return 0;
}
Complete migration of nouveau_event_get/_put from add/remove semantics to enable/disable semantics. Introduce nouveau_event_handler_install/_remove interface to add/remove non-local-scope event handlers (ie., those stored in parent containers). This change in semantics makes explicit the handler lifetime, and distinguishes "one-of" event handlers (such as gpio) from "many temporary" event handlers (such as uevent). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> --- drivers/gpu/drm/nouveau/core/core/event.c | 63 +++++++++++++++++++--- .../gpu/drm/nouveau/core/engine/software/nv50.c | 31 +++++++++-- .../gpu/drm/nouveau/core/engine/software/nvc0.c | 31 +++++++++-- drivers/gpu/drm/nouveau/core/include/core/event.h | 6 +++ .../gpu/drm/nouveau/core/include/engine/software.h | 2 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 10 +++- drivers/gpu/drm/nouveau/nouveau_drm.c | 17 +++++- 7 files changed, 140 insertions(+), 20 deletions(-)