Commit 51cb4b39 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/core: convert event handler apis to split create/enable semantics



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: default avatarBen Skeggs <bskeggs@redhat.com>
parent 7589563e
......@@ -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);
}
}
......
......@@ -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;
}
......
......@@ -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
......@@ -54,40 +54,6 @@ nvc0_software_mthd_vblsem_offset(struct nouveau_object *object, u32 mthd,
return 0;
}
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);
......
......@@ -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
......@@ -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) {
......
......@@ -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;
......
......@@ -38,12 +38,85 @@
#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);
......
......@@ -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,
......
......@@ -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),
......
......@@ -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 */