mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-12 15:44:01 +08:00
Merge branch 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6 into drm-next
display rework fixes lots of displayport issues. * 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6: (43 commits) drm/nouveau/disp/dp: fix tmds passthrough on dp connector drm/nouveau/dp: probe dpcd to determine connectedness drm/nv50-: trigger update after all connectors disabled drm/nv50-: prepare for attaching a SOR to multiple heads drm/gf119-/disp: fix debug output on update failure drm/nouveau/disp/dp: make use of postcursor when its available drm/g94-/disp/dp: take max pullup value across all lanes drm/nouveau/bios/dp: parse lane postcursor data drm/nouveau/dp: fix support for dpms drm/nouveau: register a drm_dp_aux channel for each dp connector drm/g94-/disp: add method to power-off dp lanes drm/nouveau/disp/dp: maintain link in response to hpd signal drm/g94-/disp: bash and wait for something after changing lane power regs drm/nouveau/disp/dp: split link config/power into two steps drm/nv50/disp: train PIOR-attached DP from second supervisor drm/nouveau/disp/dp: make use of existing output data for link training drm/gf119/disp: start removing direct vbios parsing from supervisor drm/nv50/disp: start removing direct vbios parsing from supervisor drm/nouveau/disp/dp: maintain receiver caps in response to hpd signal drm/nouveau/disp/dp: create subclass for dp outputs ...
This commit is contained in:
commit
bc1dfff04a
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
172
drivers/gpu/drm/nouveau/core/engine/disp/conn.c
Normal file
172
drivers/gpu/drm/nouveau/core/engine/disp/conn.c
Normal file
@ -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;
|
59
drivers/gpu/drm/nouveau/core/engine/disp/conn.h
Normal file
59
drivers/gpu/drm/nouveau/core/engine/disp/conn.h
Normal file
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
static int
|
||||
dp_link_train_update(struct dp_state *dp, u32 delay)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
|
||||
mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
|
||||
else
|
||||
udelay(delay);
|
||||
|
||||
ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
|
||||
ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DBG("status %6ph\n", dp->stat);
|
||||
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, bool pc, u32 delay)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
int ret;
|
||||
|
||||
if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
|
||||
mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
|
||||
else
|
||||
udelay(delay);
|
||||
|
||||
ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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 (dp->dpcd[1] > dp->outp->dpconf.link_bw)
|
||||
dp->dpcd[1] = dp->outp->dpconf.link_bw;
|
||||
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;
|
||||
|
||||
/* adjust required bandwidth for 8B/10B coding overhead */
|
||||
datarate = (datarate / 8) * 10;
|
||||
/* 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++;
|
||||
}
|
||||
cfg--;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* off-chip encoders require matching the exact encoder type */
|
||||
if (dcb->location != 0)
|
||||
type |= dcb->extdev << 8;
|
||||
|
||||
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;
|
||||
u32 mask, conf;
|
||||
|
||||
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;
|
||||
}
|
||||
outp = exec_clkcmp(priv, head, 0xff, pclk, &conf);
|
||||
if (!outp)
|
||||
return;
|
||||
|
||||
nouveau_dp_train(&priv->base, priv->sor.dp,
|
||||
&outp, head, datarate);
|
||||
}
|
||||
/* 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;
|
||||
u32 conf;
|
||||
|
||||
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;
|
||||
}
|
||||
outp = exec_clkcmp(priv, head, 1, pclk, &conf);
|
||||
if (!outp)
|
||||
return;
|
||||
|
||||
nouveau_dp_train(&priv->base, priv->pior.dp,
|
||||
&outp, head, datarate);
|
||||
}
|
||||
}
|
||||
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,10 +1700,15 @@ 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),
|
||||
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,12 +123,17 @@ 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),
|
||||
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
u32 conf, 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;
|
||||
}
|
||||
outp = exec_clkcmp(priv, head, 0xff, pclk, &conf);
|
||||
if (!outp)
|
||||
return;
|
||||
|
||||
nouveau_dp_train(&priv->base, priv->sor.dp,
|
||||
&outp, head, pclk);
|
||||
/* 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 (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;
|
||||
}
|
||||
|
||||
nv_mask(priv, addr, 0x00000707, data);
|
||||
if (nvkm_output_dp_train(outp, pclk, true))
|
||||
ERR("link not trained before attach\n");
|
||||
}
|
||||
|
||||
exec_clkcmp(priv, head, 0, pclk, &conf);
|
||||
|
||||
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,10 +1378,15 @@ 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),
|
||||
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
137
drivers/gpu/drm/nouveau/core/engine/disp/outp.c
Normal file
137
drivers/gpu/drm/nouveau/core/engine/disp/outp.c
Normal file
@ -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;
|
59
drivers/gpu/drm/nouveau/core/engine/disp/outp.h
Normal file
59
drivers/gpu/drm/nouveau/core/engine/disp/outp.h
Normal file
@ -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
|
276
drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
Normal file
276
drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
Normal file
@ -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;
|
||||
}
|
65
drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
Normal file
65
drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
Normal file
@ -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
|
@ -32,69 +32,108 @@
|
||||
|
||||
#include "nv50.h"
|
||||
|
||||
/******************************************************************************
|
||||
* TMDS
|
||||
*****************************************************************************/
|
||||
|
||||
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(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 struct nouveau_i2c_port *
|
||||
nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp)
|
||||
|
||||
static int
|
||||
nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
{
|
||||
struct nouveau_i2c *i2c = nouveau_i2c(disp);
|
||||
return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
|
||||
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_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int pattern)
|
||||
nv50_pior_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||
{
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||
{
|
||||
struct nouveau_i2c_port *port;
|
||||
int ret = -EINVAL;
|
||||
|
||||
port = nv50_pior_dp_find(disp, outp);
|
||||
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 ret;
|
||||
return port->func->lnk_ctl(port, nr, bw, ef);
|
||||
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_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return ret;
|
||||
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;
|
||||
}
|
||||
|
||||
const struct nouveau_dp_func
|
||||
nv50_pior_dp_func = {
|
||||
static int
|
||||
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 *i2c = nouveau_i2c(parent);
|
||||
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;
|
||||
|
||||
outp->base.edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(
|
||||
outp->base.info.extdev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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)
|
||||
int
|
||||
nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||
{
|
||||
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;
|
||||
u32 mask = 0, i;
|
||||
|
||||
dpctrl |= ((1 << link_nr) - 1) << 16;
|
||||
if (enh_frame)
|
||||
dpctrl |= 0x00004000;
|
||||
if (link_bw > 0x06)
|
||||
clksor |= 0x00040000;
|
||||
for (i = 0; i < nr; i++)
|
||||
mask |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3);
|
||||
|
||||
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);
|
||||
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_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
|
||||
int head, int lane, int swing, int preem)
|
||||
nv94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||
{
|
||||
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);
|
||||
const u32 soff = nv94_sor_soff(outp);
|
||||
const u32 loff = nv94_sor_loff(outp);
|
||||
u32 dpctrl = 0x00000000;
|
||||
u32 clksor = 0x00000000;
|
||||
|
||||
dpctrl |= ((1 << nr) - 1) << 16;
|
||||
if (ef)
|
||||
dpctrl |= 0x00004000;
|
||||
if (bw > 0x06)
|
||||
clksor |= 0x00040000;
|
||||
|
||||
nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor);
|
||||
nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
||||
{
|
||||
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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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,37 +105,51 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
_nouveau_gpio_dtor(struct nouveau_object *object)
|
||||
static void
|
||||
nouveau_gpio_intr_disable(struct nouveau_event *event, int type, int index)
|
||||
{
|
||||
struct nouveau_gpio *gpio = (void *)object;
|
||||
nouveau_event_destroy(&gpio->events);
|
||||
nouveau_subdev_destroy(&gpio->base);
|
||||
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_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, int lines,
|
||||
int length, void **pobject)
|
||||
_nouveau_gpio_fini(struct nouveau_object *object, bool suspend)
|
||||
{
|
||||
struct nouveau_gpio *gpio;
|
||||
int ret;
|
||||
const struct nouveau_gpio_impl *impl = (void *)object->oclass;
|
||||
struct nouveau_gpio *gpio = nouveau_gpio(object);
|
||||
u32 mask = (1 << impl->lines) - 1;
|
||||
|
||||
ret = nouveau_subdev_create_(parent, engine, oclass, 0, "GPIO", "gpio",
|
||||
length, pobject);
|
||||
gpio = *pobject;
|
||||
if (ret)
|
||||
return ret;
|
||||
impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0);
|
||||
impl->intr_stat(gpio, &mask, &mask);
|
||||
|
||||
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;
|
||||
return 0;
|
||||
return nouveau_subdev_fini(&gpio->base, suspend);
|
||||
}
|
||||
|
||||
static struct dmi_system_id gpio_reset_ids[] = {
|
||||
@ -147,12 +164,73 @@ static struct dmi_system_id gpio_reset_ids[] = {
|
||||
};
|
||||
|
||||
int
|
||||
nouveau_gpio_init(struct nouveau_gpio *gpio)
|
||||
_nouveau_gpio_init(struct nouveau_object *object)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
struct nouveau_gpio *gpio = (void *)object;
|
||||
nouveau_event_destroy(&gpio->events);
|
||||
nouveau_subdev_destroy(&gpio->base);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_gpio_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass,
|
||||
int length, void **pobject)
|
||||
{
|
||||
const struct nouveau_gpio_impl *impl = (void *)oclass;
|
||||
struct nouveau_gpio *gpio;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_subdev_create_(parent, engine, oclass, 0, "GPIO", "gpio",
|
||||
length, pobject);
|
||||
gpio = *pobject;
|
||||
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;
|
||||
}
|
||||
|
||||
int
|
||||
_nouveau_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
||||
struct nouveau_object **pobject)
|
||||
{
|
||||
struct nouveau_gpio *gpio;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_gpio_create(parent, engine, oclass, &gpio);
|
||||
*pobject = nv_object(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -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)
|
||||
nv10_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
|
||||
{
|
||||
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);
|
||||
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_intr_enable(struct nouveau_event *event, int line)
|
||||
nv10_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
|
||||
{
|
||||
nv_wr32(event->priv, 0x001104, 0x00010001 << line);
|
||||
nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct nv10_gpio_priv *priv = (void *)object;
|
||||
nv_wr32(priv, 0x001144, 0x00000000);
|
||||
return nouveau_gpio_fini(&priv->base, suspend);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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)
|
||||
static void
|
||||
nv50_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_gpio_intr_enable(struct nouveau_event *event, int line)
|
||||
static void
|
||||
nv50_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_gpio_fini(struct nouveau_object *object, bool suspend)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
74
drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c
Normal file
74
drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c
Normal file
@ -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;
|
@ -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;
|
||||
|
@ -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);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
72
drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c
Normal file
72
drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c
Normal file
@ -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;
|
84
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c
Normal file
84
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c
Normal file
@ -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;
|
||||
}
|
58
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h
Normal file
58
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h
Normal file
@ -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
|
35
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c
Normal file
35
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c
Normal file
@ -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,
|
||||
},
|
||||
};
|
86
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c
Normal file
86
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c
Normal file
@ -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,
|
||||
},
|
||||
};
|
15
drivers/gpu/drm/nouveau/core/subdev/i2c/port.h
Normal file
15
drivers/gpu/drm/nouveau/core/subdev/i2c/port.h
Normal file
@ -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
|
85
drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h
Normal file
85
drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h
Normal file
@ -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
|
@ -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 },
|
||||
|
@ -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 },
|
||||
|
@ -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 },
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
@ -911,34 +905,104 @@ nouveau_connector_funcs_lvds = {
|
||||
.force = nouveau_connector_force
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
@ -1763,34 +1774,37 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_ctrl(struct nouveau_encoder *nv_encoder, u32 mask, u32 data)
|
||||
{
|
||||
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));
|
||||
}
|
||||
evo_kick(push, mast);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_disconnect(struct drm_encoder *encoder)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
nv50_hdmi_disconnect(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
|
||||
|
Loading…
Reference in New Issue
Block a user