From 4f47643dbb4c345c5beebe53588682a7ff2c872a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 3 Feb 2013 12:56:16 +1000 Subject: [PATCH] drm/nouveau/gpio: use event interfaces for interrupt signalling Signed-off-by: Ben Skeggs --- .../drm/nouveau/core/include/subdev/gpio.h | 31 ++-- .../gpu/drm/nouveau/core/subdev/gpio/base.c | 137 ++---------------- .../gpu/drm/nouveau/core/subdev/gpio/nv10.c | 33 +++-- .../gpu/drm/nouveau/core/subdev/gpio/nv50.c | 39 +++-- .../gpu/drm/nouveau/core/subdev/gpio/nvd0.c | 4 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 77 +++++----- drivers/gpu/drm/nouveau/nouveau_connector.h | 7 +- drivers/gpu/drm/nouveau/nouveau_display.c | 12 +- drivers/gpu/drm/nouveau/nouveau_dp.c | 6 +- 9 files changed, 127 insertions(+), 219 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h index 43cd20521a94..b0007b508567 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -10,28 +11,18 @@ struct nouveau_gpio { struct nouveau_subdev base; + struct nouveau_event *events; + /* hardware interfaces */ void (*reset)(struct nouveau_gpio *, u8 func); int (*drive)(struct nouveau_gpio *, int line, int dir, int out); int (*sense)(struct nouveau_gpio *, int line); - void (*irq_enable)(struct nouveau_gpio *, int line, bool); /* software interfaces */ int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line, struct dcb_gpio_func *); int (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state); int (*get)(struct nouveau_gpio *, int idx, u8 tag, u8 line); - int (*irq)(struct nouveau_gpio *, int idx, u8 tag, u8 line, bool on); - - /* interrupt handling */ - struct list_head isr; - spinlock_t lock; - - void (*isr_run)(struct nouveau_gpio *, int idx, u32 mask); - int (*isr_add)(struct nouveau_gpio *, int idx, u8 tag, u8 line, - void (*)(void *, int state), void *data); - void (*isr_del)(struct nouveau_gpio *, int idx, u8 tag, u8 line, - void (*)(void *, int state), void *data); }; static inline struct nouveau_gpio * @@ -42,14 +33,17 @@ nouveau_gpio(void *obj) #define nouveau_gpio_create(p,e,o,l,d) \ nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d) -#define nouveau_gpio_destroy(p) \ - nouveau_subdev_destroy(&(p)->base) +#define nouveau_gpio_destroy(p) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_dtor(nv_object(gpio)); \ +}) #define nouveau_gpio_fini(p,s) \ nouveau_subdev_fini(&(p)->base, (s)) -int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, int, int, void **); -int nouveau_gpio_init(struct nouveau_gpio *); +int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, int, void **); +void _nouveau_gpio_dtor(struct nouveau_object *); +int nouveau_gpio_init(struct nouveau_gpio *); extern struct nouveau_oclass nv10_gpio_oclass; extern struct nouveau_oclass nv50_gpio_oclass; @@ -59,6 +53,7 @@ void nv50_gpio_dtor(struct nouveau_object *); int nv50_gpio_init(struct nouveau_object *); int nv50_gpio_fini(struct nouveau_object *, bool); void nv50_gpio_intr(struct nouveau_subdev *); -void nv50_gpio_irq_enable(struct nouveau_gpio *, int line, bool); +void nv50_gpio_intr_enable(struct nouveau_event *, int line); +void nv50_gpio_intr_disable(struct nouveau_event *, int line); #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c index 6f574fdc27c1..d422acc9af15 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c @@ -102,129 +102,12 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line) return ret; } -static int -nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on) +void +_nouveau_gpio_dtor(struct nouveau_object *object) { - struct dcb_gpio_func func; - int ret; - - ret = nouveau_gpio_find(gpio, idx, tag, line, &func); - if (ret == 0) { - if (idx == 0 && gpio->irq_enable) - gpio->irq_enable(gpio, func.line, on); - else - ret = -ENODEV; - } - - return ret; -} - -struct gpio_isr { - struct nouveau_gpio *gpio; - struct list_head head; - struct work_struct work; - int idx; - struct dcb_gpio_func func; - void (*handler)(void *, int); - void *data; - bool inhibit; -}; - -static void -nouveau_gpio_isr_bh(struct work_struct *work) -{ - struct gpio_isr *isr = container_of(work, struct gpio_isr, work); - struct nouveau_gpio *gpio = isr->gpio; - unsigned long flags; - int state; - - state = nouveau_gpio_get(gpio, isr->idx, isr->func.func, - isr->func.line); - if (state >= 0) - isr->handler(isr->data, state); - - spin_lock_irqsave(&gpio->lock, flags); - isr->inhibit = false; - spin_unlock_irqrestore(&gpio->lock, flags); -} - -static void -nouveau_gpio_isr_run(struct nouveau_gpio *gpio, int idx, u32 line_mask) -{ - struct gpio_isr *isr; - - if (idx != 0) - return; - - spin_lock(&gpio->lock); - list_for_each_entry(isr, &gpio->isr, head) { - if (line_mask & (1 << isr->func.line)) { - if (isr->inhibit) - continue; - isr->inhibit = true; - schedule_work(&isr->work); - } - } - spin_unlock(&gpio->lock); -} - -static int -nouveau_gpio_isr_add(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, - void (*handler)(void *, int), void *data) -{ - struct gpio_isr *isr; - unsigned long flags; - int ret; - - isr = kzalloc(sizeof(*isr), GFP_KERNEL); - if (!isr) - return -ENOMEM; - - ret = nouveau_gpio_find(gpio, idx, tag, line, &isr->func); - if (ret) { - kfree(isr); - return ret; - } - - INIT_WORK(&isr->work, nouveau_gpio_isr_bh); - isr->gpio = gpio; - isr->handler = handler; - isr->data = data; - isr->idx = idx; - - spin_lock_irqsave(&gpio->lock, flags); - list_add(&isr->head, &gpio->isr); - spin_unlock_irqrestore(&gpio->lock, flags); - return 0; -} - -static void -nouveau_gpio_isr_del(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, - void (*handler)(void *, int), void *data) -{ - struct gpio_isr *isr, *tmp; - struct dcb_gpio_func func; - unsigned long flags; - LIST_HEAD(tofree); - int ret; - - ret = nouveau_gpio_find(gpio, idx, tag, line, &func); - if (ret == 0) { - spin_lock_irqsave(&gpio->lock, flags); - list_for_each_entry_safe(isr, tmp, &gpio->isr, head) { - if (memcmp(&isr->func, &func, sizeof(func)) || - isr->idx != idx || - isr->handler != handler || isr->data != data) - continue; - list_move_tail(&isr->head, &tofree); - } - spin_unlock_irqrestore(&gpio->lock, flags); - - list_for_each_entry_safe(isr, tmp, &tofree, head) { - flush_work(&isr->work); - kfree(isr); - } - } + struct nouveau_gpio *gpio = (void *)object; + nouveau_event_destroy(&gpio->events); + nouveau_subdev_destroy(&gpio->base); } int @@ -242,15 +125,13 @@ nouveau_gpio_create_(struct nouveau_object *parent, if (ret) return ret; + ret = nouveau_event_create(lines, &gpio->events); + if (ret) + return ret; + gpio->find = nouveau_gpio_find; gpio->set = nouveau_gpio_set; gpio->get = nouveau_gpio_get; - gpio->irq = nouveau_gpio_irq; - gpio->isr_run = nouveau_gpio_isr_run; - gpio->isr_add = nouveau_gpio_isr_add; - gpio->isr_del = nouveau_gpio_isr_del; - INIT_LIST_HEAD(&gpio->isr); - spin_lock_init(&gpio->lock); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c index cf38d2a1d7f1..9665f5f70ee3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c @@ -82,15 +82,6 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) return 0; } -static void -nv10_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on) -{ - u32 mask = 0x00010001 << line; - - nv_wr32(gpio, 0x001104, mask); - nv_mask(gpio, 0x001144, mask, on ? mask : 0); -} - static void nv10_gpio_intr(struct nouveau_subdev *subdev) { @@ -98,12 +89,30 @@ nv10_gpio_intr(struct nouveau_subdev *subdev) u32 intr = nv_rd32(priv, 0x001104); u32 hi = (intr & 0x0000ffff) >> 0; u32 lo = (intr & 0xffff0000) >> 16; + int i; - priv->base.isr_run(&priv->base, 0, hi | lo); + for (i = 0; (hi | lo) && i < 32; i++) { + if ((hi | lo) & (1 << i)) + nouveau_event_trigger(priv->base.events, i); + } nv_wr32(priv, 0x001104, intr); } +static void +nv10_gpio_intr_enable(struct nouveau_event *event, int line) +{ + nv_wr32(event->priv, 0x001104, 0x00010001 << line); + nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line); +} + +static void +nv10_gpio_intr_disable(struct nouveau_event *event, int line) +{ + nv_wr32(event->priv, 0x001104, 0x00010001 << line); + nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000); +} + static int nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -119,7 +128,9 @@ nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.drive = nv10_gpio_drive; priv->base.sense = nv10_gpio_sense; - priv->base.irq_enable = nv10_gpio_irq_enable; + priv->base.events->priv = priv; + priv->base.events->enable = nv10_gpio_intr_enable; + priv->base.events->disable = nv10_gpio_intr_disable; nv_subdev(priv)->intr = nv10_gpio_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c index dd022a5787b6..cbe609aa237c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c @@ -94,22 +94,13 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line) return !!(nv_rd32(gpio, reg) & (4 << shift)); } -void -nv50_gpio_irq_enable(struct nouveau_gpio *gpio, int line, bool on) -{ - u32 reg = line < 16 ? 0xe050 : 0xe070; - u32 mask = 0x00010001 << (line & 0xf); - - nv_wr32(gpio, reg + 4, mask); - nv_mask(gpio, reg + 0, mask, on ? mask : 0); -} - void nv50_gpio_intr(struct nouveau_subdev *subdev) { struct nv50_gpio_priv *priv = (void *)subdev; u32 intr0, intr1 = 0; u32 hi, lo; + int i; intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050); if (nv_device(priv)->chipset >= 0x90) @@ -117,13 +108,35 @@ nv50_gpio_intr(struct nouveau_subdev *subdev) hi = (intr0 & 0x0000ffff) | (intr1 << 16); lo = (intr0 >> 16) | (intr1 & 0xffff0000); - priv->base.isr_run(&priv->base, 0, hi | lo); + + for (i = 0; (hi | lo) && i < 32; i++) { + if ((hi | lo) & (1 << i)) + nouveau_event_trigger(priv->base.events, i); + } nv_wr32(priv, 0xe054, intr0); if (nv_device(priv)->chipset >= 0x90) nv_wr32(priv, 0xe074, intr1); } +void +nv50_gpio_intr_enable(struct nouveau_event *event, int line) +{ + const u32 addr = line < 16 ? 0xe050 : 0xe070; + const u32 mask = 0x00010001 << (line & 0xf); + nv_wr32(event->priv, addr + 0x04, mask); + nv_mask(event->priv, addr + 0x00, mask, mask); +} + +void +nv50_gpio_intr_disable(struct nouveau_event *event, int line) +{ + const u32 addr = line < 16 ? 0xe050 : 0xe070; + const u32 mask = 0x00010001 << (line & 0xf); + nv_wr32(event->priv, addr + 0x04, mask); + nv_mask(event->priv, addr + 0x00, mask, 0x00000000); +} + static int nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -142,7 +155,9 @@ nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.reset = nv50_gpio_reset; priv->base.drive = nv50_gpio_drive; priv->base.sense = nv50_gpio_sense; - priv->base.irq_enable = nv50_gpio_irq_enable; + priv->base.events->priv = priv; + priv->base.events->enable = nv50_gpio_intr_enable; + priv->base.events->disable = nv50_gpio_intr_disable; nv_subdev(priv)->intr = nv50_gpio_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c index bc74199259a2..5ef16a262de7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c @@ -88,7 +88,9 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.reset = nvd0_gpio_reset; priv->base.drive = nvd0_gpio_drive; priv->base.sense = nvd0_gpio_sense; - priv->base.irq_enable = nv50_gpio_irq_enable; + priv->base.events->priv = priv; + priv->base.events->enable = nv50_gpio_intr_enable; + priv->base.events->disable = nv50_gpio_intr_disable; nv_subdev(priv)->intr = nv50_gpio_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index a64e8777cbe9..9c4b3f5fba01 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -55,8 +55,6 @@ MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)"); static int nouveau_duallink = 1; module_param_named(duallink, nouveau_duallink, int, 0400); -static void nouveau_connector_hotplug(void *, int); - struct nouveau_encoder * find_encoder(struct drm_connector *connector, int type) { @@ -100,22 +98,6 @@ static void nouveau_connector_destroy(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); - struct nouveau_gpio *gpio; - struct nouveau_drm *drm; - struct drm_device *dev; - - if (!nv_connector) - return; - - dev = nv_connector->base.dev; - drm = nouveau_drm(dev); - gpio = nouveau_gpio(drm->device); - - if (gpio && nv_connector->hpd.func != DCB_GPIO_UNUSED) { - gpio->isr_del(gpio, 0, nv_connector->hpd.func, 0xff, - nouveau_connector_hotplug, connector); - } - kfree(nv_connector->edid); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -912,6 +894,37 @@ nouveau_connector_funcs_lvds = { .force = nouveau_connector_force }; +static void +nouveau_connector_hotplug_work(struct work_struct *work) +{ + struct nouveau_connector *nv_connector = + container_of(work, struct nouveau_connector, hpd_work); + struct drm_connector *connector = &nv_connector->base; + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); + bool plugged = gpio->get(gpio, 0, nv_connector->hpd.func, 0xff); + + NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", + drm_get_connector_name(connector)); + + if (plugged) + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + else + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + + drm_helper_hpd_irq_event(dev); +} + +static int +nouveau_connector_hotplug(struct nouveau_eventh *event, int index) +{ + struct nouveau_connector *nv_connector = + container_of(event, struct nouveau_connector, hpd_func); + schedule_work(&nv_connector->hpd_work); + return NVKM_EVENT_KEEP; +} + static int drm_conntype_from_dcb(enum dcb_connector_type dcb) { @@ -962,6 +975,7 @@ nouveau_connector_create(struct drm_device *dev, int index) return ERR_PTR(-ENOMEM); connector = &nv_connector->base; + INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work); nv_connector->index = index; /* attempt to parse vbios connector type and hotplug gpio */ @@ -978,6 +992,7 @@ 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; @@ -1129,31 +1144,9 @@ nouveau_connector_create(struct drm_device *dev, int index) } connector->polled = DRM_CONNECTOR_POLL_CONNECT; - if (gpio && nv_connector->hpd.func != DCB_GPIO_UNUSED) { - ret = gpio->isr_add(gpio, 0, nv_connector->hpd.func, 0xff, - nouveau_connector_hotplug, connector); - if (ret == 0) - connector->polled = DRM_CONNECTOR_POLL_HPD; - } + if (nv_connector->hpd.func != DCB_GPIO_UNUSED) + connector->polled = DRM_CONNECTOR_POLL_HPD; drm_sysfs_connector_add(connector); return connector; } - -static void -nouveau_connector_hotplug(void *data, int plugged) -{ - struct drm_connector *connector = data; - struct drm_device *dev = connector->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - - NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", - drm_get_connector_name(connector)); - - if (plugged) - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); - else - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); - - drm_helper_hpd_irq_event(dev); -} diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index f37250c8469d..6e399aad491a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -30,6 +30,8 @@ #include #include "nouveau_crtc.h" +#include + #include #include @@ -62,10 +64,13 @@ enum nouveau_dithering_depth { struct nouveau_connector { struct drm_connector base; enum dcb_connector_type type; - struct dcb_gpio_func hpd; u8 index; u8 *dcb; + struct dcb_gpio_func hpd; + struct work_struct hpd_work; + struct nouveau_eventh hpd_func; + int dithering_mode; int dithering_depth; int scaling_mode; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index a698e79f99d9..78fc5aa5f5dc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -233,8 +233,10 @@ 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) - gpio->irq(gpio, 0, conn->hpd.func, 0xff, true); + if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) { + nouveau_event_get(gpio->events, conn->hpd.line, + &conn->hpd_func); + } } return ret; @@ -251,8 +253,10 @@ nouveau_display_fini(struct drm_device *dev) /* disable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (gpio) - gpio->irq(gpio, 0, conn->hpd.func, 0xff, false); + if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) { + nouveau_event_put(gpio->events, conn->hpd.line, + &conn->hpd_func); + } } drm_kms_helper_poll_disable(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c273c2afed10..a87c3674157f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -260,7 +260,8 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, * we take during link training (DP_SET_POWER is one), we need * to ignore them for the moment to avoid races. */ - gpio->irq(gpio, 0, nv_connector->hpd.func, 0xff, false); + nouveau_event_put(gpio->events, nv_connector->hpd.line, + &nv_connector->hpd_func); /* enable down-spreading and execute pre-train script from vbios */ dp_link_train_init(dev, &dp, nv_encoder->dp.dpcd[3] & 1); @@ -300,7 +301,8 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, dp_link_train_fini(dev, &dp); /* re-enable hotplug detect */ - gpio->irq(gpio, 0, nv_connector->hpd.func, 0xff, true); + nouveau_event_get(gpio->events, nv_connector->hpd.line, + &nv_connector->hpd_func); return true; }