drm/nouveau/gpio: use event interfaces for interrupt signalling
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
0f0800661a
commit
4f47643dbb
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <core/subdev.h>
|
#include <core/subdev.h>
|
||||||
#include <core/device.h>
|
#include <core/device.h>
|
||||||
|
#include <core/event.h>
|
||||||
|
|
||||||
#include <subdev/bios.h>
|
#include <subdev/bios.h>
|
||||||
#include <subdev/bios/gpio.h>
|
#include <subdev/bios/gpio.h>
|
||||||
|
@ -10,28 +11,18 @@
|
||||||
struct nouveau_gpio {
|
struct nouveau_gpio {
|
||||||
struct nouveau_subdev base;
|
struct nouveau_subdev base;
|
||||||
|
|
||||||
|
struct nouveau_event *events;
|
||||||
|
|
||||||
/* hardware interfaces */
|
/* hardware interfaces */
|
||||||
void (*reset)(struct nouveau_gpio *, u8 func);
|
void (*reset)(struct nouveau_gpio *, u8 func);
|
||||||
int (*drive)(struct nouveau_gpio *, int line, int dir, int out);
|
int (*drive)(struct nouveau_gpio *, int line, int dir, int out);
|
||||||
int (*sense)(struct nouveau_gpio *, int line);
|
int (*sense)(struct nouveau_gpio *, int line);
|
||||||
void (*irq_enable)(struct nouveau_gpio *, int line, bool);
|
|
||||||
|
|
||||||
/* software interfaces */
|
/* software interfaces */
|
||||||
int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
|
int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
|
||||||
struct dcb_gpio_func *);
|
struct dcb_gpio_func *);
|
||||||
int (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state);
|
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 (*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 *
|
static inline struct nouveau_gpio *
|
||||||
|
@ -42,14 +33,17 @@ nouveau_gpio(void *obj)
|
||||||
|
|
||||||
#define nouveau_gpio_create(p,e,o,l,d) \
|
#define nouveau_gpio_create(p,e,o,l,d) \
|
||||||
nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d)
|
nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d)
|
||||||
#define nouveau_gpio_destroy(p) \
|
#define nouveau_gpio_destroy(p) ({ \
|
||||||
nouveau_subdev_destroy(&(p)->base)
|
struct nouveau_gpio *gpio = (p); \
|
||||||
|
_nouveau_gpio_dtor(nv_object(gpio)); \
|
||||||
|
})
|
||||||
#define nouveau_gpio_fini(p,s) \
|
#define nouveau_gpio_fini(p,s) \
|
||||||
nouveau_subdev_fini(&(p)->base, (s))
|
nouveau_subdev_fini(&(p)->base, (s))
|
||||||
|
|
||||||
int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
|
int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
|
||||||
struct nouveau_oclass *, int, int, void **);
|
struct nouveau_oclass *, int, int, void **);
|
||||||
int nouveau_gpio_init(struct nouveau_gpio *);
|
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 nv10_gpio_oclass;
|
||||||
extern struct nouveau_oclass nv50_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_init(struct nouveau_object *);
|
||||||
int nv50_gpio_fini(struct nouveau_object *, bool);
|
int nv50_gpio_fini(struct nouveau_object *, bool);
|
||||||
void nv50_gpio_intr(struct nouveau_subdev *);
|
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
|
#endif
|
||||||
|
|
|
@ -102,129 +102,12 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
void
|
||||||
nouveau_gpio_irq(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line, bool on)
|
_nouveau_gpio_dtor(struct nouveau_object *object)
|
||||||
{
|
{
|
||||||
struct dcb_gpio_func func;
|
struct nouveau_gpio *gpio = (void *)object;
|
||||||
int ret;
|
nouveau_event_destroy(&gpio->events);
|
||||||
|
nouveau_subdev_destroy(&gpio->base);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -242,15 +125,13 @@ nouveau_gpio_create_(struct nouveau_object *parent,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = nouveau_event_create(lines, &gpio->events);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
gpio->find = nouveau_gpio_find;
|
gpio->find = nouveau_gpio_find;
|
||||||
gpio->set = nouveau_gpio_set;
|
gpio->set = nouveau_gpio_set;
|
||||||
gpio->get = nouveau_gpio_get;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,15 +82,6 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
|
||||||
return 0;
|
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
|
static void
|
||||||
nv10_gpio_intr(struct nouveau_subdev *subdev)
|
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 intr = nv_rd32(priv, 0x001104);
|
||||||
u32 hi = (intr & 0x0000ffff) >> 0;
|
u32 hi = (intr & 0x0000ffff) >> 0;
|
||||||
u32 lo = (intr & 0xffff0000) >> 16;
|
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);
|
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
|
static int
|
||||||
nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
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.drive = nv10_gpio_drive;
|
||||||
priv->base.sense = nv10_gpio_sense;
|
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;
|
nv_subdev(priv)->intr = nv10_gpio_intr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,22 +94,13 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
|
||||||
return !!(nv_rd32(gpio, reg) & (4 << shift));
|
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
|
void
|
||||||
nv50_gpio_intr(struct nouveau_subdev *subdev)
|
nv50_gpio_intr(struct nouveau_subdev *subdev)
|
||||||
{
|
{
|
||||||
struct nv50_gpio_priv *priv = (void *)subdev;
|
struct nv50_gpio_priv *priv = (void *)subdev;
|
||||||
u32 intr0, intr1 = 0;
|
u32 intr0, intr1 = 0;
|
||||||
u32 hi, lo;
|
u32 hi, lo;
|
||||||
|
int i;
|
||||||
|
|
||||||
intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
|
intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
|
||||||
if (nv_device(priv)->chipset >= 0x90)
|
if (nv_device(priv)->chipset >= 0x90)
|
||||||
|
@ -117,13 +108,35 @@ nv50_gpio_intr(struct nouveau_subdev *subdev)
|
||||||
|
|
||||||
hi = (intr0 & 0x0000ffff) | (intr1 << 16);
|
hi = (intr0 & 0x0000ffff) | (intr1 << 16);
|
||||||
lo = (intr0 >> 16) | (intr1 & 0xffff0000);
|
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);
|
nv_wr32(priv, 0xe054, intr0);
|
||||||
if (nv_device(priv)->chipset >= 0x90)
|
if (nv_device(priv)->chipset >= 0x90)
|
||||||
nv_wr32(priv, 0xe074, intr1);
|
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
|
static int
|
||||||
nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
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.reset = nv50_gpio_reset;
|
||||||
priv->base.drive = nv50_gpio_drive;
|
priv->base.drive = nv50_gpio_drive;
|
||||||
priv->base.sense = nv50_gpio_sense;
|
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;
|
nv_subdev(priv)->intr = nv50_gpio_intr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,9 @@ nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||||
priv->base.reset = nvd0_gpio_reset;
|
priv->base.reset = nvd0_gpio_reset;
|
||||||
priv->base.drive = nvd0_gpio_drive;
|
priv->base.drive = nvd0_gpio_drive;
|
||||||
priv->base.sense = nvd0_gpio_sense;
|
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;
|
nv_subdev(priv)->intr = nv50_gpio_intr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,6 @@ MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)");
|
||||||
static int nouveau_duallink = 1;
|
static int nouveau_duallink = 1;
|
||||||
module_param_named(duallink, nouveau_duallink, int, 0400);
|
module_param_named(duallink, nouveau_duallink, int, 0400);
|
||||||
|
|
||||||
static void nouveau_connector_hotplug(void *, int);
|
|
||||||
|
|
||||||
struct nouveau_encoder *
|
struct nouveau_encoder *
|
||||||
find_encoder(struct drm_connector *connector, int type)
|
find_encoder(struct drm_connector *connector, int type)
|
||||||
{
|
{
|
||||||
|
@ -100,22 +98,6 @@ static void
|
||||||
nouveau_connector_destroy(struct drm_connector *connector)
|
nouveau_connector_destroy(struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
struct nouveau_connector *nv_connector = nouveau_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);
|
kfree(nv_connector->edid);
|
||||||
drm_sysfs_connector_remove(connector);
|
drm_sysfs_connector_remove(connector);
|
||||||
drm_connector_cleanup(connector);
|
drm_connector_cleanup(connector);
|
||||||
|
@ -912,6 +894,37 @@ nouveau_connector_funcs_lvds = {
|
||||||
.force = nouveau_connector_force
|
.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
|
static int
|
||||||
drm_conntype_from_dcb(enum dcb_connector_type dcb)
|
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);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
connector = &nv_connector->base;
|
connector = &nv_connector->base;
|
||||||
|
INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work);
|
||||||
nv_connector->index = index;
|
nv_connector->index = index;
|
||||||
|
|
||||||
/* attempt to parse vbios connector type and hotplug gpio */
|
/* 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)],
|
ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)],
|
||||||
DCB_GPIO_UNUSED, &nv_connector->hpd);
|
DCB_GPIO_UNUSED, &nv_connector->hpd);
|
||||||
|
nv_connector->hpd_func.func = nouveau_connector_hotplug;
|
||||||
if (ret)
|
if (ret)
|
||||||
nv_connector->hpd.func = DCB_GPIO_UNUSED;
|
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;
|
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||||
if (gpio && nv_connector->hpd.func != DCB_GPIO_UNUSED) {
|
if (nv_connector->hpd.func != DCB_GPIO_UNUSED)
|
||||||
ret = gpio->isr_add(gpio, 0, nv_connector->hpd.func, 0xff,
|
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||||
nouveau_connector_hotplug, connector);
|
|
||||||
if (ret == 0)
|
|
||||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
|
||||||
}
|
|
||||||
|
|
||||||
drm_sysfs_connector_add(connector);
|
drm_sysfs_connector_add(connector);
|
||||||
return 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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
#include <drm/drm_edid.h>
|
#include <drm/drm_edid.h>
|
||||||
#include "nouveau_crtc.h"
|
#include "nouveau_crtc.h"
|
||||||
|
|
||||||
|
#include <core/event.h>
|
||||||
|
|
||||||
#include <subdev/bios.h>
|
#include <subdev/bios.h>
|
||||||
#include <subdev/bios/gpio.h>
|
#include <subdev/bios/gpio.h>
|
||||||
|
|
||||||
|
@ -62,10 +64,13 @@ enum nouveau_dithering_depth {
|
||||||
struct nouveau_connector {
|
struct nouveau_connector {
|
||||||
struct drm_connector base;
|
struct drm_connector base;
|
||||||
enum dcb_connector_type type;
|
enum dcb_connector_type type;
|
||||||
struct dcb_gpio_func hpd;
|
|
||||||
u8 index;
|
u8 index;
|
||||||
u8 *dcb;
|
u8 *dcb;
|
||||||
|
|
||||||
|
struct dcb_gpio_func hpd;
|
||||||
|
struct work_struct hpd_work;
|
||||||
|
struct nouveau_eventh hpd_func;
|
||||||
|
|
||||||
int dithering_mode;
|
int dithering_mode;
|
||||||
int dithering_depth;
|
int dithering_depth;
|
||||||
int scaling_mode;
|
int scaling_mode;
|
||||||
|
|
|
@ -233,8 +233,10 @@ nouveau_display_init(struct drm_device *dev)
|
||||||
/* enable hotplug interrupts */
|
/* enable hotplug interrupts */
|
||||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||||
struct nouveau_connector *conn = nouveau_connector(connector);
|
struct nouveau_connector *conn = nouveau_connector(connector);
|
||||||
if (gpio)
|
if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
|
||||||
gpio->irq(gpio, 0, conn->hpd.func, 0xff, true);
|
nouveau_event_get(gpio->events, conn->hpd.line,
|
||||||
|
&conn->hpd_func);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -251,8 +253,10 @@ nouveau_display_fini(struct drm_device *dev)
|
||||||
/* disable hotplug interrupts */
|
/* disable hotplug interrupts */
|
||||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||||
struct nouveau_connector *conn = nouveau_connector(connector);
|
struct nouveau_connector *conn = nouveau_connector(connector);
|
||||||
if (gpio)
|
if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) {
|
||||||
gpio->irq(gpio, 0, conn->hpd.func, 0xff, false);
|
nouveau_event_put(gpio->events, conn->hpd.line,
|
||||||
|
&conn->hpd_func);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_kms_helper_poll_disable(dev);
|
drm_kms_helper_poll_disable(dev);
|
||||||
|
|
|
@ -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
|
* we take during link training (DP_SET_POWER is one), we need
|
||||||
* to ignore them for the moment to avoid races.
|
* 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 */
|
/* enable down-spreading and execute pre-train script from vbios */
|
||||||
dp_link_train_init(dev, &dp, nv_encoder->dp.dpcd[3] & 1);
|
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);
|
dp_link_train_fini(dev, &dp);
|
||||||
|
|
||||||
/* re-enable hotplug detect */
|
/* 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue