diff options
90 files changed, 3346 insertions, 1486 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 1aaa2ef577d9..2b6156d0e4b5 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -125,17 +125,22 @@ nouveau-y += core/subdev/fb/gddr5.o nouveau-y += core/subdev/gpio/base.o nouveau-y += core/subdev/gpio/nv10.o nouveau-y += core/subdev/gpio/nv50.o +nouveau-y += core/subdev/gpio/nv92.o nouveau-y += core/subdev/gpio/nvd0.o nouveau-y += core/subdev/gpio/nve0.o nouveau-y += core/subdev/i2c/base.o nouveau-y += core/subdev/i2c/anx9805.o nouveau-y += core/subdev/i2c/aux.o nouveau-y += core/subdev/i2c/bit.o +nouveau-y += core/subdev/i2c/pad.o +nouveau-y += core/subdev/i2c/padnv04.o +nouveau-y += core/subdev/i2c/padnv94.o nouveau-y += core/subdev/i2c/nv04.o nouveau-y += core/subdev/i2c/nv4e.o nouveau-y += core/subdev/i2c/nv50.o nouveau-y += core/subdev/i2c/nv94.o nouveau-y += core/subdev/i2c/nvd0.o +nouveau-y += core/subdev/i2c/nve0.o nouveau-y += core/subdev/ibus/nvc0.o nouveau-y += core/subdev/ibus/nve0.o nouveau-y += core/subdev/ibus/gk20a.o @@ -217,6 +222,9 @@ nouveau-y += core/engine/device/nvc0.o nouveau-y += core/engine/device/nve0.o nouveau-y += core/engine/device/gm100.o nouveau-y += core/engine/disp/base.o +nouveau-y += core/engine/disp/conn.o +nouveau-y += core/engine/disp/outp.o +nouveau-y += core/engine/disp/outpdp.o nouveau-y += core/engine/disp/nv04.o nouveau-y += core/engine/disp/nv50.o nouveau-y += core/engine/disp/nv84.o diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c index 3f3c76581a9e..ae81d3b5d8b7 100644 --- a/drivers/gpu/drm/nouveau/core/core/event.c +++ b/drivers/gpu/drm/nouveau/core/core/event.c @@ -28,14 +28,20 @@ nouveau_event_put(struct nouveau_eventh *handler) { struct nouveau_event *event = handler->event; unsigned long flags; - if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) { - spin_lock_irqsave(&event->refs_lock, flags); - if (!--event->index[handler->index].refs) { + u32 m, t; + + if (!__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) + return; + + spin_lock_irqsave(&event->refs_lock, flags); + for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) { + if (!--event->refs[handler->index * event->types_nr + t]) { if (event->disable) - event->disable(event, handler->index); + event->disable(event, 1 << t, handler->index); } - spin_unlock_irqrestore(&event->refs_lock, flags); + } + spin_unlock_irqrestore(&event->refs_lock, flags); } void @@ -43,14 +49,20 @@ nouveau_event_get(struct nouveau_eventh *handler) { struct nouveau_event *event = handler->event; unsigned long flags; - if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) { - spin_lock_irqsave(&event->refs_lock, flags); - if (!event->index[handler->index].refs++) { + u32 m, t; + + if (__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) + return; + + spin_lock_irqsave(&event->refs_lock, flags); + for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) { + if (!event->refs[handler->index * event->types_nr + t]++) { if (event->enable) - event->enable(event, handler->index); + event->enable(event, 1 << t, handler->index); } - spin_unlock_irqrestore(&event->refs_lock, flags); + } + spin_unlock_irqrestore(&event->refs_lock, flags); } static void @@ -65,38 +77,47 @@ nouveau_event_fini(struct nouveau_eventh *handler) } static int -nouveau_event_init(struct nouveau_event *event, int index, - int (*func)(void *, int), void *priv, +nouveau_event_init(struct nouveau_event *event, u32 types, int index, + int (*func)(void *, u32, int), void *priv, struct nouveau_eventh *handler) { unsigned long flags; + if (types & ~((1 << event->types_nr) - 1)) + return -EINVAL; if (index >= event->index_nr) return -EINVAL; handler->event = event; handler->flags = 0; + handler->types = types; handler->index = index; handler->func = func; handler->priv = priv; spin_lock_irqsave(&event->list_lock, flags); - list_add_tail(&handler->head, &event->index[index].list); + list_add_tail(&handler->head, &event->list[index]); spin_unlock_irqrestore(&event->list_lock, flags); return 0; } int -nouveau_event_new(struct nouveau_event *event, int index, - int (*func)(void *, int), void *priv, +nouveau_event_new(struct nouveau_event *event, u32 types, int index, + int (*func)(void *, u32, int), void *priv, struct nouveau_eventh **phandler) { struct nouveau_eventh *handler; int ret = -ENOMEM; + if (event->check) { + ret = event->check(event, types, index); + if (ret) + return ret; + } + handler = *phandler = kmalloc(sizeof(*handler), GFP_KERNEL); if (handler) { - ret = nouveau_event_init(event, index, func, priv, handler); + ret = nouveau_event_init(event, types, index, func, priv, handler); if (ret) kfree(handler); } @@ -116,7 +137,7 @@ nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref) } void -nouveau_event_trigger(struct nouveau_event *event, int index) +nouveau_event_trigger(struct nouveau_event *event, u32 types, int index) { struct nouveau_eventh *handler; unsigned long flags; @@ -125,10 +146,15 @@ nouveau_event_trigger(struct nouveau_event *event, int index) return; spin_lock_irqsave(&event->list_lock, flags); - list_for_each_entry(handler, &event->index[index].list, head) { - if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) && - handler->func(handler->priv, index) == NVKM_EVENT_DROP) - nouveau_event_put(handler); + list_for_each_entry(handler, &event->list[index], head) { + if (!test_bit(NVKM_EVENT_ENABLE, &handler->flags)) + continue; + if (!(handler->types & types)) + continue; + if (handler->func(handler->priv, handler->types & types, index) + != NVKM_EVENT_DROP) + continue; + nouveau_event_put(handler); } spin_unlock_irqrestore(&event->list_lock, flags); } @@ -144,20 +170,27 @@ nouveau_event_destroy(struct nouveau_event **pevent) } int -nouveau_event_create(int index_nr, struct nouveau_event **pevent) +nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **pevent) { struct nouveau_event *event; int i; - event = *pevent = kzalloc(sizeof(*event) + index_nr * - sizeof(event->index[0]), GFP_KERNEL); + event = *pevent = kzalloc(sizeof(*event) + (index_nr * types_nr) * + sizeof(event->refs[0]), GFP_KERNEL); if (!event) return -ENOMEM; + event->list = kmalloc(sizeof(*event->list) * index_nr, GFP_KERNEL); + if (!event->list) { + kfree(event); + return -ENOMEM; + } + spin_lock_init(&event->list_lock); spin_lock_init(&event->refs_lock); for (i = 0; i < index_nr; i++) - INIT_LIST_HEAD(&event->index[i].list); + INIT_LIST_HEAD(&event->list[i]); + event->types_nr = types_nr; event->index_nr = index_nr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c index d258c21c4a22..a520029e25d9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c @@ -60,8 +60,8 @@ gm100_identify(struct nouveau_device *device) case 0x117: device->cname = "GM107"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; #if 0 device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c index 0a51ff4e9e00..40b29d0214cb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c @@ -47,7 +47,7 @@ nv04_identify(struct nouveau_device *device) case 0x04: device->cname = "NV04"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv04_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -65,7 +65,7 @@ nv04_identify(struct nouveau_device *device) case 0x05: device->cname = "NV05"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv05_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c index e008de8b51b0..5f7c25ff523d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c @@ -48,8 +48,8 @@ nv10_identify(struct nouveau_device *device) case 0x10: device->cname = "NV10"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -65,8 +65,8 @@ nv10_identify(struct nouveau_device *device) case 0x15: device->cname = "NV15"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -84,8 +84,8 @@ nv10_identify(struct nouveau_device *device) case 0x16: device->cname = "NV16"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -103,8 +103,8 @@ nv10_identify(struct nouveau_device *device) case 0x1a: device->cname = "nForce"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -122,8 +122,8 @@ nv10_identify(struct nouveau_device *device) case 0x11: device->cname = "NV11"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -141,8 +141,8 @@ nv10_identify(struct nouveau_device *device) case 0x17: device->cname = "NV17"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -160,8 +160,8 @@ nv10_identify(struct nouveau_device *device) case 0x1f: device->cname = "nForce2"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -179,8 +179,8 @@ nv10_identify(struct nouveau_device *device) case 0x18: device->cname = "NV18"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c index 7b629a3aed05..75fed11bba0a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c @@ -49,8 +49,8 @@ nv20_identify(struct nouveau_device *device) case 0x20: device->cname = "NV20"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -68,8 +68,8 @@ nv20_identify(struct nouveau_device *device) case 0x25: device->cname = "NV25"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -87,8 +87,8 @@ nv20_identify(struct nouveau_device *device) case 0x28: device->cname = "NV28"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -106,8 +106,8 @@ nv20_identify(struct nouveau_device *device) case 0x2a: device->cname = "NV2A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c index 7dfddd5a1908..36919d7db7cc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c @@ -49,8 +49,8 @@ nv30_identify(struct nouveau_device *device) case 0x30: device->cname = "NV30"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -68,8 +68,8 @@ nv30_identify(struct nouveau_device *device) case 0x35: device->cname = "NV35"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -87,8 +87,8 @@ nv30_identify(struct nouveau_device *device) case 0x31: device->cname = "NV31"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -107,8 +107,8 @@ nv30_identify(struct nouveau_device *device) case 0x36: device->cname = "NV36"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; @@ -127,8 +127,8 @@ nv30_identify(struct nouveau_device *device) case 0x34: device->cname = "NV34"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c index 7c1ce6cf4f1f..1130a62be2c7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c @@ -53,8 +53,8 @@ nv40_identify(struct nouveau_device *device) case 0x40: device->cname = "NV40"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -76,8 +76,8 @@ nv40_identify(struct nouveau_device *device) case 0x41: device->cname = "NV41"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -99,8 +99,8 @@ nv40_identify(struct nouveau_device *device) case 0x42: device->cname = "NV42"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -122,8 +122,8 @@ nv40_identify(struct nouveau_device *device) case 0x43: device->cname = "NV43"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -145,8 +145,8 @@ nv40_identify(struct nouveau_device *device) case 0x45: device->cname = "NV45"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -168,8 +168,8 @@ nv40_identify(struct nouveau_device *device) case 0x47: device->cname = "G70"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -191,8 +191,8 @@ nv40_identify(struct nouveau_device *device) case 0x49: device->cname = "G71"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -214,8 +214,8 @@ nv40_identify(struct nouveau_device *device) case 0x4b: device->cname = "G73"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -237,8 +237,8 @@ nv40_identify(struct nouveau_device *device) case 0x44: device->cname = "NV44"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -260,8 +260,8 @@ nv40_identify(struct nouveau_device *device) case 0x46: device->cname = "G72"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -283,8 +283,8 @@ nv40_identify(struct nouveau_device *device) case 0x4a: device->cname = "NV44A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -306,8 +306,8 @@ nv40_identify(struct nouveau_device *device) case 0x4c: device->cname = "C61"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -329,8 +329,8 @@ nv40_identify(struct nouveau_device *device) case 0x4e: device->cname = "C51"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv4e_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv4e_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -352,8 +352,8 @@ nv40_identify(struct nouveau_device *device) case 0x63: device->cname = "C73"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -375,8 +375,8 @@ nv40_identify(struct nouveau_device *device) case 0x67: device->cname = "C67"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; @@ -398,8 +398,8 @@ nv40_identify(struct nouveau_device *device) case 0x68: device->cname = "C68"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c index 66499fa0f758..ef0b0bde1a91 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c @@ -60,8 +60,8 @@ nv50_identify(struct nouveau_device *device) case 0x50: device->cname = "G80"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -85,8 +85,8 @@ nv50_identify(struct nouveau_device *device) case 0x84: device->cname = "G84"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -113,8 +113,8 @@ nv50_identify(struct nouveau_device *device) case 0x86: device->cname = "G86"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -141,8 +141,8 @@ nv50_identify(struct nouveau_device *device) case 0x92: device->cname = "G92"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -169,8 +169,8 @@ nv50_identify(struct nouveau_device *device) case 0x94: device->cname = "G94"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -197,8 +197,8 @@ nv50_identify(struct nouveau_device *device) case 0x96: device->cname = "G96"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -225,8 +225,8 @@ nv50_identify(struct nouveau_device *device) case 0x98: device->cname = "G98"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -253,8 +253,8 @@ nv50_identify(struct nouveau_device *device) case 0xa0: device->cname = "G200"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -281,8 +281,8 @@ nv50_identify(struct nouveau_device *device) case 0xaa: device->cname = "MCP77/MCP78"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -309,8 +309,8 @@ nv50_identify(struct nouveau_device *device) case 0xac: device->cname = "MCP79/MCP7A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -337,8 +337,8 @@ nv50_identify(struct nouveau_device *device) case 0xa3: device->cname = "GT215"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -367,8 +367,8 @@ nv50_identify(struct nouveau_device *device) case 0xa5: device->cname = "GT216"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -396,8 +396,8 @@ nv50_identify(struct nouveau_device *device) case 0xa8: device->cname = "GT218"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -425,8 +425,8 @@ nv50_identify(struct nouveau_device *device) case 0xaf: device->cname = "MCP89"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index 2075b3027052..f199957995fa 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -60,8 +60,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc0: device->cname = "GF100"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -92,8 +92,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc4: device->cname = "GF104"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -124,8 +124,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc3: device->cname = "GF106"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -155,8 +155,8 @@ nvc0_identify(struct nouveau_device *device) case 0xce: device->cname = "GF114"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -187,8 +187,8 @@ nvc0_identify(struct nouveau_device *device) case 0xcf: device->cname = "GF116"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -219,8 +219,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc1: device->cname = "GF108"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -250,8 +250,8 @@ nvc0_identify(struct nouveau_device *device) case 0xc8: device->cname = "GF110"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -282,8 +282,8 @@ nvc0_identify(struct nouveau_device *device) case 0xd9: device->cname = "GF119"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -313,8 +313,8 @@ nvc0_identify(struct nouveau_device *device) case 0xd7: device->cname = "GF117"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 459099e2515f..2d1e97d4264f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -60,8 +60,8 @@ nve0_identify(struct nouveau_device *device) case 0xe4: device->cname = "GK104"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -93,8 +93,8 @@ nve0_identify(struct nouveau_device *device) case 0xe7: device->cname = "GK107"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -126,8 +126,8 @@ nve0_identify(struct nouveau_device *device) case 0xe6: device->cname = "GK106"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -176,8 +176,8 @@ nve0_identify(struct nouveau_device *device) case 0xf0: device->cname = "GK110"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -209,8 +209,8 @@ nve0_identify(struct nouveau_device *device) case 0xf1: device->cname = "GK110B"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -242,8 +242,8 @@ nve0_identify(struct nouveau_device *device) case 0x108: device->cname = "GK208"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = &nve0_gpio_oclass; - device->oclass[NVDEV_SUBDEV_I2C ] = &nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c index 7a5cae42834f..c41f656abe64 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/base.c @@ -22,13 +22,87 @@ * Authors: Ben Skeggs */ -#include <engine/disp.h> +#include "priv.h" +#include "outp.h" +#include "conn.h" + +static int +nouveau_disp_hpd_check(struct nouveau_event *event, u32 types, int index) +{ + struct nouveau_disp *disp = event->priv; + struct nvkm_output *outp; + list_for_each_entry(outp, &disp->outp, head) { + if (outp->conn->index == index) { + if (outp->conn->hpd.event) + return 0; + break; + } + } + return -ENOSYS; +} + +int +_nouveau_disp_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_disp *disp = (void *)object; + struct nvkm_output *outp; + int ret; + + list_for_each_entry(outp, &disp->outp, head) { + ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend); + if (ret && suspend) + goto fail_outp; + } + + return nouveau_engine_fini(&disp->base, suspend); + +fail_outp: + list_for_each_entry_continue_reverse(outp, &disp->outp, head) { + nv_ofuncs(outp)->init(nv_object(outp)); + } + + return ret; +} + +int +_nouveau_disp_init(struct nouveau_object *object) +{ + struct nouveau_disp *disp = (void *)object; + struct nvkm_output *outp; + int ret; + + ret = nouveau_engine_init(&disp->base); + if (ret) + return ret; + + list_for_each_entry(outp, &disp->outp, head) { + ret = nv_ofuncs(outp)->init(nv_object(outp)); + if (ret) + goto fail_outp; + } + + return ret; + +fail_outp: + list_for_each_entry_continue_reverse(outp, &disp->outp, head) { + nv_ofuncs(outp)->fini(nv_object(outp), false); + } + + return ret; +} void _nouveau_disp_dtor(struct nouveau_object *object) { struct nouveau_disp *disp = (void *)object; + struct nvkm_output *outp, *outt; + nouveau_event_destroy(&disp->vblank); + + list_for_each_entry_safe(outp, outt, &disp->outp, head) { + nouveau_object_ref(NULL, (struct nouveau_object **)&outp); + } + nouveau_engine_destroy(&disp->base); } @@ -39,8 +113,15 @@ nouveau_disp_create_(struct nouveau_object *parent, const char *intname, const char *extname, int length, void **pobject) { + struct nouveau_disp_impl *impl = (void *)oclass; + struct nouveau_bios *bios = nouveau_bios(parent); struct nouveau_disp *disp; - int ret; + struct nouveau_oclass **sclass; + struct nouveau_object *object; + struct dcb_output dcbE; + u8 hpd = 0, ver, hdr; + u32 data; + int ret, i; ret = nouveau_engine_create_(parent, engine, oclass, true, intname, extname, length, pobject); @@ -48,5 +129,42 @@ nouveau_disp_create_(struct nouveau_object *parent, if (ret) return ret; - return nouveau_event_create(heads, &disp->vblank); + INIT_LIST_HEAD(&disp->outp); + + /* create output objects for each display path in the vbios */ + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { + if (dcbE.type == DCB_OUTPUT_UNUSED) + continue; + if (dcbE.type == DCB_OUTPUT_EOL) + break; + data = dcbE.location << 4 | dcbE.type; + + oclass = nvkm_output_oclass; + sclass = impl->outp; + while (sclass && sclass[0]) { + if (sclass[0]->handle == data) { + oclass = sclass[0]; + break; + } + sclass++; + } + + nouveau_object_ctor(*pobject, *pobject, oclass, + &dcbE, i, &object); + hpd = max(hpd, (u8)(dcbE.connector + 1)); + } + + ret = nouveau_event_create(3, hpd, &disp->hpd); + if (ret) + return ret; + + disp->hpd->priv = disp; + disp->hpd->check = nouveau_disp_hpd_check; + + ret = nouveau_event_create(1, heads, &disp->vblank); + if (ret) + return ret; + + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c new file mode 100644 index 000000000000..4ffbc70ecf5a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c @@ -0,0 +1,172 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/gpio.h> + +#include "conn.h" +#include "outp.h" + +static void +nvkm_connector_hpd_work(struct work_struct *w) +{ + struct nvkm_connector *conn = container_of(w, typeof(*conn), hpd.work); + struct nouveau_disp *disp = nouveau_disp(conn); + struct nouveau_gpio *gpio = nouveau_gpio(conn); + u32 send = NVKM_HPD_UNPLUG; + if (gpio->get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.event->index)) + send = NVKM_HPD_PLUG; + nouveau_event_trigger(disp->hpd, send, conn->index); + nouveau_event_get(conn->hpd.event); +} + +static int +nvkm_connector_hpd(void *data, u32 type, int index) +{ + struct nvkm_connector *conn = data; + DBG("HPD: %d\n", type); + schedule_work(&conn->hpd.work); + return NVKM_EVENT_DROP; +} + +int +_nvkm_connector_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_connector *conn = (void *)object; + if (conn->hpd.event) + nouveau_event_put(conn->hpd.event); + return nouveau_object_fini(&conn->base, suspend); +} + +int +_nvkm_connector_init(struct nouveau_object *object) +{ + struct nvkm_connector *conn = (void *)object; + int ret = nouveau_object_init(&conn->base); + if (ret == 0) { + if (conn->hpd.event) + nouveau_event_get(conn->hpd.event); + } + return ret; +} + +void +_nvkm_connector_dtor(struct nouveau_object *object) +{ + struct nvkm_connector *conn = (void *)object; + nouveau_event_ref(NULL, &conn->hpd.event); + nouveau_object_destroy(&conn->base); +} + +int +nvkm_connector_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + struct nvbios_connE *info, int index, + int length, void **pobject) +{ + static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 }; + struct nouveau_gpio *gpio = nouveau_gpio(parent); + struct nouveau_disp *disp = (void *)engine; + struct nvkm_connector *conn; + struct nvkm_output *outp; + struct dcb_gpio_func func; + int ret; + + list_for_each_entry(outp, &disp->outp, head) { + if (outp->conn && outp->conn->index == index) { + atomic_inc(&nv_object(outp->conn)->refcount); + *pobject = outp->conn; + return 1; + } + } + + ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject); + conn = *pobject; + if (ret) + return ret; + + conn->info = *info; + conn->index = index; + + DBG("type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x\n", + info->type, info->location, info->hpd, info->dp, + info->di, info->sr, info->lcdid); + + if ((info->hpd = ffs(info->hpd))) { + if (--info->hpd >= ARRAY_SIZE(hpd)) { + ERR("hpd %02x unknown\n", info->hpd); + goto done; + } + info->hpd = hpd[info->hpd]; + + ret = gpio->find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func); + if (ret) { + ERR("func %02x lookup failed, %d\n", info->hpd, ret); + goto done; + } + + ret = nouveau_event_new(gpio->events, NVKM_GPIO_TOGGLED, + func.line, nvkm_connector_hpd, + conn, &conn->hpd.event); + if (ret) { + ERR("func %02x failed, %d\n", info->hpd, ret); + } else { + DBG("func %02x (HPD)\n", info->hpd); + } + } + +done: + INIT_WORK(&conn->hpd.work, nvkm_connector_hpd_work); + return 0; +} + +int +_nvkm_connector_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_connector *conn; + int ret; + + ret = nvkm_connector_create(parent, engine, oclass, info, index, &conn); + *pobject = nv_object(conn); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass * +nvkm_connector_oclass = &(struct nvkm_connector_impl) { + .base = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_connector_ctor, + .dtor = _nvkm_connector_dtor, + .init = _nvkm_connector_init, + .fini = _nvkm_connector_fini, + }, + }, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.h b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h new file mode 100644 index 000000000000..035ebeacbb1c --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h @@ -0,0 +1,59 @@ +#ifndef __NVKM_DISP_CONN_H__ +#define __NVKM_DISP_CONN_H__ + +#include "priv.h" + +struct nvkm_connector { + struct nouveau_object base; + struct list_head head; + + struct nvbios_connE info; + int index; + + struct { + struct nouveau_eventh *event; + struct work_struct work; + } hpd; +}; + +#define nvkm_connector_create(p,e,c,b,i,d) \ + nvkm_connector_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_connector_destroy(d) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_dtor(nv_object(disp)); \ +}) +#define nvkm_connector_init(d) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_init(nv_object(disp)); \ +}) +#define nvkm_connector_fini(d,s) ({ \ + struct nvkm_connector *disp = (d); \ + _nvkm_connector_fini(nv_object(disp), (s)); \ +}) + +int nvkm_connector_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct nvbios_connE *, + int, int, void **); + +int _nvkm_connector_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nvkm_connector_dtor(struct nouveau_object *); +int _nvkm_connector_init(struct nouveau_object *); +int _nvkm_connector_fini(struct nouveau_object *, bool); + +struct nvkm_connector_impl { + struct nouveau_oclass base; +}; + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_connector *_conn = (void *)conn; \ + nv_##l(nv_object(conn)->engine, "%02x:%02x%02x: "f, _conn->index, \ + _conn->info.location, _conn->info.type, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c index 13903533d7a2..39562d48101d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c @@ -30,42 +30,38 @@ #include <engine/disp.h> -#include "dport.h" +#include <core/class.h> -#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt, \ - dp->outp->hasht, dp->outp->hashm, ##args) -#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt, \ - dp->outp->hasht, dp->outp->hashm, ##args) +#include "dport.h" +#include "outpdp.h" /****************************************************************************** * link training *****************************************************************************/ struct dp_state { - const struct nouveau_dp_func *func; - struct nouveau_disp *disp; - struct dcb_output *outp; - struct nvbios_dpout info; - u8 version; - struct nouveau_i2c_port *aux; - int head; - u8 dpcd[16]; + struct nvkm_output_dp *outp; int link_nr; u32 link_bw; u8 stat[6]; u8 conf[4]; + bool pc2; + u8 pc2stat; + u8 pc2conf[2]; }; static int dp_set_link_config(struct dp_state *dp) { - struct nouveau_disp *disp = dp->disp; + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; + struct nouveau_disp *disp = nouveau_disp(outp); struct nouveau_bios *bios = nouveau_bios(disp); struct nvbios_init init = { - .subdev = nv_subdev(dp->disp), + .subdev = nv_subdev(disp), .bios = bios, .offset = 0x0000, - .outp = dp->outp, - .crtc = dp->head, + .outp = &outp->base.info, + .crtc = -1, .execute = 1, }; u32 lnkcmp; @@ -75,8 +71,8 @@ dp_set_link_config(struct dp_state *dp) DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); /* set desired link configuration on the source */ - if ((lnkcmp = dp->info.lnkcmp)) { - if (dp->version < 0x30) { + if ((lnkcmp = dp->outp->info.lnkcmp)) { + if (outp->version < 0x30) { while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp)) lnkcmp += 4; init.offset = nv_ro16(bios, lnkcmp + 2); @@ -89,76 +85,112 @@ dp_set_link_config(struct dp_state *dp) nvbios_exec(&init); } - ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head, - dp->link_nr, dp->link_bw / 27000, - dp->dpcd[DPCD_RC02] & - DPCD_RC02_ENHANCED_FRAME_CAP); + ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000, + outp->dpcd[DPCD_RC02] & + DPCD_RC02_ENHANCED_FRAME_CAP); if (ret) { - ERR("lnk_ctl failed with %d\n", ret); + if (ret < 0) + ERR("lnk_ctl failed with %d\n", ret); return ret; } + impl->lnk_pwr(outp, dp->link_nr); + /* set desired link configuration on the sink */ sink[0] = dp->link_bw / 27000; sink[1] = dp->link_nr; - if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) + if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; - return nv_wraux(dp->aux, DPCD_LC00, sink, 2); + return nv_wraux(outp->base.edid, DPCD_LC00_LINK_BW_SET, sink, 2); } static void dp_set_training_pattern(struct dp_state *dp, u8 pattern) { + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; u8 sink_tp; DBG("training pattern %d\n", pattern); - dp->func->pattern(dp->disp, dp->outp, dp->head, pattern); + impl->pattern(outp, pattern); - nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); + nv_rdaux(outp->base.edid, DPCD_LC02, &sink_tp, 1); sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; sink_tp |= pattern; - nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); + nv_wraux(outp->base.edid, DPCD_LC02, &sink_tp, 1); } static int -dp_link_train_commit(struct dp_state *dp) +dp_link_train_commit(struct dp_state *dp, bool pc) { - int i; + struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp); + struct nvkm_output_dp *outp = dp->outp; + int ret, i; for (i = 0; i < dp->link_nr; i++) { u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; + u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3; u8 lpre = (lane & 0x0c) >> 2; u8 lvsw = (lane & 0x03) >> 0; + u8 hivs = 3 - lpre; + u8 hipe = 3; + u8 hipc = 3; + + if (lpc2 >= hipc) + lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; + if (lpre >= hipe) { + lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ + lvsw = hivs = 3 - (lpre & 3); + } else + if (lvsw >= hivs) { + lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; + } dp->conf[i] = (lpre << 3) | lvsw; - if (lvsw == 3) - dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED; - if (lpre == 3) - dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED; + dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); - DBG("config lane %d %02x\n", i, dp->conf[i]); - dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre); + DBG("config lane %d %02x %02x\n", i, dp->conf[i], lpc2); + impl->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3); } - return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4); + ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4); + if (ret) + return ret; + + if (pc) { + ret = nv_wraux(outp->base.edid, DPCD_LC0F, dp->pc2conf, 2); + if (ret) + return ret; + } + + return 0; } static int -dp_link_train_update(struct dp_state *dp, u32 delay) +dp_link_train_update(struct dp_state *dp, bool pc, u32 delay) { + struct nvkm_output_dp *outp = dp->outp; int ret; - if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) - mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); + if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) + mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); else udelay(delay); - ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6); + ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6); if (ret) return ret; - DBG("status %6ph\n", dp->stat); + if (pc) { + ret = nv_rdaux(outp->base.edid, DPCD_LS0C, &dp->pc2stat, 1); + if (ret) + dp->pc2stat = 0x00; + DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat); + } else { + DBG("status %6ph\n", dp->stat); + } + return 0; } @@ -172,8 +204,8 @@ dp_link_train_cr(struct dp_state *dp) dp_set_training_pattern(dp, 1); do { - if (dp_link_train_commit(dp) || - dp_link_train_update(dp, 100)) + if (dp_link_train_commit(dp, false) || + dp_link_train_update(dp, false, 100)) break; cr_done = true; @@ -199,16 +231,17 @@ dp_link_train_cr(struct dp_state *dp) static int dp_link_train_eq(struct dp_state *dp) { + struct nvkm_output_dp *outp = dp->outp; bool eq_done = false, cr_done = true; int tries = 0, i; - if (dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED) + if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED) dp_set_training_pattern(dp, 3); else dp_set_training_pattern(dp, 2); do { - if (dp_link_train_update(dp, 400)) + if (dp_link_train_update(dp, dp->pc2, 400)) break; eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); @@ -221,7 +254,7 @@ dp_link_train_eq(struct dp_state *dp) eq_done = false; } - if (dp_link_train_commit(dp)) + if (dp_link_train_commit(dp, dp->pc2)) break; } while (!eq_done && cr_done && ++tries <= 5); @@ -231,123 +264,109 @@ dp_link_train_eq(struct dp_state *dp) static void dp_link_train_init(struct dp_state *dp, bool spread) { + struct nvkm_output_dp *outp = dp->outp; + struct nouveau_disp *disp = nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(disp); struct nvbios_init init = { - .subdev = nv_subdev(dp->disp), - .bios = nouveau_bios(dp->disp), - .outp = dp->outp, - .crtc = dp->head, + .subdev = nv_subdev(disp), + .bios = bios, + .outp = &outp->base.info, + .crtc = -1, .execute = 1, }; /* set desired spread */ if (spread) - init.offset = dp->info.script[2]; + init.offset = outp->info.script[2]; else - init.offset = dp->info.script[3]; + init.offset = outp->info.script[3]; nvbios_exec(&init); /* pre-train script */ - init.offset = dp->info.script[0]; + init.offset = outp->info.script[0]; nvbios_exec(&init); } static void dp_link_train_fini(struct dp_state *dp) { + struct nvkm_output_dp *outp = dp->outp; + struct nouveau_disp *disp = nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(disp); struct nvbios_init init = { - .subdev = nv_subdev(dp->disp), - .bios = nouveau_bios(dp->disp), - .outp = dp->outp, - .crtc = dp->head, + .subdev = nv_subdev(disp), + .bios = bios, + .outp = &outp->base.info, + .crtc = -1, .execute = 1, }; /* post-train script */ - init.offset = dp->info.script[1], + init.offset = outp->info.script[1], nvbios_exec(&init); } -int -nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, - struct dcb_output *outp, int head, u32 datarate) +static const struct dp_rates { + u32 rate; + u8 bw; + u8 nr; +} nouveau_dp_rates[] = { + { 2160000, 0x14, 4 }, + { 1080000, 0x0a, 4 }, + { 1080000, 0x14, 2 }, + { 648000, 0x06, 4 }, + { 540000, 0x0a, 2 }, + { 540000, 0x14, 1 }, + { 324000, 0x06, 2 }, + { 270000, 0x0a, 1 }, + { 162000, 0x06, 1 }, + {} +}; + +void +nouveau_dp_train(struct work_struct *w) { - struct nouveau_bios *bios = nouveau_bios(disp); - struct nouveau_i2c *i2c = nouveau_i2c(disp); + struct nvkm_output_dp *outp = container_of(w, typeof(*outp), lt.work); + struct nouveau_disp *disp = nouveau_disp(outp); + const struct dp_rates *cfg = nouveau_dp_rates; struct dp_state _dp = { - .disp = disp, - .func = func, .outp = outp, - .head = head, }, *dp = &_dp; - const u32 bw_list[] = { 540000, 270000, 162000, 0 }; - const u32 *link_bw = bw_list; - u8 hdr, cnt, len; - u32 data; + u32 datarate = 0; int ret; - /* find the bios displayport data relevant to this output */ - data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version, - &hdr, &cnt, &len, &dp->info); - if (!data) { - ERR("bios data not found\n"); - return -EINVAL; - } - - /* acquire the aux channel and fetch some info about the display */ - if (outp->location) - dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev)); - else - dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index)); - if (!dp->aux) { - ERR("no aux channel?!\n"); - return -ENODEV; - } - - ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd)); - if (ret) { - /* it's possible the display has been unplugged before we - * get here. we still need to execute the full set of - * vbios scripts, and program the OR at a high enough - * frequency to satisfy the target mode. failure to do - * so results at best in an UPDATE hanging, and at worst - * with PDISP running away to join the circus. - */ - dp->dpcd[1] = link_bw[0] / 27000; - dp->dpcd[2] = 4; - dp->dpcd[3] = 0x00; - ERR("failed to read DPCD\n"); - } - /* bring capabilities within encoder limits */ - if (nv_oclass(disp)->handle < NV_ENGINE(DISP, 0x90)) - dp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED; - if ((dp->dpcd[2] & 0x1f) > dp->outp->dpconf.link_nr) { - dp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT; - dp->dpcd[2] |= dp->outp->dpconf.link_nr; + if (nv_mclass(disp) < NVD0_DISP_CLASS) + outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED; + if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) { + outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT; + outp->dpcd[2] |= outp->base.info.dpconf.link_nr; + } + if (outp->dpcd[1] > outp->base.info.dpconf.link_bw) + outp->dpcd[1] = outp->base.info.dpconf.link_bw; + dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; + + /* restrict link config to the lowest required rate, if requested */ + if (datarate) { + datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */ + while (cfg[1].rate >= datarate) + cfg++; } - if (dp->dpcd[1] > dp->outp->dpconf.link_bw) - dp->dpcd[1] = dp->outp->dpconf.link_bw; + cfg--; - /* adjust required bandwidth for 8B/10B coding overhead */ - datarate = (datarate / 8) * 10; + /* disable link interrupt handling during link training */ + nouveau_event_put(outp->irq); /* enable down-spreading and execute pre-train script from vbios */ - dp_link_train_init(dp, dp->dpcd[3] & 0x01); + dp_link_train_init(dp, outp->dpcd[3] & 0x01); - /* start off at highest link rate supported by encoder and display */ - while (*link_bw > (dp->dpcd[1] * 27000)) - link_bw++; - - while ((ret = -EIO) && link_bw[0]) { - /* find minimum required lane count at this link rate */ - dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT; - while ((dp->link_nr >> 1) * link_bw[0] > datarate) - dp->link_nr >>= 1; - - /* drop link rate to minimum with this lane count */ - while ((link_bw[1] * dp->link_nr) > datarate) - link_bw++; - dp->link_bw = link_bw[0]; + while (ret = -EIO, (++cfg)->rate) { + /* select next configuration supported by encoder and sink */ + while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) || + cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE])) + cfg++; + dp->link_bw = cfg->bw * 27000; + dp->link_nr = cfg->nr; /* program selected link configuration */ ret = dp_set_link_config(dp); @@ -364,17 +383,18 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func, */ break; } - - /* retry at lower rate */ - link_bw++; } - /* finish link training */ + /* finish link training and execute post-train script from vbios */ dp_set_training_pattern(dp, 0); if (ret < 0) ERR("link training failed\n"); - /* execute post-train script from vbios */ dp_link_train_fini(dp); - return (ret < 0) ? false : true; + + /* signal completion and enable link interrupt handling */ + DBG("training complete\n"); + atomic_set(&outp->lt.done, 1); + wake_up(&outp->lt.wait); + nouveau_event_get(outp->irq); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h index 43281c8e9e7b..5628d2d5ec71 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h @@ -13,8 +13,7 @@ #define DPCD_RC0E_AUX_RD_INTERVAL 0x0000e /* DPCD Link Configuration */ -#define DPCD_LC00 0x00100 -#define DPCD_LC00_LINK_BW_SET 0xff +#define DPCD_LC00_LINK_BW_SET 0x00100 #define DPCD_LC01 0x00101 #define DPCD_LC01_ENHANCED_FRAME_EN 0x80 #define DPCD_LC01_LANE_COUNT_SET 0x1f @@ -25,6 +24,16 @@ #define DPCD_LC03_PRE_EMPHASIS_SET 0x18 #define DPCD_LC03_MAX_SWING_REACHED 0x04 #define DPCD_LC03_VOLTAGE_SWING_SET 0x03 +#define DPCD_LC0F 0x0010f +#define DPCD_LC0F_LANE1_MAX_POST_CURSOR2_REACHED 0x40 +#define DPCD_LC0F_LANE1_POST_CURSOR2_SET 0x30 +#define DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED 0x04 +#define DPCD_LC0F_LANE0_POST_CURSOR2_SET 0x03 +#define DPCD_LC10 0x00110 +#define DPCD_LC10_LANE3_MAX_POST_CURSOR2_REACHED 0x40 +#define DPCD_LC10_LANE3_POST_CURSOR2_SET 0x30 +#define DPCD_LC10_LANE2_MAX_POST_CURSOR2_REACHED 0x04 +#define DPCD_LC10_LANE2_POST_CURSOR2_SET 0x03 /* DPCD Link/Sink Status */ #define DPCD_LS02 0x00202 @@ -55,24 +64,12 @@ #define DPCD_LS07_LANE3_VOLTAGE_SWING 0x30 #define DPCD_LS07_LANE2_PRE_EMPHASIS 0x0c #define DPCD_LS07_LANE2_VOLTAGE_SWING 0x03 +#define DPCD_LS0C 0x0020c +#define DPCD_LS0C_LANE3_POST_CURSOR2 0xc0 +#define DPCD_LS0C_LANE2_POST_CURSOR2 0x30 +#define DPCD_LS0C_LANE1_POST_CURSOR2 0x0c +#define DPCD_LS0C_LANE0_POST_CURSOR2 0x03 -struct nouveau_disp; -struct dcb_output; - -struct nouveau_dp_func { - int (*pattern)(struct nouveau_disp *, struct dcb_output *, - int head, int pattern); - int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head, - int link_nr, int link_bw, bool enh_frame); - int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head, - int lane, int swing, int preem); -}; - -extern const struct nouveau_dp_func nv94_sor_dp_func; -extern const struct nouveau_dp_func nvd0_sor_dp_func; -extern const struct nouveau_dp_func nv50_pior_dp_func; - -int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *, - struct dcb_output *, int, u32); +void nouveau_dp_train(struct work_struct *); #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c index cf6f59677b74..9fc7447fec90 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c @@ -81,7 +81,6 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } @@ -94,6 +93,7 @@ gm107_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nve0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nve0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c index 94a2cc69ec2c..a32666ed0c47 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c @@ -86,13 +86,13 @@ nv04_disp_sclass[] = { ******************************************************************************/ static void -nv04_disp_vblank_enable(struct nouveau_event *event, int head) +nv04_disp_vblank_enable(struct nouveau_event *event, int type, int head) { nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001); } static void -nv04_disp_vblank_disable(struct nouveau_event *event, int head) +nv04_disp_vblank_disable(struct nouveau_event *event, int type, int head) { nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000); } @@ -106,12 +106,12 @@ nv04_disp_intr(struct nouveau_subdev *subdev) u32 pvideo; if (crtc0 & 0x00000001) { - nouveau_event_trigger(priv->base.vblank, 0); + nouveau_event_trigger(priv->base.vblank, 1, 0); nv_wr32(priv, 0x600100, 0x00000001); } if (crtc1 & 0x00000001) { - nouveau_event_trigger(priv->base.vblank, 1); + nouveau_event_trigger(priv->base.vblank, 1, 1); nv_wr32(priv, 0x602100, 0x00000001); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 9a0cab9c3adb..1e85f36c705f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -829,13 +829,13 @@ nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, } static void -nv50_disp_base_vblank_enable(struct nouveau_event *event, int head) +nv50_disp_base_vblank_enable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x61002c, (4 << head), (4 << head)); } static void -nv50_disp_base_vblank_disable(struct nouveau_event *event, int head) +nv50_disp_base_vblank_disable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x61002c, (4 << head), 0); } @@ -1114,19 +1114,20 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv, int chid) nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000); } -static u16 -exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, - struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, +static struct nvkm_output * +exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl, + u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) { struct nouveau_bios *bios = nouveau_bios(priv); - u16 mask, type, data; + struct nvkm_output *outp; + u16 mask, type; - if (outp < 4) { + if (or < 4) { type = DCB_OUTPUT_ANALOG; mask = 0; } else - if (outp < 8) { + if (or < 8) { switch (ctrl & 0x00000f00) { case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; @@ -1136,45 +1137,48 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; default: nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); - return 0x0000; + return NULL; } - outp -= 4; + or -= 4; } else { - outp = outp - 8; + or = or - 8; type = 0x0010; mask = 0; switch (ctrl & 0x00000f00) { - case 0x00000000: type |= priv->pior.type[outp]; break; + case 0x00000000: type |= priv->pior.type[or]; break; default: nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl); - return 0x0000; + return NULL; } } mask = 0x00c0 & (mask << 6); - mask |= 0x0001 << outp; + mask |= 0x0001 << or; mask |= 0x0100 << head; - data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); - if (!data) - return 0x0000; - - /* off-chip encoders require matching the exact encoder type */ - if (dcb->location != 0) - type |= dcb->extdev << 8; + list_for_each_entry(outp, &priv->base.outp, head) { + if ((outp->info.hasht & 0xff) == type && + (outp->info.hashm & mask) == mask) { + *data = nvbios_outp_match(bios, outp->info.hasht, + outp->info.hashm, + ver, hdr, cnt, len, info); + if (!*data) + return NULL; + return outp; + } + } - return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); + return NULL; } -static bool +static struct nvkm_output * exec_script(struct nv50_disp_priv *priv, int head, int id) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info; - struct dcb_output dcb; u8 ver, hdr, cnt, len; - u16 data; - u32 ctrl = 0x00000000; + u32 data, ctrl = 0; u32 reg; int i; @@ -1204,36 +1208,35 @@ exec_script(struct nv50_disp_priv *priv, int head, int id) } if (!(ctrl & (1 << head))) - return false; + return NULL; i--; - data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); - if (data) { + outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info); + if (outp) { struct nvbios_init init = { .subdev = nv_subdev(priv), .bios = bios, .offset = info.script[id], - .outp = &dcb, + .outp = &outp->info, .crtc = head, .execute = 1, }; - return nvbios_exec(&init) == 0; + nvbios_exec(&init); } - return false; + return outp; } -static u32 -exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, - struct dcb_output *outp) +static struct nvkm_output * +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info1; struct nvbios_ocfg info2; u8 ver, hdr, cnt, len; - u32 ctrl = 0x00000000; - u32 data, conf = ~0; + u32 data, ctrl = 0; u32 reg; int i; @@ -1263,37 +1266,37 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, } if (!(ctrl & (1 << head))) - return conf; + return NULL; i--; - data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1); + outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); if (!data) - return conf; + return NULL; - if (outp->location == 0) { - switch (outp->type) { + if (outp->info.location == 0) { + switch (outp->info.type) { case DCB_OUTPUT_TMDS: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; if (pclk >= 165000) - conf |= 0x0100; + *conf |= 0x0100; break; case DCB_OUTPUT_LVDS: - conf = priv->sor.lvdsconf; + *conf = priv->sor.lvdsconf; break; case DCB_OUTPUT_DP: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; break; case DCB_OUTPUT_ANALOG: default: - conf = 0x00ff; + *conf = 0x00ff; break; } } else { - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; pclk = pclk / 2; } - data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); + data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2); if (data && id < 0xff) { data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); if (data) { @@ -1301,7 +1304,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, .subdev = nv_subdev(priv), .bios = bios, .offset = data, - .outp = outp, + .outp = &outp->info, .crtc = head, .execute = 1, }; @@ -1310,7 +1313,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, } } - return conf; + return outp; } static void @@ -1322,7 +1325,35 @@ nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head) static void nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head) { - exec_script(priv, head, 2); + struct nvkm_output *outp = exec_script(priv, head, 2); + + /* the binary driver does this outside of the supervisor handling + * (after the third supervisor from a detach). we (currently?) + * allow both detach/attach to happen in the same set of + * supervisor interrupts, so it would make sense to execute this + * (full power down?) script after all the detach phases of the + * supervisor handling. like with training if needed from the + * second supervisor, nvidia doesn't do this, so who knows if it's + * entirely safe, but it does appear to work.. + * + * without this script being run, on some configurations i've + * seen, switching from DP to TMDS on a DP connector may result + * in a blank screen (SOR_PWR off/on can restore it) + */ + if (outp && outp->info.type == DCB_OUTPUT_DP) { + struct nvkm_output_dp *outpdp = (void *)outp; + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = nouveau_bios(priv), + .outp = &outp->info, + .crtc = head, + .offset = outpdp->info.script[4], + .execute = 1, + }; + + nvbios_exec(&init); + atomic_set(&outpdp->lt.done, 0); + } } static void @@ -1444,56 +1475,83 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, static void nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; + struct nvkm_output *outp; u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; u32 hval, hreg = 0x614200 + (head * 0x800); u32 oval, oreg; - u32 mask; - u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); - if (conf != ~0) { - if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) { - u32 soff = (ffs(outp.or) - 1) * 0x08; - u32 ctrl = nv_rd32(priv, 0x610794 + soff); - u32 datarate; - - switch ((ctrl & 0x000f0000) >> 16) { - case 6: datarate = pclk * 30 / 8; break; - case 5: datarate = pclk * 24 / 8; break; - case 2: - default: - datarate = pclk * 18 / 8; - break; - } + u32 mask, conf; - nouveau_dp_train(&priv->base, priv->sor.dp, - &outp, head, datarate); - } + outp = exec_clkcmp(priv, head, 0xff, pclk, &conf); + if (!outp) + return; + + /* we allow both encoder attach and detach operations to occur + * within a single supervisor (ie. modeset) sequence. the + * encoder detach scripts quite often switch off power to the + * lanes, which requires the link to be re-trained. + * + * this is not generally an issue as the sink "must" (heh) + * signal an irq when it's lost sync so the driver can + * re-train. + * + * however, on some boards, if one does not configure at least + * the gpu side of the link *before* attaching, then various + * things can go horribly wrong (PDISP disappearing from mmio, + * third supervisor never happens, etc). + * + * the solution is simply to retrain here, if necessary. last + * i checked, the binary driver userspace does not appear to + * trigger this situation (it forces an UPDATE between steps). + */ + if (outp->info.type == DCB_OUTPUT_DP) { + u32 soff = (ffs(outp->info.or) - 1) * 0x08; + u32 ctrl, datarate; - exec_clkcmp(priv, head, 0, pclk, &outp); - - if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) { - oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800; - oval = 0x00000000; - hval = 0x00000000; - mask = 0xffffffff; - } else - if (!outp.location) { - if (outp.type == DCB_OUTPUT_DP) - nv50_disp_intr_unk20_2_dp(priv, &outp, pclk); - oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800; - oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; - hval = 0x00000000; - mask = 0x00000707; + if (outp->info.location == 0) { + ctrl = nv_rd32(priv, 0x610794 + soff); + soff = 1; } else { - oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800; - oval = 0x00000001; - hval = 0x00000001; - mask = 0x00000707; + ctrl = nv_rd32(priv, 0x610b80 + soff); + soff = 2; } - nv_mask(priv, hreg, 0x0000000f, hval); - nv_mask(priv, oreg, mask, oval); + switch ((ctrl & 0x000f0000) >> 16) { + case 6: datarate = pclk * 30 / 8; break; + case 5: datarate = pclk * 24 / 8; break; + case 2: + default: + datarate = pclk * 18 / 8; + break; + } + + if (nvkm_output_dp_train(outp, datarate / soff, true)) + ERR("link not trained before attach\n"); } + + exec_clkcmp(priv, head, 0, pclk, &conf); + + if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) { + oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800; + oval = 0x00000000; + hval = 0x00000000; + mask = 0xffffffff; + } else + if (!outp->info.location) { + if (outp->info.type == DCB_OUTPUT_DP) + nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk); + oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; + oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; + hval = 0x00000000; + mask = 0x00000707; + } else { + oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800; + oval = 0x00000001; + hval = 0x00000001; + mask = 0x00000707; + } + + nv_mask(priv, hreg, 0x0000000f, hval); + nv_mask(priv, oreg, mask, oval); } /* If programming a TMDS output on a SOR that can also be configured for @@ -1521,30 +1579,16 @@ nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp static void nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; + struct nvkm_output *outp; u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; - if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) { - if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS) - nv50_disp_intr_unk40_0_tmds(priv, &outp); - else - if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) { - u32 soff = (ffs(outp.or) - 1) * 0x08; - u32 ctrl = nv_rd32(priv, 0x610b84 + soff); - u32 datarate; - - switch ((ctrl & 0x000f0000) >> 16) { - case 6: datarate = pclk * 30 / 8; break; - case 5: datarate = pclk * 24 / 8; break; - case 2: - default: - datarate = pclk * 18 / 8; - break; - } + u32 conf; - nouveau_dp_train(&priv->base, priv->pior.dp, - &outp, head, datarate); - } - } + outp = exec_clkcmp(priv, head, 1, pclk, &conf); + if (!outp) + return; + + if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS) + nv50_disp_intr_unk40_0_tmds(priv, &outp->info); } void @@ -1610,13 +1654,13 @@ nv50_disp_intr(struct nouveau_subdev *subdev) } if (intr1 & 0x00000004) { - nouveau_event_trigger(priv->base.vblank, 0); + nouveau_event_trigger(priv->base.vblank, 1, 0); nv_wr32(priv, 0x610024, 0x00000004); intr1 &= ~0x00000004; } if (intr1 & 0x00000008) { - nouveau_event_trigger(priv->base.vblank, 1); + nouveau_event_trigger(priv->base.vblank, 1, 1); nv_wr32(priv, 0x610024, 0x00000008); intr1 &= ~0x00000008; } @@ -1656,11 +1700,16 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } struct nouveau_oclass * +nv50_disp_outp_sclass[] = { + &nv50_pior_dp_impl.base.base, + NULL +}; + +struct nouveau_oclass * nv50_disp_oclass = &(struct nv50_disp_impl) { .base.base.handle = NV_ENGINE(DISP, 0x50), .base.base.ofuncs = &(struct nouveau_ofuncs) { @@ -1669,6 +1718,7 @@ nv50_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv50_disp_outp_sclass, .mthd.core = &nv50_disp_mast_mthd_chan, .mthd.base = &nv50_disp_sync_mthd_chan, .mthd.ovly = &nv50_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h index 48d59db47f0d..1a886472b6f5 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -11,6 +11,8 @@ #include "dport.h" #include "priv.h" +#include "outp.h" +#include "outpdp.h" struct nv50_disp_impl { struct nouveau_disp_impl base; @@ -43,13 +45,11 @@ struct nv50_disp_priv { int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32); int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32); u32 lvdsconf; - const struct nouveau_dp_func *dp; } sor; struct { int nr; int (*power)(struct nv50_disp_priv *, int ext, u32 data); u8 type[3]; - const struct nouveau_dp_func *dp; } pior; }; @@ -199,4 +199,14 @@ void nvd0_disp_intr(struct nouveau_subdev *); extern const struct nv50_disp_mthd_chan nve0_disp_mast_mthd_chan; extern const struct nv50_disp_mthd_chan nve0_disp_ovly_mthd_chan; +extern struct nvkm_output_dp_impl nv50_pior_dp_impl; +extern struct nouveau_oclass *nv50_disp_outp_sclass[]; + +extern struct nvkm_output_dp_impl nv94_sor_dp_impl; +int nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int); +extern struct nouveau_oclass *nv94_disp_outp_sclass[]; + +extern struct nvkm_output_dp_impl nvd0_sor_dp_impl; +extern struct nouveau_oclass *nvd0_disp_outp_sclass[]; + #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c index 98c5b19bc2b0..1cc62e434683 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -264,7 +264,6 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } @@ -277,6 +276,7 @@ nv84_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv50_disp_outp_sclass, .mthd.core = &nv84_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nv84_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c index 6844061c7e04..4f718a9f5aef 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -77,6 +77,7 @@ nv94_disp_base_omthds[] = { { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, @@ -122,13 +123,18 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; - priv->sor.dp = &nv94_sor_dp_func; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } struct nouveau_oclass * +nv94_disp_outp_sclass[] = { + &nv50_pior_dp_impl.base.base, + &nv94_sor_dp_impl.base.base, + NULL +}; + +struct nouveau_oclass * nv94_disp_oclass = &(struct nv50_disp_impl) { .base.base.handle = NV_ENGINE(DISP, 0x88), .base.base.ofuncs = &(struct nouveau_ofuncs) { @@ -137,6 +143,7 @@ nv94_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv94_disp_outp_sclass, .mthd.core = &nv94_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nv84_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c index 88c96241c02a..6237a9a36f70 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c @@ -126,7 +126,6 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hdmi = nv84_hdmi_ctrl; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } @@ -139,6 +138,7 @@ nva0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv50_disp_outp_sclass, .mthd.core = &nv84_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nva0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c index 46cb2ce0e82a..019124d4782b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -50,6 +50,7 @@ nva3_disp_base_omthds[] = { { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, @@ -96,9 +97,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nva3_hda_eld; priv->sor.hdmi = nva3_hdmi_ctrl; - priv->sor.dp = &nv94_sor_dp_func; priv->pior.power = nv50_pior_power; - priv->pior.dp = &nv50_pior_dp_func; return 0; } @@ -111,6 +110,7 @@ nva3_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nv94_disp_outp_sclass, .mthd.core = &nv94_disp_mast_mthd_chan, .mthd.base = &nv84_disp_sync_mthd_chan, .mthd.ovly = &nv84_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index 876de9ac3793..48aa38a87e3f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -748,13 +748,13 @@ nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, } static void -nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head) +nvd0_disp_base_vblank_enable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); } static void -nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head) +nvd0_disp_base_vblank_disable(struct nouveau_event *event, int type, int head) { nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); } @@ -887,6 +887,7 @@ nvd0_disp_base_omthds[] = { { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, @@ -915,19 +916,20 @@ nvd0_disp_sclass[] = { * Display engine implementation ******************************************************************************/ -static u16 -exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, - struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, +static struct nvkm_output * +exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl, + u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info) { struct nouveau_bios *bios = nouveau_bios(priv); - u16 mask, type, data; + struct nvkm_output *outp; + u16 mask, type; - if (outp < 4) { + if (or < 4) { type = DCB_OUTPUT_ANALOG; mask = 0; } else { - outp -= 4; + or -= 4; switch (ctrl & 0x00000f00) { case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; @@ -939,101 +941,106 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); return 0x0000; } - dcb->sorconf.link = mask; } mask = 0x00c0 & (mask << 6); - mask |= 0x0001 << outp; + mask |= 0x0001 << or; mask |= 0x0100 << head; - data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); - if (!data) - return 0x0000; + list_for_each_entry(outp, &priv->base.outp, head) { + if ((outp->info.hasht & 0xff) == type && + (outp->info.hashm & mask) == mask) { + *data = nvbios_outp_match(bios, outp->info.hasht, + outp->info.hashm, + ver, hdr, cnt, len, info); + if (!*data) + return NULL; + return outp; + } + } - return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); + return NULL; } -static bool +static struct nvkm_output * exec_script(struct nv50_disp_priv *priv, int head, int id) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info; - struct dcb_output dcb; u8 ver, hdr, cnt, len; - u32 ctrl = 0x00000000; - u16 data; - int outp; + u32 data, ctrl = 0; + int or; - for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { - ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20)); + for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { + ctrl = nv_rd32(priv, 0x640180 + (or * 0x20)); if (ctrl & (1 << head)) break; } - if (outp == 8) - return false; + if (or == 8) + return NULL; - data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); - if (data) { + outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info); + if (outp) { struct nvbios_init init = { .subdev = nv_subdev(priv), .bios = bios, .offset = info.script[id], - .outp = &dcb, + .outp = &outp->info, .crtc = head, .execute = 1, }; - return nvbios_exec(&init) == 0; + nvbios_exec(&init); } - return false; + return outp; } -static u32 -exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, - u32 pclk, struct dcb_output *dcb) +static struct nvkm_output * +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf) { struct nouveau_bios *bios = nouveau_bios(priv); + struct nvkm_output *outp; struct nvbios_outp info1; struct nvbios_ocfg info2; u8 ver, hdr, cnt, len; - u32 ctrl = 0x00000000; - u32 data, conf = ~0; - int outp; + u32 data, ctrl = 0; + int or; - for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { - ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20)); + for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { + ctrl = nv_rd32(priv, 0x660180 + (or * 0x20)); if (ctrl & (1 << head)) break; } - if (outp == 8) - return conf; + if (or == 8) + return NULL; - data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1); - if (data == 0x0000) - return conf; + outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); + if (!outp) + return NULL; - switch (dcb->type) { + switch (outp->info.type) { case DCB_OUTPUT_TMDS: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; if (pclk >= 165000) - conf |= 0x0100; + *conf |= 0x0100; break; case DCB_OUTPUT_LVDS: - conf = priv->sor.lvdsconf; + *conf = priv->sor.lvdsconf; break; case DCB_OUTPUT_DP: - conf = (ctrl & 0x00000f00) >> 8; + *conf = (ctrl & 0x00000f00) >> 8; break; case DCB_OUTPUT_ANALOG: default: - conf = 0x00ff; + *conf = 0x00ff; break; } - data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); + data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2); if (data && id < 0xff) { data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); if (data) { @@ -1041,7 +1048,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, .subdev = nv_subdev(priv), .bios = bios, .offset = data, - .outp = dcb, + .outp = &outp->info, .crtc = head, .execute = 1, }; @@ -1050,7 +1057,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, } } - return conf; + return outp; } static void @@ -1062,7 +1069,23 @@ nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head) static void nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head) { - exec_script(priv, head, 2); + struct nvkm_output *outp = exec_script(priv, head, 2); + + /* see note in nv50_disp_intr_unk20_0() */ + if (outp && outp->info.type == DCB_OUTPUT_DP) { + struct nvkm_output_dp *outpdp = (void *)outp; + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = nouveau_bios(priv), + .outp = &outp->info, + .crtc = head, + .offset = outpdp->info.script[4], + .execute = 1, + }; + + nvbios_exec(&init); + atomic_set(&outpdp->lt.done, 0); + } } static void @@ -1124,49 +1147,52 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, static void nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; + struct nvkm_output *outp; u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; - u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); - if (conf != ~0) { - u32 addr, data; - - if (outp.type == DCB_OUTPUT_DP) { - u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300)); - switch ((sync & 0x000003c0) >> 6) { - case 6: pclk = pclk * 30 / 8; break; - case 5: pclk = pclk * 24 / 8; break; - case 2: - default: - pclk = pclk * 18 / 8; - break; - } - - nouveau_dp_train(&priv->base, priv->sor.dp, - &outp, head, pclk); + u32 conf, addr, data; + + outp = exec_clkcmp(priv, head, 0xff, pclk, &conf); + if (!outp) + return; + + /* see note in nv50_disp_intr_unk20_2() */ + if (outp->info.type == DCB_OUTPUT_DP) { + u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300)); + switch ((sync & 0x000003c0) >> 6) { + case 6: pclk = pclk * 30 / 8; break; + case 5: pclk = pclk * 24 / 8; break; + case 2: + default: + pclk = pclk * 18 / 8; + break; } - exec_clkcmp(priv, head, 0, pclk, &outp); + if (nvkm_output_dp_train(outp, pclk, true)) + ERR("link not trained before attach\n"); + } - if (outp.type == DCB_OUTPUT_ANALOG) { - addr = 0x612280 + (ffs(outp.or) - 1) * 0x800; - data = 0x00000000; - } else { - if (outp.type == DCB_OUTPUT_DP) - nvd0_disp_intr_unk2_2_tu(priv, head, &outp); - addr = 0x612300 + (ffs(outp.or) - 1) * 0x800; - data = (conf & 0x0100) ? 0x00000101 : 0x00000000; - } + exec_clkcmp(priv, head, 0, pclk, &conf); - nv_mask(priv, addr, 0x00000707, data); + if (outp->info.type == DCB_OUTPUT_ANALOG) { + addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800; + data = 0x00000000; + } else { + if (outp->info.type == DCB_OUTPUT_DP) + nvd0_disp_intr_unk2_2_tu(priv, head, &outp->info); + addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800; + data = (conf & 0x0100) ? 0x00000101 : 0x00000000; } + + nv_mask(priv, addr, 0x00000707, data); } static void nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head) { - struct dcb_output outp; u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; - exec_clkcmp(priv, head, 1, pclk, &outp); + u32 conf; + + exec_clkcmp(priv, head, 1, pclk, &conf); } void @@ -1240,7 +1266,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid) chid, (mthd & 0x0000ffc), data, mthd, unkn); if (chid == 0) { - switch (mthd) { + switch (mthd & 0xffc) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0, impl->mthd.core); @@ -1250,7 +1276,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid) } } else if (chid <= 4) { - switch (mthd) { + switch (mthd & 0xffc) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1, impl->mthd.base); @@ -1260,7 +1286,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid) } } else if (chid <= 8) { - switch (mthd) { + switch (mthd & 0xffc) { case 0x0080: nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 5, impl->mthd.ovly); @@ -1317,7 +1343,7 @@ nvd0_disp_intr(struct nouveau_subdev *subdev) if (mask & intr) { u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800)); if (stat & 0x00000001) - nouveau_event_trigger(priv->base.vblank, i); + nouveau_event_trigger(priv->base.vblank, 1, i); nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0); nv_rd32(priv, 0x6100c0 + (i * 0x800)); } @@ -1352,11 +1378,16 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } struct nouveau_oclass * +nvd0_disp_outp_sclass[] = { + &nvd0_sor_dp_impl.base.base, + NULL +}; + +struct nouveau_oclass * nvd0_disp_oclass = &(struct nv50_disp_impl) { .base.base.handle = NV_ENGINE(DISP, 0x90), .base.base.ofuncs = &(struct nouveau_ofuncs) { @@ -1365,6 +1396,7 @@ nvd0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nvd0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nvd0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c index 44e0b8f34c1a..11328e3f5df1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c @@ -246,7 +246,6 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } @@ -259,6 +258,7 @@ nve0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nve0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nve0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c index 482585d375fa..104388081d73 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c @@ -81,7 +81,6 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hdmi = nvd0_hdmi_ctrl; - priv->sor.dp = &nvd0_sor_dp_func; return 0; } @@ -94,6 +93,7 @@ nvf0_disp_oclass = &(struct nv50_disp_impl) { .init = _nouveau_disp_init, .fini = _nouveau_disp_fini, }, + .base.outp = nvd0_disp_outp_sclass, .mthd.core = &nve0_disp_mast_mthd_chan, .mthd.base = &nvd0_disp_sync_mthd_chan, .mthd.ovly = &nve0_disp_ovly_mthd_chan, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c new file mode 100644 index 000000000000..ad9ba7ccec7f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c @@ -0,0 +1,137 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/i2c.h> +#include <subdev/bios.h> +#include <subdev/bios/conn.h> + +#include "outp.h" + +int +_nvkm_output_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_output *outp = (void *)object; + nv_ofuncs(outp->conn)->fini(nv_object(outp->conn), suspend); + return nouveau_object_fini(&outp->base, suspend); +} + +int +_nvkm_output_init(struct nouveau_object *object) +{ + struct nvkm_output *outp = (void *)object; + int ret = nouveau_object_init(&outp->base); + if (ret == 0) + nv_ofuncs(outp->conn)->init(nv_object(outp->conn)); + return 0; +} + +void +_nvkm_output_dtor(struct nouveau_object *object) +{ + struct nvkm_output *outp = (void *)object; + list_del(&outp->head); + nouveau_object_ref(NULL, (void *)&outp->conn); + nouveau_object_destroy(&outp->base); +} + +int +nvkm_output_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + struct dcb_output *dcbE, int index, + int length, void **pobject) +{ + struct nouveau_bios *bios = nouveau_bios(engine); + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nouveau_disp *disp = (void *)engine; + struct nvbios_connE connE; + struct nvkm_output *outp; + u8 ver, hdr; + u32 data; + int ret; + + ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject); + outp = *pobject; + if (ret) + return ret; + + outp->info = *dcbE; + outp->index = index; + + DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n", + dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ? + dcbE->sorconf.link : 0, dcbE->connector, dcbE->i2c_index, + dcbE->bus, dcbE->heads); + + outp->port = i2c->find(i2c, outp->info.i2c_index); + outp->edid = outp->port; + + data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, &connE); + if (!data) { + DBG("vbios connector data not found\n"); + memset(&connE, 0x00, sizeof(connE)); + connE.type = DCB_CONNECTOR_NONE; + } + + ret = nouveau_object_ctor(parent, engine, nvkm_connector_oclass, + &connE, outp->info.connector, + (struct nouveau_object **)&outp->conn); + if (ret < 0) { + ERR("error %d creating connector, disabling\n", ret); + return ret; + } + + list_add_tail(&outp->head, &disp->outp); + return 0; +} + +int +_nvkm_output_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *dcbE, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_output *outp; + int ret; + + ret = nvkm_output_create(parent, engine, oclass, dcbE, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass * +nvkm_output_oclass = &(struct nvkm_output_impl) { + .base = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_output_ctor, + .dtor = _nvkm_output_dtor, + .init = _nvkm_output_init, + .fini = _nvkm_output_fini, + }, + }, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h new file mode 100644 index 000000000000..bc76fbf85710 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h @@ -0,0 +1,59 @@ +#ifndef __NVKM_DISP_OUTP_H__ +#define __NVKM_DISP_OUTP_H__ + +#include "priv.h" + +struct nvkm_output { + struct nouveau_object base; + struct list_head head; + + struct dcb_output info; + int index; + + struct nouveau_i2c_port *port; + struct nouveau_i2c_port *edid; + + struct nvkm_connector *conn; +}; + +#define nvkm_output_create(p,e,c,b,i,d) \ + nvkm_output_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_output_destroy(d) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_dtor(nv_object(_outp)); \ +}) +#define nvkm_output_init(d) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_init(nv_object(_outp)); \ +}) +#define nvkm_output_fini(d,s) ({ \ + struct nvkm_output *_outp = (d); \ + _nvkm_output_fini(nv_object(_outp), (s)); \ +}) + +int nvkm_output_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct dcb_output *, + int, int, void **); + +int _nvkm_output_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nvkm_output_dtor(struct nouveau_object *); +int _nvkm_output_init(struct nouveau_object *); +int _nvkm_output_fini(struct nouveau_object *, bool); + +struct nvkm_output_impl { + struct nouveau_oclass base; +}; + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_output *_outp = (void *)outp; \ + nv_##l(nv_object(outp)->engine, "%02x:%04x:%04x: "f, _outp->index, \ + _outp->info.hasht, _outp->info.hashm, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c new file mode 100644 index 000000000000..52c299c3d300 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c @@ -0,0 +1,276 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include <subdev/i2c.h> + +#include "outpdp.h" +#include "conn.h" +#include "dport.h" + +int +nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait) +{ + struct nvkm_output_dp *outp = (void *)base; + bool retrain = true; + u8 link[2], stat[3]; + u32 rate; + int ret, i; + + /* check that the link is trained at a high enough rate */ + ret = nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, link, 2); + if (ret) { + DBG("failed to read link config, assuming no sink\n"); + goto done; + } + + rate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET); + if (rate < ((datarate / 8) * 10)) { + DBG("link not trained at sufficient rate\n"); + goto done; + } + + /* check that link is still trained */ + ret = nv_rdaux(outp->base.edid, DPCD_LS02, stat, 3); + if (ret) { + DBG("failed to read link status, assuming no sink\n"); + goto done; + } + + if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { + for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) { + u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; + if (!(lane & DPCD_LS02_LANE0_CR_DONE) || + !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || + !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { + DBG("lane %d not equalised\n", lane); + goto done; + } + } + retrain = false; + } else { + DBG("no inter-lane alignment\n"); + } + +done: + if (retrain || !atomic_read(&outp->lt.done)) { + /* no sink, but still need to configure source */ + if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) { + outp->dpcd[DPCD_RC01_MAX_LINK_RATE] = + outp->base.info.dpconf.link_bw; + outp->dpcd[DPCD_RC02] = + outp->base.info.dpconf.link_nr; + } + atomic_set(&outp->lt.done, 0); + schedule_work(&outp->lt.work); + } else { + nouveau_event_get(outp->irq); + } + + if (wait) { + if (!wait_event_timeout(outp->lt.wait, + atomic_read(&outp->lt.done), + msecs_to_jiffies(2000))) + ret = -ETIMEDOUT; + } + + return ret; +} + +static void +nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present) +{ + struct nouveau_i2c_port *port = outp->base.edid; + if (present) { + if (!outp->present) { + nouveau_i2c(port)->acquire_pad(port, 0); + DBG("aux power -> always\n"); + outp->present = true; + } + nvkm_output_dp_train(&outp->base, 0, true); + } else { + if (outp->present) { + nouveau_i2c(port)->release_pad(port); + DBG("aux power -> demand\n"); + outp->present = false; + } + atomic_set(&outp->lt.done, 0); + } +} + +static void +nvkm_output_dp_detect(struct nvkm_output_dp *outp) +{ + struct nouveau_i2c_port *port = outp->base.edid; + int ret = nouveau_i2c(port)->acquire_pad(port, 0); + if (ret == 0) { + ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV, + outp->dpcd, sizeof(outp->dpcd)); + nvkm_output_dp_enable(outp, ret == 0); + nouveau_i2c(port)->release_pad(port); + } +} + +static void +nvkm_output_dp_service_work(struct work_struct *work) +{ + struct nvkm_output_dp *outp = container_of(work, typeof(*outp), work); + struct nouveau_disp *disp = nouveau_disp(outp); + int type = atomic_xchg(&outp->pending, 0); + u32 send = 0; + + if (type & (NVKM_I2C_PLUG | NVKM_I2C_UNPLUG)) { + nvkm_output_dp_detect(outp); + if (type & NVKM_I2C_UNPLUG) + send |= NVKM_HPD_UNPLUG; + if (type & NVKM_I2C_PLUG) + send |= NVKM_HPD_PLUG; + nouveau_event_get(outp->base.conn->hpd.event); + } + + if (type & NVKM_I2C_IRQ) { + nvkm_output_dp_train(&outp->base, 0, true); + send |= NVKM_HPD_IRQ; + } + + nouveau_event_trigger(disp->hpd, send, outp->base.info.connector); +} + +static int +nvkm_output_dp_service(void *data, u32 type, int index) +{ + struct nvkm_output_dp *outp = data; + DBG("HPD: %d\n", type); + atomic_or(type, &outp->pending); + schedule_work(&outp->work); + return NVKM_EVENT_DROP; +} + +int +_nvkm_output_dp_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_output_dp *outp = (void *)object; + nouveau_event_put(outp->irq); + nvkm_output_dp_enable(outp, false); + return nvkm_output_fini(&outp->base, suspend); +} + +int +_nvkm_output_dp_init(struct nouveau_object *object) +{ + struct nvkm_output_dp *outp = (void *)object; + nvkm_output_dp_detect(outp); + return nvkm_output_init(&outp->base); +} + +void +_nvkm_output_dp_dtor(struct nouveau_object *object) +{ + struct nvkm_output_dp *outp = (void *)object; + nouveau_event_ref(NULL, &outp->irq); + nvkm_output_destroy(&outp->base); +} + +int +nvkm_output_dp_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, + struct dcb_output *info, int index, + int length, void **pobject) +{ + struct nouveau_bios *bios = nouveau_bios(parent); + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nvkm_output_dp *outp; + u8 hdr, cnt, len; + u32 data; + int ret; + + ret = nvkm_output_create_(parent, engine, oclass, info, index, + length, pobject); + outp = *pobject; + if (ret) + return ret; + + nouveau_event_ref(NULL, &outp->base.conn->hpd.event); + + /* access to the aux channel is not optional... */ + if (!outp->base.edid) { + ERR("aux channel not found\n"); + return -ENODEV; + } + + /* nor is the bios data for this output... */ + data = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, &outp->version, + &hdr, &cnt, &len, &outp->info); + if (!data) { + ERR("no bios dp data\n"); + return -ENODEV; + } + + DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len); + + /* link training */ + INIT_WORK(&outp->lt.work, nouveau_dp_train); + init_waitqueue_head(&outp->lt.wait); + atomic_set(&outp->lt.done, 0); + + /* link maintenance */ + ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_IRQ, outp->base.edid->index, + nvkm_output_dp_service, outp, &outp->irq); + if (ret) { + ERR("error monitoring aux irq event: %d\n", ret); + return ret; + } + + INIT_WORK(&outp->work, nvkm_output_dp_service_work); + + /* hotplug detect, replaces gpio-based mechanism with aux events */ + ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, + outp->base.edid->index, + nvkm_output_dp_service, outp, + &outp->base.conn->hpd.event); + if (ret) { + ERR("error monitoring aux hpd events: %d\n", ret); + return ret; + } + + return 0; +} + +int +_nvkm_output_dp_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_output_dp *outp; + int ret; + + ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h new file mode 100644 index 000000000000..ff33ba12cb67 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h @@ -0,0 +1,65 @@ +#ifndef __NVKM_DISP_OUTP_DP_H__ +#define __NVKM_DISP_OUTP_DP_H__ + +#include <subdev/bios.h> +#include <subdev/bios/dp.h> + +#include "outp.h" + +struct nvkm_output_dp { + struct nvkm_output base; + + struct nvbios_dpout info; + u8 version; + + struct nouveau_eventh *irq; + struct nouveau_eventh *hpd; + struct work_struct work; + atomic_t pending; + bool present; + u8 dpcd[16]; + + struct { + struct work_struct work; + wait_queue_head_t wait; + atomic_t done; + } lt; +}; + +#define nvkm_output_dp_create(p,e,c,b,i,d) \ + nvkm_output_dp_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d) +#define nvkm_output_dp_destroy(d) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_dtor(nv_object(_outp)); \ +}) +#define nvkm_output_dp_init(d) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_init(nv_object(_outp)); \ +}) +#define nvkm_output_dp_fini(d,s) ({ \ + struct nvkm_output_dp *_outp = (d); \ + _nvkm_output_dp_fini(nv_object(_outp), (s)); \ +}) + +int nvkm_output_dp_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct dcb_output *, + int, int, void **); + +int _nvkm_output_dp_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nvkm_output_dp_dtor(struct nouveau_object *); +int _nvkm_output_dp_init(struct nouveau_object *); +int _nvkm_output_dp_fini(struct nouveau_object *, bool); + +struct nvkm_output_dp_impl { + struct nvkm_output_impl base; + int (*pattern)(struct nvkm_output_dp *, int); + int (*lnk_pwr)(struct nvkm_output_dp *, int nr); + int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef); + int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc); +}; + +int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c index 2c8ce351b52d..fe0f256f11bf 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c @@ -33,68 +33,107 @@ #include "nv50.h" /****************************************************************************** - * DisplayPort + * TMDS *****************************************************************************/ -static struct nouveau_i2c_port * -nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp) + +static int +nv50_pior_tmds_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) { - struct nouveau_i2c *i2c = nouveau_i2c(disp); - return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev)); + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nvkm_output *outp; + int ret; + + ret = nvkm_output_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + outp->edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(outp->info.extdev)); + return 0; } +struct nvkm_output_impl +nv50_pior_tmds_impl = { + .base.handle = DCB_OUTPUT_TMDS | 0x0100, + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_pior_tmds_ctor, + .dtor = _nvkm_output_dtor, + .init = _nvkm_output_init, + .fini = _nvkm_output_fini, + }, +}; + +/****************************************************************************** + * DisplayPort + *****************************************************************************/ + static int -nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int pattern) +nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern) { - struct nouveau_i2c_port *port; - int ret = -EINVAL; - - port = nv50_pior_dp_find(disp, outp); - if (port) { - if (port->func->pattern) - ret = port->func->pattern(port, pattern); - else - ret = 0; - } - - return ret; + struct nouveau_i2c_port *port = outp->base.edid; + if (port && port->func->pattern) + return port->func->pattern(port, pattern); + return port ? 0 : -ENODEV; } static int -nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane_nr, int link_bw, bool enh) +nv50_pior_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) { - struct nouveau_i2c_port *port; - int ret = -EINVAL; + return 0; +} - port = nv50_pior_dp_find(disp, outp); +static int +nv50_pior_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) +{ + struct nouveau_i2c_port *port = outp->base.edid; if (port && port->func->lnk_ctl) - ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh); + return port->func->lnk_ctl(port, nr, bw, ef); + return port ? 0 : -ENODEV; +} - return ret; +static int +nv50_pior_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) +{ + struct nouveau_i2c_port *port = outp->base.edid; + if (port && port->func->drv_ctl) + return port->func->drv_ctl(port, ln, vs, pe); + return port ? 0 : -ENODEV; } static int -nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane, int vsw, int pre) +nv50_pior_dp_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *info, u32 index, + struct nouveau_object **pobject) { - struct nouveau_i2c_port *port; - int ret = -EINVAL; - - port = nv50_pior_dp_find(disp, outp); - if (port) { - if (port->func->drv_ctl) - ret = port->func->drv_ctl(port, lane, vsw, pre); - else - ret = 0; - } + struct nouveau_i2c *i2c = nouveau_i2c(parent); + struct nvkm_output_dp *outp; + int ret; - return ret; + ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp); + *pobject = nv_object(outp); + if (ret) + return ret; + + outp->base.edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX( + outp->base.info.extdev)); + return 0; } -const struct nouveau_dp_func -nv50_pior_dp_func = { +struct nvkm_output_dp_impl +nv50_pior_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP | 0x0010, + .base.base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_pior_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, .pattern = nv50_pior_dp_pattern, + .lnk_pwr = nv50_pior_dp_lnk_pwr, .lnk_ctl = nv50_pior_dp_lnk_ctl, .drv_ctl = nv50_pior_dp_drv_ctl, }; @@ -102,6 +141,7 @@ nv50_pior_dp_func = { /****************************************************************************** * General PIOR handling *****************************************************************************/ + int nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data) { diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h index cc3c7a4ca747..26e9a42569c7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h @@ -1,10 +1,42 @@ #ifndef __NVKM_DISP_PRIV_H__ #define __NVKM_DISP_PRIV_H__ +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/conn.h> + #include <engine/disp.h> struct nouveau_disp_impl { struct nouveau_oclass base; + struct nouveau_oclass **outp; + struct nouveau_oclass **conn; }; +#define nouveau_disp_create(p,e,c,h,i,x,d) \ + nouveau_disp_create_((p), (e), (c), (h), (i), (x), \ + sizeof(**d), (void **)d) +#define nouveau_disp_destroy(d) ({ \ + struct nouveau_disp *disp = (d); \ + _nouveau_disp_dtor(nv_object(disp)); \ +}) +#define nouveau_disp_init(d) ({ \ + struct nouveau_disp *disp = (d); \ + _nouveau_disp_init(nv_object(disp)); \ +}) +#define nouveau_disp_fini(d,s) ({ \ + struct nouveau_disp *disp = (d); \ + _nouveau_disp_fini(nv_object(disp), (s)); \ +}) + +int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int heads, + const char *, const char *, int, void **); +void _nouveau_disp_dtor(struct nouveau_object *); +int _nouveau_disp_init(struct nouveau_object *); +int _nouveau_disp_fini(struct nouveau_object *, bool); + +extern struct nouveau_oclass *nvkm_output_oclass; +extern struct nouveau_oclass *nvkm_connector_oclass; + #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c index 526b75242899..e1832778e8b6 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c @@ -47,8 +47,12 @@ int nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) { struct nv50_disp_priv *priv = (void *)object->engine; + const u8 type = (mthd & NV50_DISP_SOR_MTHD_TYPE) >> 12; const u8 head = (mthd & NV50_DISP_SOR_MTHD_HEAD) >> 3; + const u8 link = (mthd & NV50_DISP_SOR_MTHD_LINK) >> 2; const u8 or = (mthd & NV50_DISP_SOR_MTHD_OR); + const u16 mask = (0x0100 << head) | (0x0040 << link) | (0x0001 << or); + struct nvkm_output *outp = NULL, *temp; u32 data; int ret = -EINVAL; @@ -56,6 +60,13 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) return -EINVAL; data = *(u32 *)args; + list_for_each_entry(temp, &priv->base.outp, head) { + if ((temp->info.hasht & 0xff) == type && + (temp->info.hashm & mask) == mask) { + outp = temp; + break; + } + } switch (mthd & ~0x3f) { case NV50_DISP_SOR_PWR: @@ -71,6 +82,23 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID; ret = 0; break; + case NV94_DISP_SOR_DP_PWR: + if (outp) { + struct nvkm_output_dp *outpdp = (void *)outp; + switch (data) { + case NV94_DISP_SOR_DP_PWR_STATE_OFF: + ((struct nvkm_output_dp_impl *)nv_oclass(outp)) + ->lnk_pwr(outpdp, 0); + atomic_set(&outpdp->lt.done, 0); + break; + case NV94_DISP_SOR_DP_PWR_STATE_ON: + nvkm_output_dp_train(&outpdp->base, 0, true); + break; + default: + return -EINVAL; + } + } + break; default: BUG_ON(1); } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c index eea3ef59693d..05487cda84a8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c @@ -29,19 +29,21 @@ #include <subdev/bios/dcb.h> #include <subdev/bios/dp.h> #include <subdev/bios/init.h> +#include <subdev/timer.h> #include "nv50.h" +#include "outpdp.h" static inline u32 -nv94_sor_soff(struct dcb_output *outp) +nv94_sor_soff(struct nvkm_output_dp *outp) { - return (ffs(outp->or) - 1) * 0x800; + return (ffs(outp->base.info.or) - 1) * 0x800; } static inline u32 -nv94_sor_loff(struct dcb_output *outp) +nv94_sor_loff(struct nvkm_output_dp *outp) { - return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; + return nv94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; } static inline u32 @@ -55,77 +57,96 @@ nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) } static int -nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int pattern) +nv94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 loff = nv94_sor_loff(outp); nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24); return 0; } +int +nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) +{ + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); + const u32 soff = nv94_sor_soff(outp); + const u32 loff = nv94_sor_loff(outp); + u32 mask = 0, i; + + for (i = 0; i < nr; i++) + mask |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3); + + nv_mask(priv, 0x61c130 + loff, 0x0000000f, mask); + nv_mask(priv, 0x61c034 + soff, 0x80000000, 0x80000000); + nv_wait(priv, 0x61c034 + soff, 0x80000000, 0x00000000); + return 0; +} + static int -nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int link_nr, int link_bw, bool enh_frame) +nv94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 soff = nv94_sor_soff(outp); const u32 loff = nv94_sor_loff(outp); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - u32 lane = 0; - int i; - dpctrl |= ((1 << link_nr) - 1) << 16; - if (enh_frame) + dpctrl |= ((1 << nr) - 1) << 16; + if (ef) dpctrl |= 0x00004000; - if (link_bw > 0x06) + if (bw > 0x06) clksor |= 0x00040000; - for (i = 0; i < link_nr; i++) - lane |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3); - nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor); nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); - nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane); return 0; } static int -nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane, int swing, int preem) +nv94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) { - struct nouveau_bios *bios = nouveau_bios(disp); - struct nv50_disp_priv *priv = (void *)disp; - const u32 shift = nv94_sor_dp_lane_map(priv, lane); + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 shift = nv94_sor_dp_lane_map(priv, ln); const u32 loff = nv94_sor_loff(outp); u32 addr, data[3]; u8 ver, hdr, cnt, len; struct nvbios_dpout info; struct nvbios_dpcfg ocfg; - addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, + addr = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, &ver, &hdr, &cnt, &len, &info); if (!addr) return -ENODEV; - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, + addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe, &ver, &hdr, &cnt, &len, &ocfg); if (!addr) return -EINVAL; data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift); data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift); - data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00); - nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift)); - nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift)); - nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8)); + data[2] = nv_rd32(priv, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); + nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); + nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); + nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8)); return 0; } -const struct nouveau_dp_func -nv94_sor_dp_func = { +struct nvkm_output_dp_impl +nv94_sor_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP, + .base.base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_output_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, .pattern = nv94_sor_dp_pattern, + .lnk_pwr = nv94_sor_dp_lnk_pwr, .lnk_ctl = nv94_sor_dp_lnk_ctl, .drv_ctl = nv94_sor_dp_drv_ctl, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c index d2df572f16a3..97f0e9cd3d40 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c @@ -29,19 +29,20 @@ #include <subdev/bios/dcb.h> #include <subdev/bios/dp.h> #include <subdev/bios/init.h> +#include <subdev/timer.h> #include "nv50.h" static inline u32 -nvd0_sor_soff(struct dcb_output *outp) +nvd0_sor_soff(struct nvkm_output_dp *outp) { - return (ffs(outp->or) - 1) * 0x800; + return (ffs(outp->base.info.or) - 1) * 0x800; } static inline u32 -nvd0_sor_loff(struct dcb_output *outp) +nvd0_sor_loff(struct nvkm_output_dp *outp) { - return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80; + return nvd0_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; } static inline u32 @@ -52,77 +53,80 @@ nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) } static int -nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int pattern) +nvd0_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 loff = nvd0_sor_loff(outp); nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); return 0; } static int -nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int link_nr, int link_bw, bool enh_frame) +nvd0_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) { - struct nv50_disp_priv *priv = (void *)disp; + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); const u32 soff = nvd0_sor_soff(outp); const u32 loff = nvd0_sor_loff(outp); u32 dpctrl = 0x00000000; u32 clksor = 0x00000000; - u32 lane = 0; - int i; - clksor |= link_bw << 18; - dpctrl |= ((1 << link_nr) - 1) << 16; - if (enh_frame) + clksor |= bw << 18; + dpctrl |= ((1 << nr) - 1) << 16; + if (ef) dpctrl |= 0x00004000; - for (i = 0; i < link_nr; i++) - lane |= 1 << (nvd0_sor_dp_lane_map(priv, i) >> 3); - nv_mask(priv, 0x612300 + soff, 0x007c0000, clksor); nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); - nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane); return 0; } static int -nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp, - int head, int lane, int swing, int preem) +nvd0_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) { - struct nouveau_bios *bios = nouveau_bios(disp); - struct nv50_disp_priv *priv = (void *)disp; - const u32 shift = nvd0_sor_dp_lane_map(priv, lane); + struct nv50_disp_priv *priv = (void *)nouveau_disp(outp); + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 shift = nvd0_sor_dp_lane_map(priv, ln); const u32 loff = nvd0_sor_loff(outp); - u32 addr, data[3]; + u32 addr, data[4]; u8 ver, hdr, cnt, len; struct nvbios_dpout info; struct nvbios_dpcfg ocfg; - addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm, + addr = nvbios_dpout_match(bios, outp->base.info.hasht, + outp->base.info.hashm, &ver, &hdr, &cnt, &len, &info); if (!addr) return -ENODEV; - addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, + addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, &ver, &hdr, &cnt, &len, &ocfg); if (!addr) return -EINVAL; data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift); data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift); - data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00); - nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift)); - nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift)); - nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8)); - nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000); + data[2] = nv_rd32(priv, 0x61c130 + loff); + if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) + data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); + nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); + nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); + nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8)); + data[3] = nv_rd32(priv, 0x61c13c + loff) & ~(0x000000ff << shift); + nv_wr32(priv, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); return 0; } -const struct nouveau_dp_func -nvd0_sor_dp_func = { +struct nvkm_output_dp_impl +nvd0_sor_dp_impl = { + .base.base.handle = DCB_OUTPUT_DP, + .base.base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_output_dp_ctor, + .dtor = _nvkm_output_dp_dtor, + .init = _nvkm_output_dp_init, + .fini = _nvkm_output_dp_fini, + }, .pattern = nvd0_sor_dp_pattern, + .lnk_pwr = nv94_sor_dp_lnk_pwr, .lnk_ctl = nvd0_sor_dp_lnk_ctl, .drv_ctl = nvd0_sor_dp_drv_ctl, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c index 6f9041ced9a2..56ed3d73bf8e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c @@ -91,7 +91,7 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent, if (!chan->user) return -EFAULT; - nouveau_event_trigger(priv->cevent, 0); + nouveau_event_trigger(priv->cevent, 1, 0); chan->size = size; return 0; @@ -194,11 +194,11 @@ nouveau_fifo_create_(struct nouveau_object *parent, if (!priv->channel) return -ENOMEM; - ret = nouveau_event_create(1, &priv->cevent); + ret = nouveau_event_create(1, 1, &priv->cevent); if (ret) return ret; - ret = nouveau_event_create(1, &priv->uevent); + ret = nouveau_event_create(1, 1, &priv->uevent); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c index 54f26cc801c7..c61b16a63884 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c @@ -539,7 +539,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) } if (status & 0x40000000) { - nouveau_event_trigger(priv->base.uevent, 0); + nouveau_event_trigger(priv->base.uevent, 1, 0); nv_wr32(priv, 0x002100, 0x40000000); status &= ~0x40000000; } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c index fe0f41e65d9b..6e5ac16e5460 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c @@ -389,14 +389,14 @@ nv84_fifo_cclass = { ******************************************************************************/ static void -nv84_fifo_uevent_enable(struct nouveau_event *event, int index) +nv84_fifo_uevent_enable(struct nouveau_event *event, int type, int index) { struct nv84_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x40000000, 0x40000000); } static void -nv84_fifo_uevent_disable(struct nouveau_event *event, int index) +nv84_fifo_uevent_disable(struct nouveau_event *event, int type, int index) { struct nv84_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x40000000, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c index fa1e719872b7..ae4a4dc5642a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c @@ -730,7 +730,7 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) for (unkn = 0; unkn < 8; unkn++) { u32 ints = (intr >> (unkn * 0x04)) & inte; if (ints & 0x1) { - nouveau_event_trigger(priv->base.uevent, 0); + nouveau_event_trigger(priv->base.uevent, 1, 0); ints &= ~1; } if (ints) { @@ -827,14 +827,14 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev) } static void -nvc0_fifo_uevent_enable(struct nouveau_event *event, int index) +nvc0_fifo_uevent_enable(struct nouveau_event *event, int type, int index) { struct nvc0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x80000000); } static void -nvc0_fifo_uevent_disable(struct nouveau_event *event, int index) +nvc0_fifo_uevent_disable(struct nouveau_event *event, int type, int index) { struct nvc0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index a9a1a9c9f9f2..298063edb92d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -859,7 +859,7 @@ nve0_fifo_intr_runlist(struct nve0_fifo_priv *priv) static void nve0_fifo_intr_engine(struct nve0_fifo_priv *priv) { - nouveau_event_trigger(priv->base.uevent, 0); + nouveau_event_trigger(priv->base.uevent, 1, 0); } static void @@ -952,14 +952,14 @@ nve0_fifo_intr(struct nouveau_subdev *subdev) } static void -nve0_fifo_uevent_enable(struct nouveau_event *event, int index) +nve0_fifo_uevent_enable(struct nouveau_event *event, int type, int index) { struct nve0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x80000000); } static void -nve0_fifo_uevent_disable(struct nouveau_event *event, int index) +nve0_fifo_uevent_disable(struct nouveau_event *event, int type, int index) { struct nve0_fifo_priv *priv = event->priv; nv_mask(priv, 0x002140, 0x80000000, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c index 5ce686ee729e..f3b4d9dbf23c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c @@ -124,7 +124,7 @@ nv50_software_sclass[] = { ******************************************************************************/ static int -nv50_software_vblsem_release(void *data, int head) +nv50_software_vblsem_release(void *data, u32 type, int head) { struct nv50_software_chan *chan = data; struct nv50_software_priv *priv = (void *)nv_object(chan)->engine; @@ -183,7 +183,7 @@ nv50_software_context_ctor(struct nouveau_object *parent, return -ENOMEM; for (i = 0; i < chan->vblank.nr_event; i++) { - ret = nouveau_event_new(pdisp->vblank, i, pclass->vblank, + ret = nouveau_event_new(pdisp->vblank, 1, i, pclass->vblank, chan, &chan->vblank.event[i]); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h index 2de370c21279..bb49a7a20857 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h @@ -19,7 +19,7 @@ int nv50_software_ctor(struct nouveau_object *, struct nouveau_object *, struct nv50_software_cclass { struct nouveau_oclass base; - int (*vblank)(void *, int); + int (*vblank)(void *, u32, int); }; struct nv50_software_chan { diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c index f9430c1bf3e5..135c20f38356 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c @@ -104,7 +104,7 @@ nvc0_software_sclass[] = { ******************************************************************************/ static int -nvc0_software_vblsem_release(void *data, int head) +nvc0_software_vblsem_release(void *data, u32 type, int head) { struct nv50_software_chan *chan = data; struct nv50_software_priv *priv = (void *)nv_object(chan)->engine; diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h index 9c0cd73462d9..e0c812bc884f 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/class.h +++ b/drivers/gpu/drm/nouveau/core/include/core/class.h @@ -295,6 +295,10 @@ struct nv04_display_scanoutpos { #define NV84_DISP_SOR_HDMI_PWR_REKEY 0x0000007f #define NV50_DISP_SOR_LVDS_SCRIPT 0x00013000 #define NV50_DISP_SOR_LVDS_SCRIPT_ID 0x0000ffff +#define NV94_DISP_SOR_DP_PWR 0x00016000 +#define NV94_DISP_SOR_DP_PWR_STATE 0x00000001 +#define NV94_DISP_SOR_DP_PWR_STATE_OFF 0x00000000 +#define NV94_DISP_SOR_DP_PWR_STATE_ON 0x00000001 #define NV50_DISP_DAC_MTHD 0x00020000 #define NV50_DISP_DAC_MTHD_TYPE 0x0000f000 diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h index 5d539ebff3ed..ba3f1a76a815 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/event.h +++ b/drivers/gpu/drm/nouveau/core/include/core/event.h @@ -12,32 +12,33 @@ struct nouveau_eventh { struct nouveau_event *event; struct list_head head; unsigned long flags; + u32 types; int index; - int (*func)(void *, int); + int (*func)(void *, u32, int); void *priv; }; struct nouveau_event { - spinlock_t list_lock; - spinlock_t refs_lock; - void *priv; - void (*enable)(struct nouveau_event *, int index); - void (*disable)(struct nouveau_event *, int index); + int (*check)(struct nouveau_event *, u32 type, int index); + void (*enable)(struct nouveau_event *, int type, int index); + void (*disable)(struct nouveau_event *, int type, int index); + int types_nr; int index_nr; - struct { - struct list_head list; - int refs; - } index[]; + + spinlock_t list_lock; + struct list_head *list; + spinlock_t refs_lock; + int refs[]; }; -int nouveau_event_create(int index_nr, struct nouveau_event **); +int nouveau_event_create(int types_nr, 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_trigger(struct nouveau_event *, u32 types, int index); -int nouveau_event_new(struct nouveau_event *, int index, - int (*func)(void *, int), void *, +int nouveau_event_new(struct nouveau_event *, u32 types, int index, + int (*func)(void *, u32, int), void *, struct nouveau_eventh **); void nouveau_event_ref(struct nouveau_eventh *, struct nouveau_eventh **); void nouveau_event_get(struct nouveau_eventh *); diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h index fd0c68804de3..fde842896806 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h @@ -6,8 +6,19 @@ #include <core/device.h> #include <core/event.h> +enum nvkm_hpd_event { + NVKM_HPD_PLUG = 1, + NVKM_HPD_UNPLUG = 2, + NVKM_HPD_IRQ = 4, + NVKM_HPD = (NVKM_HPD_PLUG | NVKM_HPD_UNPLUG | NVKM_HPD_IRQ) +}; + struct nouveau_disp { struct nouveau_engine base; + + struct list_head outp; + struct nouveau_event *hpd; + struct nouveau_event *vblank; }; @@ -17,25 +28,6 @@ nouveau_disp(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_DISP]; } -#define nouveau_disp_create(p,e,c,h,i,x,d) \ - nouveau_disp_create_((p), (e), (c), (h), (i), (x), \ - sizeof(**d), (void **)d) -#define nouveau_disp_destroy(d) ({ \ - struct nouveau_disp *disp = (d); \ - _nouveau_disp_dtor(nv_object(disp)); \ -}) -#define nouveau_disp_init(d) \ - nouveau_engine_init(&(d)->base) -#define nouveau_disp_fini(d,s) \ - nouveau_engine_fini(&(d)->base, (s)) - -int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, int heads, - const char *, const char *, int, void **); -void _nouveau_disp_dtor(struct nouveau_object *); -#define _nouveau_disp_init _nouveau_engine_init -#define _nouveau_disp_fini _nouveau_engine_fini - extern struct nouveau_oclass *nv04_disp_oclass; extern struct nouveau_oclass *nv50_disp_oclass; extern struct nouveau_oclass *nv84_disp_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h index a32feb3f3fb6..f3930c27cb7a 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h @@ -22,7 +22,25 @@ enum dcb_connector_type { DCB_CONNECTOR_NONE = 0xff }; -u16 dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); -u16 dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len); +struct nvbios_connT { +}; + +u32 nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_connT *info); + +struct nvbios_connE { + u8 type; + u8 location; + u8 hpd; + u8 dp; + u8 di; + u8 sr; + u8 lcdid; +}; + +u32 nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr); +u32 nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr, + struct nvbios_connE *info); #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h index 6e54218b55fc..728206e21777 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h @@ -17,9 +17,10 @@ u16 nvbios_dpout_match(struct nouveau_bios *, u16 type, u16 mask, struct nvbios_dpout *); struct nvbios_dpcfg { - u8 drv; - u8 pre; - u8 unk; + u8 pc; + u8 dc; + u8 pe; + u8 tx_pu; }; u16 @@ -27,7 +28,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *, u16 outp, u8 idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *); u16 -nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 un, u8 vs, u8 pe, +nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 pc, u8 vs, u8 pe, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h index c85b9f1579ad..612d82ab683d 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h @@ -8,17 +8,18 @@ #include <subdev/bios.h> #include <subdev/bios/gpio.h> +enum nvkm_gpio_event { + NVKM_GPIO_HI = 1, + NVKM_GPIO_LO = 2, + NVKM_GPIO_TOGGLED = (NVKM_GPIO_HI | NVKM_GPIO_LO), +}; + 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); - - /* 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); @@ -31,23 +32,10 @@ nouveau_gpio(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_GPIO]; } -#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) ({ \ - 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 **); -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; -extern struct nouveau_oclass nvd0_gpio_oclass; -extern struct nouveau_oclass nve0_gpio_oclass; +extern struct nouveau_oclass *nv10_gpio_oclass; +extern struct nouveau_oclass *nv50_gpio_oclass; +extern struct nouveau_oclass *nv92_gpio_oclass; +extern struct nouveau_oclass *nvd0_gpio_oclass; +extern struct nouveau_oclass *nve0_gpio_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h index 7f50a858b16f..db1b39d08013 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h @@ -14,52 +14,41 @@ #define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8) #define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8) +enum nvkm_i2c_event { + NVKM_I2C_PLUG = 1, + NVKM_I2C_UNPLUG = 2, + NVKM_I2C_IRQ = 4, + NVKM_I2C_DONE = 8, + NVKM_I2C_ANY = (NVKM_I2C_PLUG | + NVKM_I2C_UNPLUG | + NVKM_I2C_IRQ | + NVKM_I2C_DONE), +}; + struct nouveau_i2c_port { struct nouveau_object base; struct i2c_adapter adapter; + struct mutex mutex; struct list_head head; u8 index; + int aux; const struct nouveau_i2c_func *func; }; struct nouveau_i2c_func { - void (*acquire)(struct nouveau_i2c_port *); - void (*release)(struct nouveau_i2c_port *); - void (*drive_scl)(struct nouveau_i2c_port *, int); void (*drive_sda)(struct nouveau_i2c_port *, int); int (*sense_scl)(struct nouveau_i2c_port *); int (*sense_sda)(struct nouveau_i2c_port *); - int (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8); + int (*aux)(struct nouveau_i2c_port *, bool, u8, u32, u8 *, u8); int (*pattern)(struct nouveau_i2c_port *, int pattern); int (*lnk_ctl)(struct nouveau_i2c_port *, int nr, int bw, bool enh); int (*drv_ctl)(struct nouveau_i2c_port *, int lane, int sw, int pe); }; -#define nouveau_i2c_port_create(p,e,o,i,a,f,d) \ - nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f), \ - sizeof(**d), (void **)d) -#define nouveau_i2c_port_destroy(p) ({ \ - struct nouveau_i2c_port *port = (p); \ - _nouveau_i2c_port_dtor(nv_object(i2c)); \ -}) -#define nouveau_i2c_port_init(p) \ - nouveau_object_init(&(p)->base) -#define nouveau_i2c_port_fini(p,s) \ - nouveau_object_fini(&(p)->base, (s)) - -int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, u8, - const struct i2c_algorithm *, - const struct nouveau_i2c_func *, - int, void **); -void _nouveau_i2c_port_dtor(struct nouveau_object *); -#define _nouveau_i2c_port_init nouveau_object_init -#define _nouveau_i2c_port_fini nouveau_object_fini - struct nouveau_i2c_board_info { struct i2c_board_info dev; u8 udelay; /* set to 0 to use the standard delay */ @@ -67,13 +56,20 @@ struct nouveau_i2c_board_info { struct nouveau_i2c { struct nouveau_subdev base; + struct nouveau_event *ntfy; struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index); struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type); + int (*acquire_pad)(struct nouveau_i2c_port *, unsigned long timeout); + void (*release_pad)(struct nouveau_i2c_port *); + int (*acquire)(struct nouveau_i2c_port *, unsigned long timeout); + void (*release)(struct nouveau_i2c_port *); int (*identify)(struct nouveau_i2c *, int index, const char *what, struct nouveau_i2c_board_info *, bool (*match)(struct nouveau_i2c_port *, struct i2c_board_info *, void *), void *); + + wait_queue_head_t wait; struct list_head ports; }; @@ -83,37 +79,12 @@ nouveau_i2c(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C]; } -#define nouveau_i2c_create(p,e,o,s,d) \ - nouveau_i2c_create_((p), (e), (o), (s), sizeof(**d), (void **)d) -#define nouveau_i2c_destroy(p) ({ \ - struct nouveau_i2c *i2c = (p); \ - _nouveau_i2c_dtor(nv_object(i2c)); \ -}) -#define nouveau_i2c_init(p) ({ \ - struct nouveau_i2c *i2c = (p); \ - _nouveau_i2c_init(nv_object(i2c)); \ -}) -#define nouveau_i2c_fini(p,s) ({ \ - struct nouveau_i2c *i2c = (p); \ - _nouveau_i2c_fini(nv_object(i2c), (s)); \ -}) - -int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, struct nouveau_oclass *, - int, void **); -void _nouveau_i2c_dtor(struct nouveau_object *); -int _nouveau_i2c_init(struct nouveau_object *); -int _nouveau_i2c_fini(struct nouveau_object *, bool); - -extern struct nouveau_oclass nv04_i2c_oclass; -extern struct nouveau_oclass nv4e_i2c_oclass; -extern struct nouveau_oclass nv50_i2c_oclass; -extern struct nouveau_oclass nv94_i2c_oclass; -extern struct nouveau_oclass nvd0_i2c_oclass; -extern struct nouveau_oclass nouveau_anx9805_sclass[]; - -extern const struct i2c_algorithm nouveau_i2c_bit_algo; -extern const struct i2c_algorithm nouveau_i2c_aux_algo; +extern struct nouveau_oclass *nv04_i2c_oclass; +extern struct nouveau_oclass *nv4e_i2c_oclass; +extern struct nouveau_oclass *nv50_i2c_oclass; +extern struct nouveau_oclass *nv94_i2c_oclass; +extern struct nouveau_oclass *nvd0_i2c_oclass; +extern struct nouveau_oclass *nve0_i2c_oclass; static inline int nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg) diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c b/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c index 5ac010efd959..2ede3bcd96a1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c @@ -28,12 +28,12 @@ #include <subdev/bios/dcb.h> #include <subdev/bios/conn.h> -u16 -dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +u32 +nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { - u16 dcb = dcb_table(bios, ver, hdr, cnt, len); + u32 dcb = dcb_table(bios, ver, hdr, cnt, len); if (dcb && *ver >= 0x30 && *hdr >= 0x16) { - u16 data = nv_ro16(bios, dcb + 0x14); + u32 data = nv_ro16(bios, dcb + 0x14); if (data) { *ver = nv_ro08(bios, data + 0); *hdr = nv_ro08(bios, data + 1); @@ -42,15 +42,59 @@ dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) return data; } } - return 0x0000; + return 0x00000000; } -u16 -dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len) +u32 +nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_connT *info) +{ + u32 data = nvbios_connTe(bios, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x30: + case 0x40: + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len) { u8 hdr, cnt; - u16 data = dcb_conntab(bios, ver, &hdr, &cnt, len); + u32 data = nvbios_connTe(bios, ver, &hdr, &cnt, len); if (data && idx < cnt) return data + hdr + (idx * *len); - return 0x0000; + return 0x00000000; +} + +u32 +nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, + struct nvbios_connE *info) +{ + u32 data = nvbios_connEe(bios, idx, ver, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x30: + case 0x40: + info->type = nv_ro08(bios, data + 0x00); + info->location = nv_ro08(bios, data + 0x01) & 0x0f; + info->hpd = (nv_ro08(bios, data + 0x01) & 0x30) >> 4; + info->dp = (nv_ro08(bios, data + 0x01) & 0xc0) >> 6; + if (*len < 4) + return data; + info->hpd |= (nv_ro08(bios, data + 0x02) & 0x03) << 2; + info->dp |= nv_ro08(bios, data + 0x02) & 0x0c; + info->di = (nv_ro08(bios, data + 0x02) & 0xf0) >> 4; + info->hpd |= (nv_ro08(bios, data + 0x03) & 0x07) << 4; + info->sr = (nv_ro08(bios, data + 0x03) & 0x08) >> 3; + info->lcdid = (nv_ro08(bios, data + 0x03) & 0x70) >> 4; + return data; + default: + break; + } + return 0x00000000; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c index 7628fe759220..f309dd657250 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c @@ -162,18 +162,20 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx, struct nvbios_dpcfg *info) { u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); if (data) { switch (*ver) { case 0x21: - info->drv = nv_ro08(bios, data + 0x02); - info->pre = nv_ro08(bios, data + 0x03); - info->unk = nv_ro08(bios, data + 0x04); + info->dc = nv_ro08(bios, data + 0x02); + info->pe = nv_ro08(bios, data + 0x03); + info->tx_pu = nv_ro08(bios, data + 0x04); break; case 0x30: case 0x40: - info->drv = nv_ro08(bios, data + 0x01); - info->pre = nv_ro08(bios, data + 0x02); - info->unk = nv_ro08(bios, data + 0x03); + info->pc = nv_ro08(bios, data + 0x00); + info->dc = nv_ro08(bios, data + 0x01); + info->pe = nv_ro08(bios, data + 0x02); + info->tx_pu = nv_ro08(bios, data + 0x03); break; default: data = 0x0000; @@ -184,7 +186,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx, } u16 -nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe, +nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_dpcfg *info) { @@ -193,16 +195,15 @@ nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe, if (*ver >= 0x30) { const u8 vsoff[] = { 0, 4, 7, 9 }; - idx = (un * 10) + vsoff[vs] + pe; + idx = (pc * 10) + vsoff[vs] + pe; } else { - while ((data = nvbios_dpcfg_entry(bios, outp, idx, + while ((data = nvbios_dpcfg_entry(bios, outp, ++idx, ver, hdr, cnt, len))) { if (nv_ro08(bios, data + 0x00) == vs && nv_ro08(bios, data + 0x01) == pe) break; - idx++; } } - return nvbios_dpcfg_parse(bios, outp, pe, ver, hdr, cnt, len, info); + return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index acaeaf79e3f0..626380f9e4c0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -98,15 +98,16 @@ static u8 init_conn(struct nvbios_init *init) { struct nouveau_bios *bios = init->bios; - u8 ver, len; - u16 conn; + struct nvbios_connE connE; + u8 ver, hdr; + u32 conn; if (init_exec(init)) { if (init->outp) { conn = init->outp->connector; - conn = dcb_conn(bios, conn, &ver, &len); + conn = nvbios_connEp(bios, conn, &ver, &hdr, &connE); if (conn) - return nv_ro08(bios, conn); + return connE.type; } error("script needs connector type\n"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c index f572c2804c32..45e0202f3151 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c @@ -22,21 +22,24 @@ * Authors: Ben Skeggs */ -#include <subdev/gpio.h> #include <subdev/bios.h> #include <subdev/bios/gpio.h> +#include "priv.h" + static int nouveau_gpio_drive(struct nouveau_gpio *gpio, int idx, int line, int dir, int out) { - return gpio->drive ? gpio->drive(gpio, line, dir, out) : -ENODEV; + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + return impl->drive ? impl->drive(gpio, line, dir, out) : -ENODEV; } static int nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line) { - return gpio->sense ? gpio->sense(gpio, line) : -ENODEV; + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + return impl->sense ? impl->sense(gpio, line) : -ENODEV; } static int @@ -102,6 +105,80 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line) return ret; } +static void +nouveau_gpio_intr_disable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_gpio *gpio = nouveau_gpio(event->priv); + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + impl->intr_mask(gpio, type, 1 << index, 0); +} + +static void +nouveau_gpio_intr_enable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_gpio *gpio = nouveau_gpio(event->priv); + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + impl->intr_mask(gpio, type, 1 << index, 1 << index); +} + +static void +nouveau_gpio_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_gpio *gpio = nouveau_gpio(subdev); + const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass; + u32 hi, lo, e, i; + + impl->intr_stat(gpio, &hi, &lo); + + for (i = 0; e = 0, (hi | lo) && i < impl->lines; i++) { + if (hi & (1 << i)) + e |= NVKM_GPIO_HI; + if (lo & (1 << i)) + e |= NVKM_GPIO_LO; + nouveau_event_trigger(gpio->events, e, i); + } +} + +int +_nouveau_gpio_fini(struct nouveau_object *object, bool suspend) +{ + const struct nouveau_gpio_impl *impl = (void *)object->oclass; + struct nouveau_gpio *gpio = nouveau_gpio(object); + u32 mask = (1 << impl->lines) - 1; + + impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0); + impl->intr_stat(gpio, &mask, &mask); + + return nouveau_subdev_fini(&gpio->base, suspend); +} + +static struct dmi_system_id gpio_reset_ids[] = { + { + .ident = "Apple Macbook 10,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"), + } + }, + { } +}; + +int +_nouveau_gpio_init(struct nouveau_object *object) +{ + struct nouveau_gpio *gpio = nouveau_gpio(object); + int ret; + + ret = nouveau_subdev_init(&gpio->base); + if (ret) + return ret; + + if (gpio->reset && dmi_check_system(gpio_reset_ids)) + gpio->reset(gpio, DCB_GPIO_UNUSED); + + return ret; +} + void _nouveau_gpio_dtor(struct nouveau_object *object) { @@ -113,9 +190,10 @@ _nouveau_gpio_dtor(struct nouveau_object *object) int nouveau_gpio_create_(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, int lines, + struct nouveau_oclass *oclass, int length, void **pobject) { + const struct nouveau_gpio_impl *impl = (void *)oclass; struct nouveau_gpio *gpio; int ret; @@ -125,34 +203,34 @@ 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->reset = impl->reset; + + ret = nouveau_event_create(2, impl->lines, &gpio->events); + if (ret) + return ret; + + gpio->events->priv = gpio; + gpio->events->enable = nouveau_gpio_intr_enable; + gpio->events->disable = nouveau_gpio_intr_disable; + nv_subdev(gpio)->intr = nouveau_gpio_intr; return 0; } -static struct dmi_system_id gpio_reset_ids[] = { - { - .ident = "Apple Macbook 10,1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"), - } - }, - { } -}; - int -nouveau_gpio_init(struct nouveau_gpio *gpio) +_nouveau_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - int ret = nouveau_subdev_init(&gpio->base); - if (ret == 0 && gpio->reset) { - if (dmi_check_system(gpio_reset_ids)) - gpio->reset(gpio, DCB_GPIO_UNUSED); - } - return ret; + struct nouveau_gpio *gpio; + int ret; + + ret = nouveau_gpio_create(parent, engine, oclass, &gpio); + *pobject = nv_object(gpio); + if (ret) + return ret; + + 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 76d5d5465ddd..27ad23eaf185 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c @@ -26,10 +26,6 @@ #include "priv.h" -struct nv10_gpio_priv { - struct nouveau_gpio base; -}; - static int nv10_gpio_sense(struct nouveau_gpio *gpio, int line) { @@ -83,95 +79,38 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) } static void -nv10_gpio_intr(struct nouveau_subdev *subdev) -{ - struct nv10_gpio_priv *priv = (void *)subdev; - u32 intr = nv_rd32(priv, 0x001104); - u32 hi = (intr & 0x0000ffff) >> 0; - u32 lo = (intr & 0xffff0000) >> 16; - int i; - - 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, - struct nouveau_object **pobject) +nv10_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) { - struct nv10_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, 16, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.drive = nv10_gpio_drive; - priv->base.sense = nv10_gpio_sense; - 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; + u32 intr = nv_rd32(gpio, 0x001104); + u32 stat = nv_rd32(gpio, 0x001144) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nv_wr32(gpio, 0x001104, intr); } static void -nv10_gpio_dtor(struct nouveau_object *object) -{ - struct nv10_gpio_priv *priv = (void *)object; - nouveau_gpio_destroy(&priv->base); -} - -static int -nv10_gpio_init(struct nouveau_object *object) -{ - struct nv10_gpio_priv *priv = (void *)object; - int ret; - - ret = nouveau_gpio_init(&priv->base); - if (ret) - return ret; - - nv_wr32(priv, 0x001144, 0x00000000); - nv_wr32(priv, 0x001104, 0xffffffff); - return 0; -} - -static int -nv10_gpio_fini(struct nouveau_object *object, bool suspend) +nv10_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) { - struct nv10_gpio_priv *priv = (void *)object; - nv_wr32(priv, 0x001144, 0x00000000); - return nouveau_gpio_fini(&priv->base, suspend); + u32 inte = nv_rd32(gpio, 0x001144); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nv_wr32(gpio, 0x001144, inte); } -struct nouveau_oclass -nv10_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0x10), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv10_gpio_ctor, - .dtor = nv10_gpio_dtor, - .init = nv10_gpio_init, - .fini = nv10_gpio_fini, +struct nouveau_oclass * +nv10_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x10), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 16, + .intr_stat = nv10_gpio_intr_stat, + .intr_mask = nv10_gpio_intr_mask, + .drive = nv10_gpio_drive, + .sense = nv10_gpio_sense, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c index 2ef774731629..1864fa98e6b1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c @@ -24,15 +24,10 @@ #include "priv.h" -struct nv50_gpio_priv { - struct nouveau_gpio base; -}; - -static void +void nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match) { struct nouveau_bios *bios = nouveau_bios(gpio); - struct nv50_gpio_priv *priv = (void *)gpio; u8 ver, len; u16 entry; int ent = -1; @@ -55,7 +50,7 @@ nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match) gpio->set(gpio, 0, func, line, defs); - nv_mask(priv, reg, 0x00010001 << lsh, val << lsh); + nv_mask(gpio, reg, 0x00010001 << lsh, val << lsh); } } @@ -72,7 +67,7 @@ nv50_gpio_location(int line, u32 *reg, u32 *shift) return 0; } -static int +int nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) { u32 reg, shift; @@ -84,7 +79,7 @@ nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out) return 0; } -static int +int nv50_gpio_sense(struct nouveau_gpio *gpio, int line) { u32 reg, shift; @@ -95,119 +90,40 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line) return !!(nv_rd32(gpio, reg) & (4 << shift)); } -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 > 0x92) - intr1 = nv_rd32(priv, 0xe074) & nv_rd32(priv, 0xe070); - - hi = (intr0 & 0x0000ffff) | (intr1 << 16); - lo = (intr0 >> 16) | (intr1 & 0xffff0000); - - 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 > 0x92) - 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, - struct nouveau_object **pobject) -{ - struct nv50_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, - nv_device(parent)->chipset > 0x92 ? 32 : 16, - &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.reset = nv50_gpio_reset; - priv->base.drive = nv50_gpio_drive; - priv->base.sense = nv50_gpio_sense; - 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; -} - -void -nv50_gpio_dtor(struct nouveau_object *object) -{ - struct nv50_gpio_priv *priv = (void *)object; - nouveau_gpio_destroy(&priv->base); -} - -int -nv50_gpio_init(struct nouveau_object *object) +static void +nv50_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) { - struct nv50_gpio_priv *priv = (void *)object; - int ret; - - ret = nouveau_gpio_init(&priv->base); - if (ret) - return ret; - - /* disable, and ack any pending gpio interrupts */ - nv_wr32(priv, 0xe050, 0x00000000); - nv_wr32(priv, 0xe054, 0xffffffff); - if (nv_device(priv)->chipset > 0x92) { - nv_wr32(priv, 0xe070, 0x00000000); - nv_wr32(priv, 0xe074, 0xffffffff); - } - - return 0; + u32 intr = nv_rd32(gpio, 0x00e054); + u32 stat = nv_rd32(gpio, 0x00e050) & intr; + *lo = (stat & 0xffff0000) >> 16; + *hi = (stat & 0x0000ffff); + nv_wr32(gpio, 0x00e054, intr); } -int -nv50_gpio_fini(struct nouveau_object *object, bool suspend) +static void +nv50_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) { - struct nv50_gpio_priv *priv = (void *)object; - nv_wr32(priv, 0xe050, 0x00000000); - if (nv_device(priv)->chipset > 0x92) - nv_wr32(priv, 0xe070, 0x00000000); - return nouveau_gpio_fini(&priv->base, suspend); + u32 inte = nv_rd32(gpio, 0x00e050); + if (type & NVKM_GPIO_LO) + inte = (inte & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte = (inte & ~mask) | data; + nv_wr32(gpio, 0x00e050, inte); } -struct nouveau_oclass -nv50_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0x50), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv50_gpio_ctor, - .dtor = nv50_gpio_dtor, - .init = nv50_gpio_init, - .fini = nv50_gpio_fini, +struct nouveau_oclass * +nv50_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x50), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 16, + .intr_stat = nv50_gpio_intr_stat, + .intr_mask = nv50_gpio_intr_mask, + .drive = nv50_gpio_drive, + .sense = nv50_gpio_sense, + .reset = nv50_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c new file mode 100644 index 000000000000..252083d376f5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c @@ -0,0 +1,74 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "priv.h" + +void +nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) +{ + u32 intr0 = nv_rd32(gpio, 0x00e054); + u32 intr1 = nv_rd32(gpio, 0x00e074); + u32 stat0 = nv_rd32(gpio, 0x00e050) & intr0; + u32 stat1 = nv_rd32(gpio, 0x00e070) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nv_wr32(gpio, 0x00e054, intr0); + nv_wr32(gpio, 0x00e074, intr1); +} + +void +nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) +{ + u32 inte0 = nv_rd32(gpio, 0x00e050); + u32 inte1 = nv_rd32(gpio, 0x00e070); + if (type & NVKM_GPIO_LO) + inte0 = (inte0 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff); + mask >>= 16; + data >>= 16; + if (type & NVKM_GPIO_LO) + inte1 = (inte1 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte1 = (inte1 & ~mask) | data; + nv_wr32(gpio, 0x00e050, inte0); + nv_wr32(gpio, 0x00e070, inte1); +} + +struct nouveau_oclass * +nv92_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x92), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, + }, + .lines = 32, + .intr_stat = nv92_gpio_intr_stat, + .intr_mask = nv92_gpio_intr_mask, + .drive = nv50_gpio_drive, + .sense = nv50_gpio_sense, + .reset = nv50_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c index 010431e3acec..a4682b0956ad 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c @@ -24,15 +24,10 @@ #include "priv.h" -struct nvd0_gpio_priv { - struct nouveau_gpio base; -}; - void nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match) { struct nouveau_bios *bios = nouveau_bios(gpio); - struct nvd0_gpio_priv *priv = (void *)gpio; u8 ver, len; u16 entry; int ent = -1; @@ -51,9 +46,9 @@ nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match) gpio->set(gpio, 0, func, line, defs); - nv_mask(priv, 0x00d610 + (line * 4), 0xff, unk0); + nv_mask(gpio, 0x00d610 + (line * 4), 0xff, unk0); if (unk1--) - nv_mask(priv, 0x00d740 + (unk1 * 4), 0xff, line); + nv_mask(gpio, 0x00d740 + (unk1 * 4), 0xff, line); } } @@ -72,36 +67,19 @@ nvd0_gpio_sense(struct nouveau_gpio *gpio, int line) return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000); } -static int -nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nvd0_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.reset = nvd0_gpio_reset; - priv->base.drive = nvd0_gpio_drive; - priv->base.sense = nvd0_gpio_sense; - 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; -} - -struct nouveau_oclass -nvd0_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0xd0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nvd0_gpio_ctor, - .dtor = nv50_gpio_dtor, - .init = nv50_gpio_init, - .fini = nv50_gpio_fini, +struct nouveau_oclass * +nvd0_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0xd0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 32, + .intr_stat = nv92_gpio_intr_stat, + .intr_mask = nv92_gpio_intr_mask, + .drive = nvd0_gpio_drive, + .sense = nvd0_gpio_sense, + .reset = nvd0_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c index 8988621373b0..e1145b48c76c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c @@ -24,108 +24,51 @@ #include "priv.h" -struct nve0_gpio_priv { - struct nouveau_gpio base; -}; - -void -nve0_gpio_intr(struct nouveau_subdev *subdev) +static void +nve0_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) { - struct nve0_gpio_priv *priv = (void *)subdev; - u32 intr0 = nv_rd32(priv, 0xdc00) & nv_rd32(priv, 0xdc08); - u32 intr1 = nv_rd32(priv, 0xdc80) & nv_rd32(priv, 0xdc88); - u32 hi = (intr0 & 0x0000ffff) | (intr1 << 16); - u32 lo = (intr0 >> 16) | (intr1 & 0xffff0000); - int i; - - for (i = 0; (hi | lo) && i < 32; i++) { - if ((hi | lo) & (1 << i)) - nouveau_event_trigger(priv->base.events, i); - } - - nv_wr32(priv, 0xdc00, intr0); - nv_wr32(priv, 0xdc80, intr1); + u32 intr0 = nv_rd32(gpio, 0x00dc00); + u32 intr1 = nv_rd32(gpio, 0x00dc80); + u32 stat0 = nv_rd32(gpio, 0x00dc08) & intr0; + u32 stat1 = nv_rd32(gpio, 0x00dc88) & intr1; + *lo = (stat1 & 0xffff0000) | (stat0 >> 16); + *hi = (stat1 << 16) | (stat0 & 0x0000ffff); + nv_wr32(gpio, 0x00dc00, intr0); + nv_wr32(gpio, 0x00dc80, intr1); } void -nve0_gpio_intr_enable(struct nouveau_event *event, int line) +nve0_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) { - const u32 addr = line < 16 ? 0xdc00 : 0xdc80; - const u32 mask = 0x00010001 << (line & 0xf); - nv_wr32(event->priv, addr + 0x00, mask); - nv_mask(event->priv, addr + 0x08, mask, mask); -} - -void -nve0_gpio_intr_disable(struct nouveau_event *event, int line) -{ - const u32 addr = line < 16 ? 0xdc00 : 0xdc80; - const u32 mask = 0x00010001 << (line & 0xf); - nv_mask(event->priv, addr + 0x08, mask, 0x00000000); - nv_wr32(event->priv, addr + 0x00, mask); -} - -int -nve0_gpio_fini(struct nouveau_object *object, bool suspend) -{ - struct nve0_gpio_priv *priv = (void *)object; - nv_wr32(priv, 0xdc08, 0x00000000); - nv_wr32(priv, 0xdc88, 0x00000000); - return nouveau_gpio_fini(&priv->base, suspend); -} - -int -nve0_gpio_init(struct nouveau_object *object) -{ - struct nve0_gpio_priv *priv = (void *)object; - int ret; - - ret = nouveau_gpio_init(&priv->base); - if (ret) - return ret; - - nv_wr32(priv, 0xdc00, 0xffffffff); - nv_wr32(priv, 0xdc80, 0xffffffff); - return 0; -} - -void -nve0_gpio_dtor(struct nouveau_object *object) -{ - struct nve0_gpio_priv *priv = (void *)object; - nouveau_gpio_destroy(&priv->base); -} - -static int -nve0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nve0_gpio_priv *priv; - int ret; - - ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - priv->base.reset = nvd0_gpio_reset; - priv->base.drive = nvd0_gpio_drive; - priv->base.sense = nvd0_gpio_sense; - priv->base.events->priv = priv; - priv->base.events->enable = nve0_gpio_intr_enable; - priv->base.events->disable = nve0_gpio_intr_disable; - nv_subdev(priv)->intr = nve0_gpio_intr; - return 0; + u32 inte0 = nv_rd32(gpio, 0x00dc08); + u32 inte1 = nv_rd32(gpio, 0x00dc88); + if (type & NVKM_GPIO_LO) + inte0 = (inte0 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff); + mask >>= 16; + data >>= 16; + if (type & NVKM_GPIO_LO) + inte1 = (inte1 & ~(mask << 16)) | (data << 16); + if (type & NVKM_GPIO_HI) + inte1 = (inte1 & ~mask) | data; + nv_wr32(gpio, 0x00dc08, inte0); + nv_wr32(gpio, 0x00dc88, inte1); } -struct nouveau_oclass -nve0_gpio_oclass = { - .handle = NV_SUBDEV(GPIO, 0xe0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nve0_gpio_ctor, - .dtor = nv50_gpio_dtor, - .init = nve0_gpio_init, - .fini = nve0_gpio_fini, +struct nouveau_oclass * +nve0_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0xe0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_gpio_ctor, + .dtor = _nouveau_gpio_dtor, + .init = _nouveau_gpio_init, + .fini = _nouveau_gpio_fini, }, -}; + .lines = 32, + .intr_stat = nve0_gpio_intr_stat, + .intr_mask = nve0_gpio_intr_mask, + .drive = nvd0_gpio_drive, + .sense = nvd0_gpio_sense, + .reset = nvd0_gpio_reset, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h index 2ee1c895c782..e1724dfc86ae 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h @@ -3,15 +3,65 @@ #include <subdev/gpio.h> -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_intr_enable(struct nouveau_event *, int line); -void nv50_gpio_intr_disable(struct nouveau_event *, int line); +#define nouveau_gpio_create(p,e,o,d) \ + nouveau_gpio_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nouveau_gpio_destroy(p) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_dtor(nv_object(gpio)); \ +}) +#define nouveau_gpio_init(p) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_init(nv_object(gpio)); \ +}) +#define nouveau_gpio_fini(p,s) ({ \ + struct nouveau_gpio *gpio = (p); \ + _nouveau_gpio_fini(nv_object(gpio), (s)); \ +}) + +int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, void **); +int _nouveau_gpio_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nouveau_gpio_dtor(struct nouveau_object *); +int _nouveau_gpio_init(struct nouveau_object *); +int _nouveau_gpio_fini(struct nouveau_object *, bool); + +struct nouveau_gpio_impl { + struct nouveau_oclass base; + int lines; + + /* read and ack pending interrupts, returning only data + * for lines that have not been masked off, while still + * performing the ack for anything that was pending. + */ + void (*intr_stat)(struct nouveau_gpio *, u32 *, u32 *); + + /* mask on/off interrupts for hi/lo transitions on a + * given set of gpio lines + */ + void (*intr_mask)(struct nouveau_gpio *, u32, u32, u32); + + /* configure gpio direction and output value */ + int (*drive)(struct nouveau_gpio *, int line, int dir, int out); + + /* sense current state of given gpio line */ + int (*sense)(struct nouveau_gpio *, int line); + + /*XXX*/ + void (*reset)(struct nouveau_gpio *, u8); +}; + +void nv50_gpio_reset(struct nouveau_gpio *, u8); +int nv50_gpio_drive(struct nouveau_gpio *, int, int, int); +int nv50_gpio_sense(struct nouveau_gpio *, int); + +void nv92_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *); +void nv92_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32); void nvd0_gpio_reset(struct nouveau_gpio *, u8); int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int); int nvd0_gpio_sense(struct nouveau_gpio *, int); + #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c index 4b195ac4da66..2c2731a6cf91 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs <bskeggs@redhat.com> */ -#include <subdev/i2c.h> +#include "port.h" struct anx9805_i2c_port { struct nouveau_i2c_port base; @@ -37,6 +37,8 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh) struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; u8 tmp, i; + DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh); + nv_wri2cr(mast, chan->addr, 0xa0, link_bw); nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); nv_wri2cr(mast, chan->addr, 0xa2, 0x01); @@ -60,21 +62,29 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh) } static int -anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size) +anx9805_aux(struct nouveau_i2c_port *port, bool retry, + u8 type, u32 addr, u8 *data, u8 size) { struct anx9805_i2c_port *chan = (void *)port; struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent; int i, ret = -ETIMEDOUT; + u8 buf[16] = {}; u8 tmp; + DBG("%02x %05x %d\n", type, addr, size); + tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04; nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04); nv_wri2cr(mast, chan->ctrl, 0x07, tmp); nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); nv_wri2cr(mast, chan->addr, 0xe4, 0x80); - for (i = 0; !(type & 1) && i < size; i++) - nv_wri2cr(mast, chan->addr, 0xf0 + i, data[i]); + if (!(type & 1)) { + memcpy(buf, data, size); + DBG("%16ph", buf); + for (i = 0; i < size; i++) + nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]); + } nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type); nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >> 0); nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >> 8); @@ -93,8 +103,13 @@ anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size) goto done; } - for (i = 0; (type & 1) && i < size; i++) - data[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); + if (type & 1) { + for (i = 0; i < size; i++) + buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); + DBG("%16ph", buf); + memcpy(data, buf, size); + } + ret = 0; done: nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c index 5de074ad170b..02eb42be2e9e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c @@ -22,15 +22,19 @@ * Authors: Ben Skeggs */ -#include <subdev/i2c.h> +#include "priv.h" int nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) { + struct nouveau_i2c *i2c = nouveau_i2c(port); if (port->func->aux) { - if (port->func->acquire) - port->func->acquire(port); - return port->func->aux(port, 9, addr, data, size); + int ret = i2c->acquire(port, 0); + if (ret == 0) { + ret = port->func->aux(port, true, 9, addr, data, size); + i2c->release(port); + } + return ret; } return -ENODEV; } @@ -38,10 +42,14 @@ nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) int nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) { + struct nouveau_i2c *i2c = nouveau_i2c(port); if (port->func->aux) { - if (port->func->acquire) - port->func->acquire(port); - return port->func->aux(port, 8, addr, data, size); + int ret = i2c->acquire(port, 0); + if (ret == 0) { + ret = port->func->aux(port, true, 8, addr, data, size); + i2c->release(port); + } + return ret; } return -ENODEV; } @@ -50,13 +58,16 @@ static int aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct nouveau_i2c_port *port = adap->algo_data; + struct nouveau_i2c *i2c = nouveau_i2c(port); struct i2c_msg *msg = msgs; int ret, mcnt = num; if (!port->func->aux) return -ENODEV; - if ( port->func->acquire) - port->func->acquire(port); + + ret = i2c->acquire(port, 0); + if (ret) + return ret; while (mcnt--) { u8 remaining = msg->len; @@ -74,9 +85,11 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (mcnt || remaining > 16) cmd |= 4; /* MOT */ - ret = port->func->aux(port, cmd, msg->addr, ptr, cnt); - if (ret < 0) + ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt); + if (ret < 0) { + i2c->release(port); return ret; + } ptr += cnt; remaining -= cnt; @@ -85,6 +98,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) msg++; } + i2c->release(port); return num; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index 378e05b88e6f..09ba2cc851cf 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c @@ -23,13 +23,16 @@ */ #include <core/option.h> +#include <core/event.h> #include <subdev/bios.h> #include <subdev/bios/dcb.h> #include <subdev/bios/i2c.h> -#include <subdev/i2c.h> #include <subdev/vga.h> +#include "priv.h" +#include "pad.h" + /****************************************************************************** * interface to linux i2c bit-banging algorithm *****************************************************************************/ @@ -45,9 +48,15 @@ nouveau_i2c_pre_xfer(struct i2c_adapter *adap) { struct i2c_algo_bit_data *bit = adap->algo_data; struct nouveau_i2c_port *port = bit->data; - if (port->func->acquire) - port->func->acquire(port); - return 0; + return nouveau_i2c(port)->acquire(port, bit->timeout); +} + +static void +nouveau_i2c_post_xfer(struct i2c_adapter *adap) +{ + struct i2c_algo_bit_data *bit = adap->algo_data; + struct nouveau_i2c_port *port = bit->data; + return nouveau_i2c(port)->release(port); } static void @@ -82,6 +91,15 @@ nouveau_i2c_getsda(void *data) * base i2c "port" class implementation *****************************************************************************/ +int +_nouveau_i2c_port_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_i2c_port *port = (void *)object; + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + nv_ofuncs(pad)->fini(nv_object(pad), suspend); + return nouveau_object_fini(&port->base, suspend); +} + void _nouveau_i2c_port_dtor(struct nouveau_object *object) { @@ -98,7 +116,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, const struct nouveau_i2c_func *func, int size, void **pobject) { - struct nouveau_device *device = nv_device(parent); + struct nouveau_device *device = nv_device(engine); struct nouveau_i2c *i2c = (void *)engine; struct nouveau_i2c_port *port; int ret; @@ -113,8 +131,9 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, port->adapter.owner = THIS_MODULE; port->adapter.dev.parent = nv_device_base(device); port->index = index; + port->aux = -1; port->func = func; - i2c_set_adapdata(&port->adapter, i2c); + mutex_init(&port->mutex); if ( algo == &nouveau_i2c_bit_algo && !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) { @@ -128,6 +147,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, bit->timeout = usecs_to_jiffies(2200); bit->data = port; bit->pre_xfer = nouveau_i2c_pre_xfer; + bit->post_xfer = nouveau_i2c_post_xfer; bit->setsda = nouveau_i2c_setsda; bit->setscl = nouveau_i2c_setscl; bit->getsda = nouveau_i2c_getsda; @@ -141,7 +161,6 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, ret = i2c_add_adapter(&port->adapter); } - /* drop port's i2c subdev refcount, i2c handles this itself */ if (ret == 0) list_add_tail(&port->head, &i2c->ports); return ret; @@ -193,6 +212,75 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type) return NULL; } +static void +nouveau_i2c_release_pad(struct nouveau_i2c_port *port) +{ + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + struct nouveau_i2c *i2c = nouveau_i2c(port); + + if (atomic_dec_and_test(&nv_object(pad)->usecount)) { + nv_ofuncs(pad)->fini(nv_object(pad), false); + wake_up_all(&i2c->wait); + } +} + +static int +nouveau_i2c_try_acquire_pad(struct nouveau_i2c_port *port) +{ + struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); + + if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) { + struct nouveau_object *owner = (void *)pad->port; + do { + if (owner == (void *)port) + return 0; + owner = owner->parent; + } while(owner); + nouveau_i2c_release_pad(port); + return -EBUSY; + } + + pad->next = port; + nv_ofuncs(pad)->init(nv_object(pad)); + return 0; +} + +static int +nouveau_i2c_acquire_pad(struct nouveau_i2c_port *port, unsigned long timeout) +{ + struct nouveau_i2c *i2c = nouveau_i2c(port); + + if (timeout) { + if (wait_event_timeout(i2c->wait, + nouveau_i2c_try_acquire_pad(port) == 0, + timeout) == 0) + return -EBUSY; + } else { + wait_event(i2c->wait, nouveau_i2c_try_acquire_pad(port) == 0); + } + + return 0; +} + +static void +nouveau_i2c_release(struct nouveau_i2c_port *port) +__releases(pad->mutex) +{ + nouveau_i2c(port)->release_pad(port); + mutex_unlock(&port->mutex); +} + +static int +nouveau_i2c_acquire(struct nouveau_i2c_port *port, unsigned long timeout) +__acquires(pad->mutex) +{ + int ret; + mutex_lock(&port->mutex); + if ((ret = nouveau_i2c(port)->acquire_pad(port, timeout))) + mutex_unlock(&port->mutex); + return ret; +} + static int nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, struct nouveau_i2c_board_info *info, @@ -237,11 +325,59 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, return -ENODEV; } +static void +nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_i2c *i2c = nouveau_i2c(event->priv); + struct nouveau_i2c_port *port = i2c->find(i2c, index); + const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass; + if (port && port->aux >= 0) + impl->aux_mask(i2c, type, 1 << port->aux, 0); +} + +static void +nouveau_i2c_intr_enable(struct nouveau_event *event, int type, int index) +{ + struct nouveau_i2c *i2c = nouveau_i2c(event->priv); + struct nouveau_i2c_port *port = i2c->find(i2c, index); + const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass; + if (port && port->aux >= 0) + impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux); +} + +static void +nouveau_i2c_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_i2c_impl *impl = (void *)nv_oclass(subdev); + struct nouveau_i2c *i2c = nouveau_i2c(subdev); + struct nouveau_i2c_port *port; + u32 hi, lo, rq, tx, e; + + if (impl->aux_stat) { + impl->aux_stat(i2c, &hi, &lo, &rq, &tx); + if (hi || lo || rq || tx) { + list_for_each_entry(port, &i2c->ports, head) { + if (e = 0, port->aux < 0) + continue; + + if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG; + if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG; + if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ; + if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE; + + nouveau_event_trigger(i2c->ntfy, e, port->index); + } + } + } +} + int _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) { + struct nouveau_i2c_impl *impl = (void *)nv_oclass(object); struct nouveau_i2c *i2c = (void *)object; struct nouveau_i2c_port *port; + u32 mask; int ret; list_for_each_entry(port, &i2c->ports, head) { @@ -250,6 +386,11 @@ _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) goto fail; } + if ((mask = (1 << impl->aux) - 1), impl->aux_stat) { + impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); + impl->aux_stat(i2c, &mask, &mask, &mask, &mask); + } + return nouveau_subdev_fini(&i2c->base, suspend); fail: list_for_each_entry_continue_reverse(port, &i2c->ports, head) { @@ -290,6 +431,8 @@ _nouveau_i2c_dtor(struct nouveau_object *object) struct nouveau_i2c *i2c = (void *)object; struct nouveau_i2c_port *port, *temp; + nouveau_event_destroy(&i2c->ntfy); + list_for_each_entry_safe(port, temp, &i2c->ports, head) { nouveau_object_ref(NULL, (struct nouveau_object **)&port); } @@ -306,14 +449,14 @@ int nouveau_i2c_create_(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, - struct nouveau_oclass *sclass, int length, void **pobject) { + const struct nouveau_i2c_impl *impl = (void *)oclass; struct nouveau_bios *bios = nouveau_bios(parent); struct nouveau_i2c *i2c; struct nouveau_object *object; struct dcb_i2c_entry info; - int ret, i, j, index = -1; + int ret, i, j, index = -1, pad; struct dcb_output outp; u8 ver, hdr; u32 data; @@ -324,24 +467,48 @@ nouveau_i2c_create_(struct nouveau_object *parent, if (ret) return ret; + nv_subdev(i2c)->intr = nouveau_i2c_intr; i2c->find = nouveau_i2c_find; i2c->find_type = nouveau_i2c_find_type; + i2c->acquire_pad = nouveau_i2c_acquire_pad; + i2c->release_pad = nouveau_i2c_release_pad; + i2c->acquire = nouveau_i2c_acquire; + i2c->release = nouveau_i2c_release; i2c->identify = nouveau_i2c_identify; + init_waitqueue_head(&i2c->wait); INIT_LIST_HEAD(&i2c->ports); while (!dcb_i2c_parse(bios, ++index, &info)) { if (info.type == DCB_I2C_UNUSED) continue; - oclass = sclass; + if (info.share != DCB_I2C_UNUSED) { + if (info.type == DCB_I2C_NVIO_AUX) + pad = info.drive; + else + pad = info.share; + oclass = impl->pad_s; + } else { + pad = 0x100 + info.drive; + oclass = impl->pad_x; + } + + ret = nouveau_object_ctor(NULL, *pobject, oclass, + NULL, pad, &parent); + if (ret < 0) + continue; + + oclass = impl->sclass; do { ret = -EINVAL; if (oclass->handle == info.type) { - ret = nouveau_object_ctor(*pobject, *pobject, + ret = nouveau_object_ctor(parent, *pobject, oclass, &info, index, &object); } } while (ret && (++oclass)->handle); + + nouveau_object_ref(NULL, &parent); } /* in addition to the busses specified in the i2c table, there @@ -380,5 +547,28 @@ nouveau_i2c_create_(struct nouveau_object *parent, } } + ret = nouveau_event_create(4, index, &i2c->ntfy); + if (ret) + return ret; + + i2c->ntfy->priv = i2c; + i2c->ntfy->enable = nouveau_i2c_intr_enable; + i2c->ntfy->disable = nouveau_i2c_intr_disable; + return 0; +} + +int +_nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_i2c *i2c; + int ret; + + ret = nouveau_i2c_create(parent, engine, oclass, &i2c); + *pobject = nv_object(i2c); + if (ret) + return ret; + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c index a6e72d3b06b5..813ffc96e864 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#include "subdev/i2c.h" +#include "priv.h" #ifdef CONFIG_NOUVEAU_I2C_INTERNAL #define T_TIMEOUT 2200000 @@ -187,8 +187,9 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) struct i2c_msg *msg = msgs; int ret = 0, mcnt = num; - if (port->func->acquire) - port->func->acquire(port); + ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT)); + if (ret) + return ret; while (!ret && mcnt--) { u8 remaining = msg->len; @@ -210,6 +211,7 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) } i2c_stop(port); + nouveau_i2c(port)->release(port); return (ret < 0) ? ret : num; } #else diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c index 860d5d2365da..b1725bdea967 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c @@ -22,9 +22,10 @@ * Authors: Ben Skeggs */ -#include <subdev/i2c.h> #include <subdev/vga.h> +#include "priv.h" + struct nv04_i2c_priv { struct nouveau_i2c base; }; @@ -115,29 +116,15 @@ nv04_i2c_sclass[] = { {} }; -static int -nv04_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv04_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv04_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv04_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x04), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv04_i2c_ctor, +struct nouveau_oclass * +nv04_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x04), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv04_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c index 0c2655a03bb4..f16c87ce5ba1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c @@ -22,9 +22,10 @@ * Authors: Ben Skeggs */ -#include <subdev/i2c.h> #include <subdev/vga.h> +#include "priv.h" + struct nv4e_i2c_priv { struct nouveau_i2c base; }; @@ -107,29 +108,15 @@ nv4e_i2c_sclass[] = { {} }; -static int -nv4e_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv4e_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv4e_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv4e_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x4e), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv4e_i2c_ctor, +struct nouveau_oclass * +nv4e_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x4e), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv4e_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c index a8d67a287704..7b8756d4df08 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c @@ -121,29 +121,15 @@ nv50_i2c_sclass[] = { {} }; -static int -nv50_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv50_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv50_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv50_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x50), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv50_i2c_ctor, +struct nouveau_oclass * +nv50_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x50), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv50_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h index 4e5ba48ebf5a..5d2a77421c74 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h @@ -1,7 +1,7 @@ #ifndef __NV50_I2C_H__ #define __NV50_I2C_H__ -#include <subdev/i2c.h> +#include "priv.h" struct nv50_i2c_priv { struct nouveau_i2c base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c index df6d3e4b68be..f59c3a255462 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c @@ -24,6 +24,36 @@ #include "nv50.h" +void +nv94_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + u32 intr = nv_rd32(i2c, 0x00e06c); + u32 stat = nv_rd32(i2c, 0x00e068) & intr, i; + for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) { + if ((stat & (1 << (i * 4)))) *hi |= 1 << i; + if ((stat & (2 << (i * 4)))) *lo |= 1 << i; + if ((stat & (4 << (i * 4)))) *rq |= 1 << i; + if ((stat & (8 << (i * 4)))) *tx |= 1 << i; + } + nv_wr32(i2c, 0x00e06c, intr); +} + +void +nv94_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data) +{ + u32 temp = nv_rd32(i2c, 0x00e068), i; + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) { + if (!(data & (1 << i))) { + temp &= ~(type << (i * 4)); + continue; + } + temp |= type << (i * 4); + } + } + nv_wr32(i2c, 0x00e068, temp); +} + #define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args) #define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args) @@ -69,7 +99,8 @@ auxch_init(struct nouveau_i2c *aux, int ch) } int -nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) +nv94_aux(struct nouveau_i2c_port *base, bool retry, + u8 type, u32 addr, u8 *data, u8 size) { struct nouveau_i2c *aux = nouveau_i2c(base); struct nv50_i2c_port *port = (void *)base; @@ -105,9 +136,8 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) ctrl |= size - 1; nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr); - /* retry transaction a number of times on failure... */ - ret = -EREMOTEIO; - for (retries = 0; retries < 32; retries++) { + /* (maybe) retry transaction a number of times on failure... */ + for (retries = 0; !ret && retries < 32; retries++) { /* reset, and delay a while if this is a retry */ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl); nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); @@ -123,16 +153,21 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) udelay(1); if (!timeout--) { AUX_ERR("tx req timeout 0x%08x\n", ctrl); + ret = -EIO; goto out; } } while (ctrl & 0x00010000); + ret = 1; /* read status, and check if transaction completed ok */ stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0); - if (!(stat & 0x000f0f00)) { - ret = 0; - break; - } + if ((stat & 0x000f0000) == 0x00080000 || + (stat & 0x000f0000) == 0x00020000) + ret = retry ? 0 : 1; + if ((stat & 0x00000100)) + ret = -ETIMEDOUT; + if ((stat & 0x00000e00)) + ret = -EIO; AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); } @@ -147,29 +182,11 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size) out: auxch_fini(aux, ch); - return ret; -} - -void -nv94_i2c_acquire(struct nouveau_i2c_port *base) -{ - struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine; - struct nv50_i2c_port *port = (void *)base; - if (port->ctrl) { - nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000); - nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data); - } -} - -void -nv94_i2c_release(struct nouveau_i2c_port *base) -{ + return ret < 0 ? ret : (stat & 0x000f0000) >> 16; } static const struct nouveau_i2c_func nv94_i2c_func = { - .acquire = nv94_i2c_acquire, - .release = nv94_i2c_release, .drive_scl = nv50_i2c_drive_scl, .drive_sda = nv50_i2c_drive_sda, .sense_scl = nv50_i2c_sense_scl, @@ -206,8 +223,6 @@ nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, static const struct nouveau_i2c_func nv94_aux_func = { - .acquire = nv94_i2c_acquire, - .release = nv94_i2c_release, .aux = nv94_aux, }; @@ -227,6 +242,7 @@ nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + port->base.aux = info->drive; port->addr = info->drive; if (info->share != DCB_I2C_UNUSED) { port->ctrl = 0x00e500 + (info->drive * 0x50); @@ -257,29 +273,19 @@ nv94_i2c_sclass[] = { {} }; -static int -nv94_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv50_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nv94_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nv94_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0x94), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv94_i2c_ctor, +struct nouveau_oclass * +nv94_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0x94), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nv94_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv94_i2c_pad_oclass, + .aux = 4, + .aux_stat = nv94_aux_stat, + .aux_mask = nv94_aux_mask, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c index 29967d30f97c..364ddb1c5f03 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c @@ -42,8 +42,6 @@ nvd0_i2c_sense_sda(struct nouveau_i2c_port *base) static const struct nouveau_i2c_func nvd0_i2c_func = { - .acquire = nv94_i2c_acquire, - .release = nv94_i2c_release, .drive_scl = nv50_i2c_drive_scl, .drive_sda = nv50_i2c_drive_sda, .sense_scl = nvd0_i2c_sense_scl, @@ -75,7 +73,7 @@ nvd0_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return 0; } -static struct nouveau_oclass +struct nouveau_oclass nvd0_i2c_sclass[] = { { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), .ofuncs = &(struct nouveau_ofuncs) { @@ -96,29 +94,19 @@ nvd0_i2c_sclass[] = { {} }; -static int -nvd0_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv50_i2c_priv *priv; - int ret; - - ret = nouveau_i2c_create(parent, engine, oclass, nvd0_i2c_sclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nvd0_i2c_oclass = { - .handle = NV_SUBDEV(I2C, 0xd0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nvd0_i2c_ctor, +struct nouveau_oclass * +nvd0_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xd0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, .dtor = _nouveau_i2c_dtor, .init = _nouveau_i2c_init, .fini = _nouveau_i2c_fini, }, -}; + .sclass = nvd0_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv94_i2c_pad_oclass, + .aux = 4, + .aux_stat = nv94_aux_stat, + .aux_mask = nv94_aux_mask, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c new file mode 100644 index 000000000000..cae77e1ad8dc --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c @@ -0,0 +1,72 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "nv50.h" + +static void +nve0_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx) +{ + u32 intr = nv_rd32(i2c, 0x00dc60); + u32 stat = nv_rd32(i2c, 0x00dc68) & intr, i; + for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) { + if ((stat & (1 << (i * 4)))) *hi |= 1 << i; + if ((stat & (2 << (i * 4)))) *lo |= 1 << i; + if ((stat & (4 << (i * 4)))) *rq |= 1 << i; + if ((stat & (8 << (i * 4)))) *tx |= 1 << i; + } + nv_wr32(i2c, 0x00dc60, intr); +} + +static void +nve0_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data) +{ + u32 temp = nv_rd32(i2c, 0x00dc68), i; + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) { + if (!(data & (1 << i))) { + temp &= ~(type << (i * 4)); + continue; + } + temp |= type << (i * 4); + } + } + nv_wr32(i2c, 0x00dc68, temp); +} + +struct nouveau_oclass * +nve0_i2c_oclass = &(struct nouveau_i2c_impl) { + .base.handle = NV_SUBDEV(I2C, 0xe0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_i2c_ctor, + .dtor = _nouveau_i2c_dtor, + .init = _nouveau_i2c_init, + .fini = _nouveau_i2c_fini, + }, + .sclass = nvd0_i2c_sclass, + .pad_x = &nv04_i2c_pad_oclass, + .pad_s = &nv94_i2c_pad_oclass, + .aux = 4, + .aux_stat = nve0_aux_stat, + .aux_mask = nve0_aux_mask, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c new file mode 100644 index 000000000000..e9e412477c12 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c @@ -0,0 +1,84 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "pad.h" + +int +_nvkm_i2c_pad_fini(struct nouveau_object *object, bool suspend) +{ + struct nvkm_i2c_pad *pad = (void *)object; + DBG("-> NULL\n"); + pad->port = NULL; + return nouveau_object_fini(&pad->base, suspend); +} + +int +_nvkm_i2c_pad_init(struct nouveau_object *object) +{ + struct nvkm_i2c_pad *pad = (void *)object; + DBG("-> PORT:%02x\n", pad->next->index); + pad->port = pad->next; + return nouveau_object_init(&pad->base); +} + +int +nvkm_i2c_pad_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, int index, + int size, void **pobject) +{ + struct nouveau_i2c *i2c = (void *)engine; + struct nouveau_i2c_port *port; + struct nvkm_i2c_pad *pad; + int ret; + + list_for_each_entry(port, &i2c->ports, head) { + pad = nvkm_i2c_pad(port); + if (pad->index == index) { + atomic_inc(&nv_object(pad)->refcount); + *pobject = pad; + return 1; + } + } + + ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject); + pad = *pobject; + if (ret) + return ret; + + pad->index = index; + return 0; +} + +int +_nvkm_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct nvkm_i2c_pad *pad; + int ret; + ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); + *pobject = nv_object(pad); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h new file mode 100644 index 000000000000..452ac10c3004 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h @@ -0,0 +1,58 @@ +#ifndef __NVKM_I2C_PAD_H__ +#define __NVKM_I2C_PAD_H__ + +#include "priv.h" + +struct nvkm_i2c_pad { + struct nouveau_object base; + int index; + struct nouveau_i2c_port *port; + struct nouveau_i2c_port *next; +}; + +static inline struct nvkm_i2c_pad * +nvkm_i2c_pad(struct nouveau_i2c_port *port) +{ + struct nouveau_object *pad = nv_object(port); + while (pad->parent) + pad = pad->parent; + return (void *)pad; +} + +#define nvkm_i2c_pad_create(p,e,o,i,d) \ + nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d) +#define nvkm_i2c_pad_destroy(p) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_dtor(nv_object(_p)); \ +}) +#define nvkm_i2c_pad_init(p) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_init(nv_object(_p)); \ +}) +#define nvkm_i2c_pad_fini(p,s) ({ \ + struct nvkm_i2c_pad *_p = (p); \ + _nvkm_i2c_pad_fini(nv_object(_p), (s)); \ +}) + +int nvkm_i2c_pad_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int index, int, void **); + +int _nvkm_i2c_pad_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +#define _nvkm_i2c_pad_dtor nouveau_object_destroy +int _nvkm_i2c_pad_init(struct nouveau_object *); +int _nvkm_i2c_pad_fini(struct nouveau_object *, bool); + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nvkm_i2c_pad *_pad = (void *)pad; \ + nv_##l(nv_object(_pad)->engine, "PAD:%c:%02x: "f, \ + _pad->index >= 0x100 ? 'X' : 'S', \ + _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c new file mode 100644 index 000000000000..2c4b61296dd1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c @@ -0,0 +1,35 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "pad.h" + +struct nouveau_oclass +nv04_i2c_pad_oclass = { + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nvkm_i2c_pad_ctor, + .dtor = _nvkm_i2c_pad_dtor, + .init = _nvkm_i2c_pad_init, + .fini = _nvkm_i2c_pad_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c new file mode 100644 index 000000000000..0dc6753014f0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c @@ -0,0 +1,86 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "pad.h" + +struct nv94_i2c_pad { + struct nvkm_i2c_pad base; + int addr; +}; + +static int +nv94_i2c_pad_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_i2c *i2c = (void *)object->engine; + struct nv94_i2c_pad *pad = (void *)object; + nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001); + return nvkm_i2c_pad_fini(&pad->base, suspend); +} + +static int +nv94_i2c_pad_init(struct nouveau_object *object) +{ + struct nouveau_i2c *i2c = (void *)object->engine; + struct nv94_i2c_pad *pad = (void *)object; + + switch (nv_oclass(pad->base.next)->handle) { + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX): + nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002); + break; + case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT): + default: + nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001); + break; + } + + nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000); + return nvkm_i2c_pad_init(&pad->base); +} + +static int +nv94_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 index, + struct nouveau_object **pobject) +{ + struct nv94_i2c_pad *pad; + int ret; + + ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); + *pobject = nv_object(pad); + if (ret) + return ret; + + pad->addr = index * 0x50;; + return 0; +} + +struct nouveau_oclass +nv94_i2c_pad_oclass = { + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv94_i2c_pad_ctor, + .dtor = _nvkm_i2c_pad_dtor, + .init = nv94_i2c_pad_init, + .fini = nv94_i2c_pad_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h new file mode 100644 index 000000000000..a8ff6e077af5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h @@ -0,0 +1,15 @@ +#ifndef __NVKM_I2C_PORT_H__ +#define __NVKM_I2C_PORT_H__ + +#include "priv.h" + +#ifndef MSG +#define MSG(l,f,a...) do { \ + struct nouveau_i2c_port *_port = (void *)port; \ + nv_##l(nv_object(_port)->engine, "PORT:%02x: "f, _port->index, ##a); \ +} while(0) +#define DBG(f,a...) MSG(debug, f, ##a) +#define ERR(f,a...) MSG(error, f, ##a) +#endif + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h new file mode 100644 index 000000000000..780090b6425a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h @@ -0,0 +1,85 @@ +#ifndef __NVKM_I2C_H__ +#define __NVKM_I2C_H__ + +#include <subdev/i2c.h> + +extern struct nouveau_oclass nv04_i2c_pad_oclass; +extern struct nouveau_oclass nv94_i2c_pad_oclass; + +#define nouveau_i2c_port_create(p,e,o,i,a,f,d) \ + nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f), \ + sizeof(**d), (void **)d) +#define nouveau_i2c_port_destroy(p) ({ \ + struct nouveau_i2c_port *port = (p); \ + _nouveau_i2c_port_dtor(nv_object(i2c)); \ +}) +#define nouveau_i2c_port_init(p) \ + nouveau_object_init(&(p)->base) +#define nouveau_i2c_port_fini(p,s) \ + nouveau_object_fini(&(p)->base, (s)) + +int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, u8, + const struct i2c_algorithm *, + const struct nouveau_i2c_func *, + int, void **); +void _nouveau_i2c_port_dtor(struct nouveau_object *); +#define _nouveau_i2c_port_init nouveau_object_init +int _nouveau_i2c_port_fini(struct nouveau_object *, bool); + +#define nouveau_i2c_create(p,e,o,d) \ + nouveau_i2c_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nouveau_i2c_destroy(p) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_dtor(nv_object(i2c)); \ +}) +#define nouveau_i2c_init(p) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_init(nv_object(i2c)); \ +}) +#define nouveau_i2c_fini(p,s) ({ \ + struct nouveau_i2c *i2c = (p); \ + _nouveau_i2c_fini(nv_object(i2c), (s)); \ +}) + +int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, void **); +int _nouveau_i2c_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void _nouveau_i2c_dtor(struct nouveau_object *); +int _nouveau_i2c_init(struct nouveau_object *); +int _nouveau_i2c_fini(struct nouveau_object *, bool); + +extern struct nouveau_oclass nouveau_anx9805_sclass[]; +extern struct nouveau_oclass nvd0_i2c_sclass[]; + +extern const struct i2c_algorithm nouveau_i2c_bit_algo; +extern const struct i2c_algorithm nouveau_i2c_aux_algo; + +struct nouveau_i2c_impl { + struct nouveau_oclass base; + + /* supported i2c port classes */ + struct nouveau_oclass *sclass; + struct nouveau_oclass *pad_x; + struct nouveau_oclass *pad_s; + + /* number of native dp aux channels present */ + int aux; + + /* read and ack pending interrupts, returning only data + * for ports that have not been masked off, while still + * performing the ack for anything that was pending. + */ + void (*aux_stat)(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *); + + /* mask on/off interrupt types for a given set of auxch + */ + void (*aux_mask)(struct nouveau_i2c *, u32, u32, u32); +}; + +void nv94_aux_stat(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *); +void nv94_aux_mask(struct nouveau_i2c *, u32, u32, u32); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index 90d8bf8ce0dc..9ca93e2718f7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -34,7 +34,8 @@ nv50_mc_intr[] = { { 0x00008000, NVDEV_ENGINE_BSP }, /* NV84- */ { 0x00020000, NVDEV_ENGINE_VP }, /* NV84- */ { 0x00100000, NVDEV_SUBDEV_TIMER }, - { 0x00200000, NVDEV_SUBDEV_GPIO }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x80000000, NVDEV_ENGINE_SW }, { 0x0002d101, NVDEV_SUBDEV_FB }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c index 95b3d35388a8..3c76d9038f38 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c @@ -36,7 +36,8 @@ nv98_mc_intr[] = { { 0x00040000, NVDEV_SUBDEV_PWR }, /* NVA3:NVC0 */ { 0x00080000, NVDEV_SUBDEV_THERM }, /* NVA3:NVC0 */ { 0x00100000, NVDEV_SUBDEV_TIMER }, - { 0x00200000, NVDEV_SUBDEV_GPIO }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ { 0x00400000, NVDEV_ENGINE_COPY0 }, /* NVA3- */ { 0x10000000, NVDEV_SUBDEV_BUS }, { 0x80000000, NVDEV_ENGINE_SW }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index ac7f99a15fa7..f9c6a678b47d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -38,7 +38,8 @@ nvc0_mc_intr[] = { { 0x00040000, NVDEV_SUBDEV_THERM }, { 0x00020000, NVDEV_ENGINE_VP }, { 0x00100000, NVDEV_SUBDEV_TIMER }, - { 0x00200000, NVDEV_SUBDEV_GPIO }, + { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */ + { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */ { 0x01000000, NVDEV_SUBDEV_PWR }, { 0x02000000, NVDEV_SUBDEV_LTCG }, { 0x08000000, NVDEV_SUBDEV_FB }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c index 64f8b4702bf7..fcaabe8456e3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c @@ -150,7 +150,7 @@ mxm_dcb_sanitise_entry(struct nouveau_bios *bios, void *data, int idx, u16 pdcb) * common example is DP->eDP. */ conn = bios->data; - conn += dcb_conn(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len); + conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len); type = conn[0]; switch (ctx.desc.conn_type) { case 0x01: /* LVDS */ diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 6ecea9b2b15a..1fa222e8f007 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -44,6 +44,7 @@ #include <subdev/i2c.h> #include <subdev/gpio.h> +#include <engine/disp.h> MODULE_PARM_DESC(tv_disable, "Disable TV-out detection"); static int nouveau_tv_disable = 0; @@ -75,7 +76,8 @@ find_encoder(struct drm_connector *connector, int type) continue; nv_encoder = nouveau_encoder(obj_to_encoder(obj)); - if (type == DCB_OUTPUT_ANY || nv_encoder->dcb->type == type) + if (type == DCB_OUTPUT_ANY || + (nv_encoder->dcb && nv_encoder->dcb->type == type)) return nv_encoder; } @@ -100,22 +102,24 @@ 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); + nouveau_event_ref(NULL, &nv_connector->hpd); kfree(nv_connector->edid); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); + if (nv_connector->aux.transfer) + drm_dp_aux_unregister(&nv_connector->aux); kfree(connector); } -static struct nouveau_i2c_port * -nouveau_connector_ddc_detect(struct drm_connector *connector, - struct nouveau_encoder **pnv_encoder) +static struct nouveau_encoder * +nouveau_connector_ddc_detect(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_gpio *gpio = nouveau_gpio(drm->device); - struct nouveau_i2c_port *port = NULL; + struct nouveau_encoder *nv_encoder; + struct drm_mode_object *obj; int i, panel = -ENODEV; /* eDP panels need powering on by us (if the VBIOS doesn't default it @@ -130,13 +134,9 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, } } - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct nouveau_encoder *nv_encoder; - struct drm_mode_object *obj; - int id; - - id = connector->encoder_ids[i]; - if (!id) + for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) { + int id = connector->encoder_ids[i]; + if (id == 0) break; obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); @@ -144,22 +144,24 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, continue; nv_encoder = nouveau_encoder(obj_to_encoder(obj)); - port = nv_encoder->i2c; - if (port && nv_probe_i2c(port, 0x50)) { - *pnv_encoder = nv_encoder; - break; + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + int ret = nouveau_dp_detect(nv_encoder); + if (ret == 0) + break; + } else + if (nv_encoder->i2c) { + if (nv_probe_i2c(nv_encoder->i2c, 0x50)) + break; } - - port = NULL; } /* eDP panel not detected, restore panel power GPIO to previous * state to avoid confusing the SOR for other output types. */ - if (!port && panel == 0) + if (!nv_encoder && panel == 0) gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel); - return port; + return nv_encoder; } static struct nouveau_encoder * @@ -258,8 +260,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) if (ret < 0 && ret != -EACCES) return conn_status; - i2c = nouveau_connector_ddc_detect(connector, &nv_encoder); - if (i2c) { + nv_encoder = nouveau_connector_ddc_detect(connector); + if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) { nv_connector->edid = drm_get_edid(connector, &i2c->adapter); drm_mode_connector_update_edid_property(connector, nv_connector->edid); @@ -269,14 +271,6 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) goto detect_analog; } - if (nv_encoder->dcb->type == DCB_OUTPUT_DP && - !nouveau_dp_detect(to_drm_encoder(nv_encoder))) { - NV_ERROR(drm, "Detected %s, but failed init\n", - connector->name); - conn_status = connector_status_disconnected; - goto out; - } - /* Override encoder type for DVI-I based on whether EDID * says the display is digital or analog, both use the * same i2c channel so the value returned from ddc_detect @@ -912,33 +906,103 @@ nouveau_connector_funcs_lvds = { }; static void +nouveau_connector_dp_dpms(struct drm_connector *connector, int mode) +{ + struct nouveau_encoder *nv_encoder = NULL; + + if (connector->encoder) + nv_encoder = nouveau_encoder(connector->encoder); + if (nv_encoder && nv_encoder->dcb && + nv_encoder->dcb->type == DCB_OUTPUT_DP) { + if (mode == DRM_MODE_DPMS_ON) { + u8 data = DP_SET_POWER_D0; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + usleep_range(1000, 2000); + } else { + u8 data = DP_SET_POWER_D3; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + } + } + + drm_helper_connector_dpms(connector, mode); +} + +static const struct drm_connector_funcs +nouveau_connector_funcs_dp = { + .dpms = nouveau_connector_dp_dpms, + .save = NULL, + .restore = NULL, + .detect = nouveau_connector_detect, + .destroy = nouveau_connector_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = nouveau_connector_set_property, + .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); + container_of(work, typeof(*nv_connector), 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); + struct nouveau_drm *drm = nouveau_drm(connector->dev); + const char *name = connector->name; - NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", - connector->name); + if (nv_connector->status & NVKM_HPD_IRQ) { + } else { + bool plugged = (nv_connector->status != NVKM_HPD_UNPLUG); - if (plugged) - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); - else - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); - drm_helper_hpd_irq_event(dev); + 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(connector->dev); + } + + nouveau_event_get(nv_connector->hpd); } static int -nouveau_connector_hotplug(void *data, int index) +nouveau_connector_hotplug(void *data, u32 type, int index) { struct nouveau_connector *nv_connector = data; - schedule_work(&nv_connector->hpd_work); - return NVKM_EVENT_KEEP; + nv_connector->status = type; + schedule_work(&nv_connector->work); + return NVKM_EVENT_DROP; +} + +static ssize_t +nouveau_connector_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + struct nouveau_connector *nv_connector = + container_of(aux, typeof(*nv_connector), aux); + struct nouveau_encoder *nv_encoder; + struct nouveau_i2c_port *port; + int ret; + + nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); + if (!nv_encoder || !(port = nv_encoder->i2c)) + return -ENODEV; + if (WARN_ON(msg->size > 16)) + return -E2BIG; + if (msg->size == 0) + return msg->size; + + ret = nouveau_i2c(port)->acquire(port, 0); + if (ret) + return ret; + + ret = port->func->aux(port, false, msg->request, msg->address, + msg->buffer, msg->size); + nouveau_i2c(port)->release(port); + if (ret >= 0) { + msg->reply = ret; + return msg->size; + } + + return ret; } static int @@ -974,9 +1038,9 @@ nouveau_connector_create(struct drm_device *dev, int index) { const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct nouveau_display *disp = nouveau_display(dev); struct nouveau_connector *nv_connector = NULL; + struct nouveau_disp *pdisp = nouveau_disp(drm->device); struct drm_connector *connector; int type, ret = 0; bool dummy; @@ -992,33 +1056,15 @@ 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 */ nv_connector->dcb = olddcb_conn(dev, index); if (nv_connector->dcb) { - static const u8 hpd[16] = { - 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60, - }; - u32 entry = ROM16(nv_connector->dcb[0]); if (olddcb_conntab(dev)[3] >= 4) entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; - ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)], - DCB_GPIO_UNUSED, &nv_connector->hpd); - 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) { @@ -1040,7 +1086,6 @@ nouveau_connector_create(struct drm_device *dev, int index) } } else { nv_connector->type = DCB_CONNECTOR_NONE; - nv_connector->hpd.func = DCB_GPIO_UNUSED; } /* no vbios data, or an unknown dcb connector type - attempt to @@ -1080,8 +1125,8 @@ nouveau_connector_create(struct drm_device *dev, int index) } } - type = drm_conntype_from_dcb(nv_connector->type); - if (type == DRM_MODE_CONNECTOR_LVDS) { + switch ((type = drm_conntype_from_dcb(nv_connector->type))) { + case DRM_MODE_CONNECTOR_LVDS: ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); if (ret) { NV_ERROR(drm, "Error parsing LVDS table, disabling\n"); @@ -1090,8 +1135,23 @@ nouveau_connector_create(struct drm_device *dev, int index) } funcs = &nouveau_connector_funcs_lvds; - } else { + break; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + nv_connector->aux.dev = dev->dev; + nv_connector->aux.transfer = nouveau_connector_aux_xfer; + ret = drm_dp_aux_register(&nv_connector->aux); + if (ret) { + NV_ERROR(drm, "failed to register aux channel\n"); + kfree(nv_connector); + return ERR_PTR(ret); + } + + funcs = &nouveau_connector_funcs_dp; + break; + default: funcs = &nouveau_connector_funcs; + break; } /* defaults, will get overridden in detect() */ @@ -1166,10 +1226,16 @@ nouveau_connector_create(struct drm_device *dev, int index) break; } - connector->polled = DRM_CONNECTOR_POLL_CONNECT; - if (nv_connector->hpd.func != DCB_GPIO_UNUSED) + ret = nouveau_event_new(pdisp->hpd, NVKM_HPD, index, + nouveau_connector_hotplug, + nv_connector, &nv_connector->hpd); + if (ret) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + else connector->polled = DRM_CONNECTOR_POLL_HPD; + INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work); + drm_sysfs_connector_add(connector); return connector; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 264a778f473b..8861b6c579ad 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -28,12 +28,12 @@ #define __NOUVEAU_CONNECTOR_H__ #include <drm/drm_edid.h> +#include <drm/drm_dp_helper.h> #include "nouveau_crtc.h" #include <core/event.h> #include <subdev/bios.h> -#include <subdev/bios/gpio.h> struct nouveau_i2c_port; @@ -67,9 +67,11 @@ struct nouveau_connector { u8 index; u8 *dcb; - struct dcb_gpio_func hpd; - struct work_struct hpd_work; - struct nouveau_eventh *hpd_func; + struct nouveau_eventh *hpd; + u32 status; + struct work_struct work; + + struct drm_dp_aux aux; int dithering_mode; int dithering_depth; diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index d1e5890784d7..a0534489d23f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -74,7 +74,7 @@ struct nouveau_crtc { static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) { - return container_of(crtc, struct nouveau_crtc, base); + return crtc ? container_of(crtc, struct nouveau_crtc, base) : NULL; } static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 26fbc4f43a5f..26b5647188ef 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -42,7 +42,7 @@ #include <core/class.h> static int -nouveau_display_vblank_handler(void *data, int head) +nouveau_display_vblank_handler(void *data, u32 type, int head) { struct nouveau_drm *drm = data; drm_handle_vblank(drm->dev, head); @@ -178,7 +178,7 @@ nouveau_display_vblank_init(struct drm_device *dev) return -ENOMEM; for (i = 0; i < dev->mode_config.num_crtc; i++) { - ret = nouveau_event_new(pdisp->vblank, i, + ret = nouveau_event_new(pdisp->vblank, 1, i, nouveau_display_vblank_handler, drm, &disp->vblank[i]); if (ret) { @@ -393,7 +393,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 (conn->hpd_func) nouveau_event_get(conn->hpd_func); + if (conn->hpd) nouveau_event_get(conn->hpd); } return ret; @@ -408,7 +408,7 @@ 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 (conn->hpd_func) nouveau_event_put(conn->hpd_func); + if (conn->hpd) nouveau_event_put(conn->hpd); } 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 36fd22500569..5675ffc175ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -55,11 +55,10 @@ nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch, } -bool -nouveau_dp_detect(struct drm_encoder *encoder) +int +nouveau_dp_detect(struct nouveau_encoder *nv_encoder) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; + struct drm_device *dev = nv_encoder->base.base.dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_i2c_port *auxch; u8 *dpcd = nv_encoder->dp.dpcd; @@ -67,11 +66,11 @@ nouveau_dp_detect(struct drm_encoder *encoder) auxch = nv_encoder->i2c; if (!auxch) - return false; + return -ENODEV; ret = nv_rdaux(auxch, DP_DPCD_REV, dpcd, 8); if (ret) - return false; + return ret; nv_encoder->dp.link_bw = 27000 * dpcd[1]; nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; @@ -91,6 +90,5 @@ nouveau_dp_detect(struct drm_encoder *encoder) nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); nouveau_dp_probe_oui(dev, auxch, dpcd); - - return true; + return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 24660c0f713d..5f0e37fc2849 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -46,6 +46,7 @@ struct nouveau_encoder { /* different to drm_encoder.crtc, this reflects what's * actually programmed on the hw, not the proposed crtc */ struct drm_crtc *crtc; + u32 ctrl; struct drm_display_mode mode; int last_dpms; @@ -84,9 +85,7 @@ get_slave_funcs(struct drm_encoder *enc) } /* nouveau_dp.c */ -bool nouveau_dp_detect(struct drm_encoder *); -void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate, - struct nouveau_object *); +int nouveau_dp_detect(struct nouveau_encoder *); struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 90074d620e31..ab5ea3b0d666 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -166,7 +166,7 @@ nouveau_fence_done(struct nouveau_fence *fence) } static int -nouveau_fence_wait_uevent_handler(void *data, int index) +nouveau_fence_wait_uevent_handler(void *data, u32 type, int index) { struct nouveau_fence_priv *priv = data; wake_up_all(&priv->waiting); @@ -183,7 +183,7 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr) struct nouveau_eventh *handler; int ret = 0; - ret = nouveau_event_new(pfifo->uevent, 0, + ret = nouveau_event_new(pfifo->uevent, 1, 0, nouveau_fence_wait_uevent_handler, priv, &handler); if (ret) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 3c58854aff03..afdf607df3e6 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -26,6 +26,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> #include "nouveau_drm.h" #include "nouveau_dma.h" @@ -1207,6 +1208,7 @@ static void nv50_crtc_disable(struct drm_crtc *crtc) { struct nv50_head *head = nv50_head(crtc); + evo_sync(crtc->dev); if (head->image) nouveau_bo_unpin(head->image); nouveau_bo_ref(NULL, &head->image); @@ -1700,10 +1702,9 @@ nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) } static void -nv50_hdmi_disconnect(struct drm_encoder *encoder) +nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); struct nv50_disp *disp = nv50_disp(encoder->dev); const u32 moff = (nv_crtc->index << 3) | nv_encoder->or; @@ -1722,7 +1723,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) struct drm_device *dev = encoder->dev; struct nv50_disp *disp = nv50_disp(dev); struct drm_encoder *partner; - int or = nv_encoder->or; + u32 mthd; nv_encoder->last_dpms = mode; @@ -1740,7 +1741,17 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) } } - nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON)); + mthd = (ffs(nv_encoder->dcb->sorconf.link) - 1) << 2; + mthd |= nv_encoder->or; + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + nv_call(disp->core, NV50_DISP_SOR_PWR | mthd, 1); + mthd |= NV94_DISP_SOR_DP_PWR; + } else { + mthd |= NV50_DISP_SOR_PWR; + } + + nv_call(disp->core, mthd, (mode == DRM_MODE_DPMS_ON)); } static bool @@ -1764,33 +1775,36 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, } static void -nv50_sor_disconnect(struct drm_encoder *encoder) +nv50_sor_ctrl(struct nouveau_encoder *nv_encoder, u32 mask, u32 data) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_mast *mast = nv50_mast(encoder->dev); - const int or = nv_encoder->or; - u32 *push; - - if (nv_encoder->crtc) { - nv50_crtc_prepare(nv_encoder->crtc); - - push = evo_wait(mast, 4); - if (push) { - if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { - evo_mthd(push, 0x0600 + (or * 0x40), 1); - evo_data(push, 0x00000000); - } else { - evo_mthd(push, 0x0200 + (or * 0x20), 1); - evo_data(push, 0x00000000); - } - evo_kick(push, mast); + struct nv50_mast *mast = nv50_mast(nv_encoder->base.base.dev); + u32 temp = (nv_encoder->ctrl & ~mask) | (data & mask), *push; + if (temp != nv_encoder->ctrl && (push = evo_wait(mast, 2))) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0600 + (nv_encoder->or * 0x40), 1); + evo_data(push, (nv_encoder->ctrl = temp)); + } else { + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); + evo_data(push, (nv_encoder->ctrl = temp)); } - - nv50_hdmi_disconnect(encoder); + evo_kick(push, mast); } +} + +static void +nv50_sor_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; nv_encoder->crtc = NULL; + + if (nv_crtc) { + nv50_crtc_prepare(&nv_crtc->base); + nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0); + nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc); + } } static void @@ -1810,12 +1824,14 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_connector *nv_connector; struct nvbios *bios = &drm->vbios; - u32 *push, lvds = 0; + u32 lvds = 0, mask, ctrl; u8 owner = 1 << nv_crtc->index; u8 proto = 0xf; u8 depth = 0x0; nv_connector = nouveau_encoder_connector_get(nv_encoder); + nv_encoder->crtc = encoder->crtc; + switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: if (nv_encoder->dcb->sorconf.link & 1) { @@ -1827,7 +1843,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, proto = 0x2; } - nv50_hdmi_mode_set(encoder, mode); + nv50_hdmi_mode_set(&nv_encoder->base.base, mode); break; case DCB_OUTPUT_LVDS: proto = 0x0; @@ -1883,19 +1899,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, break; } - nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); + nv50_sor_dpms(&nv_encoder->base.base, DRM_MODE_DPMS_ON); - push = evo_wait(nv50_mast(dev), 8); - if (push) { - if (nv50_vers(mast) < NVD0_DISP_CLASS) { - u32 ctrl = (depth << 16) | (proto << 8) | owner; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - ctrl |= 0x00001000; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - ctrl |= 0x00002000; - evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1); - evo_data(push, ctrl); - } else { + if (nv50_vers(mast) >= NVD0_DISP_CLASS) { + u32 *push = evo_wait(mast, 3); + if (push) { u32 magic = 0x31ec6000 | (nv_crtc->index << 25); u32 syncs = 0x00000001; @@ -1910,14 +1918,21 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); evo_data(push, syncs | (depth << 6)); evo_data(push, magic); - evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 1); - evo_data(push, owner | (proto << 8)); + evo_kick(push, mast); } - evo_kick(push, mast); + ctrl = proto << 8; + mask = 0x00000f00; + } else { + ctrl = (depth << 16) | (proto << 8); + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl |= 0x00001000; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl |= 0x00002000; + mask = 0x000f3f00; } - nv_encoder->crtc = encoder->crtc; + nv50_sor_ctrl(nv_encoder, mask | owner, ctrl | owner); } static void |