Merge branch 'linux-3.18' of git://anongit.freedesktop.org/git/nouveau/linux-2.6 into drm-next

This is the main merge request for Nouveau 3.18, overview:
- various bits of roy's gt21x clock work
- various bits of kepler memory clock work (don't get too excited, there's at least one more major bit left that's busting higher freqs)
- misc fan control improvements
- kepler hdmi infoframe fixes
- dp audio
- l2 cache + cbc improvements

* 'linux-3.18' of git://anongit.freedesktop.org/git/nouveau/linux-2.6: (68 commits)
  drm/gt214-/disp: enable dp audio
  drm/gt214-/kms: fix hda eld regression
  drm/g94-/disp: calculate some dp audio constants
  drm/gt214-/kms: perform hda codec setup on displayport too
  drm/gk104-/disp: infoframe registers moved yet again on kepler
  drm/nouveau/bios: parse older ramcfg/timing data like we do newer ones
  drm/nva3/fb/ram: Per-partition regs
  drm/nouveau/fb/ram: Support strided regs
  drm/nv50/fb/ram: Store the number of partitions in the designated fields
  drm/nv50/kms: Set VBLANK time in modeset script
  drm/nouveau/bios: Add rammap support for version 1.0
  drm/gf100-/pwr/memx: block host and fifo around reclock
  drm/nouveau/pwr/memx: fix command ordering around block/unblock
  drm/nouveau/pwr/memx: rename fb off/on to block/unblock
  drm/nva3/clk: Pause the GPU before reclocking
  drm/nouveau/gpio: rename g92 class to g94
  drm/gk104-/fb/ram: move fb enable/disable to same place as nvidia
  drm/gk104/fb/ram: twiddle some more bits when reclocking
  drm/nouveau/bios: parse another large chunk of random memory config data
  drm/gk104-/fb/ram: perform certain steps only when bios data differs
  ...
This commit is contained in:
Dave Airlie 2014-09-16 06:20:53 +10:00
commit 4ac073640a
130 changed files with 5699 additions and 2525 deletions

View File

@ -38,6 +38,7 @@ nouveau-y += core/subdev/bios/dcb.o
nouveau-y += core/subdev/bios/disp.o nouveau-y += core/subdev/bios/disp.o
nouveau-y += core/subdev/bios/dp.o nouveau-y += core/subdev/bios/dp.o
nouveau-y += core/subdev/bios/extdev.o nouveau-y += core/subdev/bios/extdev.o
nouveau-y += core/subdev/bios/fan.o
nouveau-y += core/subdev/bios/gpio.o nouveau-y += core/subdev/bios/gpio.o
nouveau-y += core/subdev/bios/i2c.o nouveau-y += core/subdev/bios/i2c.o
nouveau-y += core/subdev/bios/init.o nouveau-y += core/subdev/bios/init.o
@ -51,6 +52,8 @@ nouveau-y += core/subdev/bios/therm.o
nouveau-y += core/subdev/bios/vmap.o nouveau-y += core/subdev/bios/vmap.o
nouveau-y += core/subdev/bios/volt.o nouveau-y += core/subdev/bios/volt.o
nouveau-y += core/subdev/bios/xpio.o nouveau-y += core/subdev/bios/xpio.o
nouveau-y += core/subdev/bios/M0205.o
nouveau-y += core/subdev/bios/M0209.o
nouveau-y += core/subdev/bios/P0260.o nouveau-y += core/subdev/bios/P0260.o
nouveau-y += core/subdev/bus/hwsq.o nouveau-y += core/subdev/bus/hwsq.o
nouveau-y += core/subdev/bus/nv04.o nouveau-y += core/subdev/bus/nv04.o
@ -124,12 +127,17 @@ nouveau-y += core/subdev/fb/ramnvc0.o
nouveau-y += core/subdev/fb/ramnve0.o nouveau-y += core/subdev/fb/ramnve0.o
nouveau-y += core/subdev/fb/ramgk20a.o nouveau-y += core/subdev/fb/ramgk20a.o
nouveau-y += core/subdev/fb/ramgm107.o nouveau-y += core/subdev/fb/ramgm107.o
nouveau-y += core/subdev/fb/sddr2.o
nouveau-y += core/subdev/fb/sddr3.o nouveau-y += core/subdev/fb/sddr3.o
nouveau-y += core/subdev/fb/gddr5.o nouveau-y += core/subdev/fb/gddr5.o
nouveau-y += core/subdev/fuse/base.o
nouveau-y += core/subdev/fuse/g80.o
nouveau-y += core/subdev/fuse/gf100.o
nouveau-y += core/subdev/fuse/gm107.o
nouveau-y += core/subdev/gpio/base.o nouveau-y += core/subdev/gpio/base.o
nouveau-y += core/subdev/gpio/nv10.o nouveau-y += core/subdev/gpio/nv10.o
nouveau-y += core/subdev/gpio/nv50.o nouveau-y += core/subdev/gpio/nv50.o
nouveau-y += core/subdev/gpio/nv92.o nouveau-y += core/subdev/gpio/nv94.o
nouveau-y += core/subdev/gpio/nvd0.o nouveau-y += core/subdev/gpio/nvd0.o
nouveau-y += core/subdev/gpio/nve0.o nouveau-y += core/subdev/gpio/nve0.o
nouveau-y += core/subdev/i2c/base.o nouveau-y += core/subdev/i2c/base.o
@ -190,6 +198,7 @@ nouveau-y += core/subdev/therm/nv50.o
nouveau-y += core/subdev/therm/nv84.o nouveau-y += core/subdev/therm/nv84.o
nouveau-y += core/subdev/therm/nva3.o nouveau-y += core/subdev/therm/nva3.o
nouveau-y += core/subdev/therm/nvd0.o nouveau-y += core/subdev/therm/nvd0.o
nouveau-y += core/subdev/therm/gm107.o
nouveau-y += core/subdev/timer/base.o nouveau-y += core/subdev/timer/base.o
nouveau-y += core/subdev/timer/nv04.o nouveau-y += core/subdev/timer/nv04.o
nouveau-y += core/subdev/timer/gk20a.o nouveau-y += core/subdev/timer/gk20a.o
@ -252,6 +261,7 @@ nouveau-y += core/engine/disp/hdanvd0.o
nouveau-y += core/engine/disp/hdminv84.o nouveau-y += core/engine/disp/hdminv84.o
nouveau-y += core/engine/disp/hdminva3.o nouveau-y += core/engine/disp/hdminva3.o
nouveau-y += core/engine/disp/hdminvd0.o nouveau-y += core/engine/disp/hdminvd0.o
nouveau-y += core/engine/disp/hdminve0.o
nouveau-y += core/engine/disp/piornv50.o nouveau-y += core/engine/disp/piornv50.o
nouveau-y += core/engine/disp/sornv50.o nouveau-y += core/engine/disp/sornv50.o
nouveau-y += core/engine/disp/sornv94.o nouveau-y += core/engine/disp/sornv94.o

View File

@ -91,9 +91,10 @@ nvkm_client_notify_del(struct nouveau_client *client, int index)
} }
int int
nvkm_client_notify_new(struct nouveau_client *client, nvkm_client_notify_new(struct nouveau_object *object,
struct nvkm_event *event, void *data, u32 size) struct nvkm_event *event, void *data, u32 size)
{ {
struct nouveau_client *client = nouveau_client(object);
struct nvkm_client_notify *notify; struct nvkm_client_notify *notify;
union { union {
struct nvif_notify_req_v0 v0; struct nvif_notify_req_v0 v0;
@ -127,8 +128,8 @@ nvkm_client_notify_new(struct nouveau_client *client,
} }
if (ret == 0) { if (ret == 0) {
ret = nvkm_notify_init(event, nvkm_client_notify, false, ret = nvkm_notify_init(object, event, nvkm_client_notify,
data, size, reply, &notify->n); false, data, size, reply, &notify->n);
if (ret == 0) { if (ret == 0) {
client->notify[index] = notify; client->notify[index] = notify;
notify->client = client; notify->client = client;

View File

@ -20,7 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
#include <core/os.h> #include <core/object.h>
#include <core/event.h> #include <core/event.h>
void void

View File

@ -115,7 +115,7 @@ nouveau_gpuobj_create_(struct nouveau_object *parent,
gpuobj->size = size; gpuobj->size = size;
if (heap) { if (heap) {
ret = nouveau_mm_head(heap, 1, size, size, ret = nouveau_mm_head(heap, 0, 1, size, size,
max(align, (u32)1), &gpuobj->node); max(align, (u32)1), &gpuobj->node);
if (ret) if (ret)
return ret; return ret;

View File

@ -349,7 +349,6 @@ nvkm_ioctl_unmap(struct nouveau_handle *handle, void *data, u32 size)
static int static int
nvkm_ioctl_ntfy_new(struct nouveau_handle *handle, void *data, u32 size) nvkm_ioctl_ntfy_new(struct nouveau_handle *handle, void *data, u32 size)
{ {
struct nouveau_client *client = nouveau_client(handle->object);
struct nouveau_object *object = handle->object; struct nouveau_object *object = handle->object;
struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs; struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs;
union { union {
@ -365,7 +364,7 @@ nvkm_ioctl_ntfy_new(struct nouveau_handle *handle, void *data, u32 size)
if (ret = -ENODEV, ofuncs->ntfy) if (ret = -ENODEV, ofuncs->ntfy)
ret = ofuncs->ntfy(object, args->v0.event, &event); ret = ofuncs->ntfy(object, args->v0.event, &event);
if (ret == 0) { if (ret == 0) {
ret = nvkm_client_notify_new(client, event, data, size); ret = nvkm_client_notify_new(object, event, data, size);
if (ret >= 0) { if (ret >= 0) {
args->v0.index = ret; args->v0.index = ret;
ret = 0; ret = 0;

View File

@ -28,6 +28,24 @@
#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ #define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry) list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
static void
nouveau_mm_dump(struct nouveau_mm *mm, const char *header)
{
struct nouveau_mm_node *node;
printk(KERN_ERR "nouveau: %s\n", header);
printk(KERN_ERR "nouveau: node list:\n");
list_for_each_entry(node, &mm->nodes, nl_entry) {
printk(KERN_ERR "nouveau: \t%08x %08x %d\n",
node->offset, node->length, node->type);
}
printk(KERN_ERR "nouveau: free list:\n");
list_for_each_entry(node, &mm->free, fl_entry) {
printk(KERN_ERR "nouveau: \t%08x %08x %d\n",
node->offset, node->length, node->type);
}
}
void void
nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis) nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis)
{ {
@ -37,29 +55,29 @@ nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis)
struct nouveau_mm_node *prev = node(this, prev); struct nouveau_mm_node *prev = node(this, prev);
struct nouveau_mm_node *next = node(this, next); struct nouveau_mm_node *next = node(this, next);
if (prev && prev->type == 0) { if (prev && prev->type == NVKM_MM_TYPE_NONE) {
prev->length += this->length; prev->length += this->length;
list_del(&this->nl_entry); list_del(&this->nl_entry);
kfree(this); this = prev; kfree(this); this = prev;
} }
if (next && next->type == 0) { if (next && next->type == NVKM_MM_TYPE_NONE) {
next->offset = this->offset; next->offset = this->offset;
next->length += this->length; next->length += this->length;
if (this->type == 0) if (this->type == NVKM_MM_TYPE_NONE)
list_del(&this->fl_entry); list_del(&this->fl_entry);
list_del(&this->nl_entry); list_del(&this->nl_entry);
kfree(this); this = NULL; kfree(this); this = NULL;
} }
if (this && this->type != 0) { if (this && this->type != NVKM_MM_TYPE_NONE) {
list_for_each_entry(prev, &mm->free, fl_entry) { list_for_each_entry(prev, &mm->free, fl_entry) {
if (this->offset < prev->offset) if (this->offset < prev->offset)
break; break;
} }
list_add_tail(&this->fl_entry, &prev->fl_entry); list_add_tail(&this->fl_entry, &prev->fl_entry);
this->type = 0; this->type = NVKM_MM_TYPE_NONE;
} }
} }
@ -80,27 +98,32 @@ region_head(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
b->offset = a->offset; b->offset = a->offset;
b->length = size; b->length = size;
b->heap = a->heap;
b->type = a->type; b->type = a->type;
a->offset += size; a->offset += size;
a->length -= size; a->length -= size;
list_add_tail(&b->nl_entry, &a->nl_entry); list_add_tail(&b->nl_entry, &a->nl_entry);
if (b->type == 0) if (b->type == NVKM_MM_TYPE_NONE)
list_add_tail(&b->fl_entry, &a->fl_entry); list_add_tail(&b->fl_entry, &a->fl_entry);
return b; return b;
} }
int int
nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, nouveau_mm_head(struct nouveau_mm *mm, u8 heap, u8 type, u32 size_max,
u32 align, struct nouveau_mm_node **pnode) u32 size_min, u32 align, struct nouveau_mm_node **pnode)
{ {
struct nouveau_mm_node *prev, *this, *next; struct nouveau_mm_node *prev, *this, *next;
u32 mask = align - 1; u32 mask = align - 1;
u32 splitoff; u32 splitoff;
u32 s, e; u32 s, e;
BUG_ON(!type); BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE);
list_for_each_entry(this, &mm->free, fl_entry) { list_for_each_entry(this, &mm->free, fl_entry) {
if (unlikely(heap != NVKM_MM_HEAP_ANY)) {
if (this->heap != heap)
continue;
}
e = this->offset + this->length; e = this->offset + this->length;
s = this->offset; s = this->offset;
@ -149,27 +172,32 @@ region_tail(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
a->length -= size; a->length -= size;
b->offset = a->offset + a->length; b->offset = a->offset + a->length;
b->length = size; b->length = size;
b->heap = a->heap;
b->type = a->type; b->type = a->type;
list_add(&b->nl_entry, &a->nl_entry); list_add(&b->nl_entry, &a->nl_entry);
if (b->type == 0) if (b->type == NVKM_MM_TYPE_NONE)
list_add(&b->fl_entry, &a->fl_entry); list_add(&b->fl_entry, &a->fl_entry);
return b; return b;
} }
int int
nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, nouveau_mm_tail(struct nouveau_mm *mm, u8 heap, u8 type, u32 size_max,
u32 align, struct nouveau_mm_node **pnode) u32 size_min, u32 align, struct nouveau_mm_node **pnode)
{ {
struct nouveau_mm_node *prev, *this, *next; struct nouveau_mm_node *prev, *this, *next;
u32 mask = align - 1; u32 mask = align - 1;
BUG_ON(!type); BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE);
list_for_each_entry_reverse(this, &mm->free, fl_entry) { list_for_each_entry_reverse(this, &mm->free, fl_entry) {
u32 e = this->offset + this->length; u32 e = this->offset + this->length;
u32 s = this->offset; u32 s = this->offset;
u32 c = 0, a; u32 c = 0, a;
if (unlikely(heap != NVKM_MM_HEAP_ANY)) {
if (this->heap != heap)
continue;
}
prev = node(this, prev); prev = node(this, prev);
if (prev && prev->type != type) if (prev && prev->type != type)
@ -209,9 +237,23 @@ nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
int int
nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
{ {
struct nouveau_mm_node *node; struct nouveau_mm_node *node, *prev;
u32 next;
if (block) { if (nouveau_mm_initialised(mm)) {
prev = list_last_entry(&mm->nodes, typeof(*node), nl_entry);
next = prev->offset + prev->length;
if (next != offset) {
BUG_ON(next > offset);
if (!(node = kzalloc(sizeof(*node), GFP_KERNEL)))
return -ENOMEM;
node->type = NVKM_MM_TYPE_HOLE;
node->offset = next;
node->length = offset - next;
list_add_tail(&node->nl_entry, &mm->nodes);
}
BUG_ON(block != mm->block_size);
} else {
INIT_LIST_HEAD(&mm->nodes); INIT_LIST_HEAD(&mm->nodes);
INIT_LIST_HEAD(&mm->free); INIT_LIST_HEAD(&mm->free);
mm->block_size = block; mm->block_size = block;
@ -230,25 +272,32 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
list_add_tail(&node->nl_entry, &mm->nodes); list_add_tail(&node->nl_entry, &mm->nodes);
list_add_tail(&node->fl_entry, &mm->free); list_add_tail(&node->fl_entry, &mm->free);
mm->heap_nodes++; node->heap = ++mm->heap_nodes;
return 0; return 0;
} }
int int
nouveau_mm_fini(struct nouveau_mm *mm) nouveau_mm_fini(struct nouveau_mm *mm)
{ {
if (nouveau_mm_initialised(mm)) { struct nouveau_mm_node *node, *temp;
struct nouveau_mm_node *node, *heap = int nodes = 0;
list_first_entry(&mm->nodes, typeof(*heap), nl_entry);
int nodes = 0;
list_for_each_entry(node, &mm->nodes, nl_entry) { if (!nouveau_mm_initialised(mm))
if (WARN_ON(nodes++ == mm->heap_nodes)) return 0;
list_for_each_entry(node, &mm->nodes, nl_entry) {
if (node->type != NVKM_MM_TYPE_HOLE) {
if (++nodes > mm->heap_nodes) {
nouveau_mm_dump(mm, "mm not clean!");
return -EBUSY; return -EBUSY;
}
} }
kfree(heap);
} }
list_for_each_entry_safe(node, temp, &mm->nodes, nl_entry) {
list_del(&node->nl_entry);
kfree(node);
}
mm->heap_nodes = 0;
return 0; return 0;
} }

View File

@ -134,14 +134,15 @@ nvkm_notify_fini(struct nvkm_notify *notify)
} }
int int
nvkm_notify_init(struct nvkm_event *event, int (*func)(struct nvkm_notify *), nvkm_notify_init(struct nouveau_object *object, struct nvkm_event *event,
bool work, void *data, u32 size, u32 reply, int (*func)(struct nvkm_notify *), bool work,
void *data, u32 size, u32 reply,
struct nvkm_notify *notify) struct nvkm_notify *notify)
{ {
unsigned long flags; unsigned long flags;
int ret = -ENODEV; int ret = -ENODEV;
if ((notify->event = event), event->refs) { if ((notify->event = event), event->refs) {
ret = event->func->ctor(data, size, notify); ret = event->func->ctor(object, data, size, notify);
if (ret == 0 && (ret = -EINVAL, notify->size == reply)) { if (ret == 0 && (ret = -EINVAL, notify->size == reply)) {
notify->flags = 0; notify->flags = 0;
notify->block = 1; notify->block = 1;

View File

@ -505,7 +505,8 @@ nouveau_device_sclass[] = {
}; };
static int static int
nouveau_device_event_ctor(void *data, u32 size, struct nvkm_notify *notify) nouveau_device_event_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{ {
if (!WARN_ON(size != 0)) { if (!WARN_ON(size != 0)) {
notify->size = 0; notify->size = 0;

View File

@ -26,6 +26,7 @@
#include <subdev/bus.h> #include <subdev/bus.h>
#include <subdev/gpio.h> #include <subdev/gpio.h>
#include <subdev/i2c.h> #include <subdev/i2c.h>
#include <subdev/fuse.h>
#include <subdev/clock.h> #include <subdev/clock.h>
#include <subdev/therm.h> #include <subdev/therm.h>
#include <subdev/mxm.h> #include <subdev/mxm.h>
@ -62,10 +63,9 @@ gm100_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
#if 0 device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
#endif
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = gm107_devinit_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = gm107_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass;
@ -77,8 +77,9 @@ gm100_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
#if 0
device->oclass[NVDEV_SUBDEV_PWR ] = nv108_pwr_oclass; device->oclass[NVDEV_SUBDEV_PWR ] = nv108_pwr_oclass;
#if 0
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
#endif #endif
device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;

View File

@ -26,6 +26,7 @@
#include <subdev/bus.h> #include <subdev/bus.h>
#include <subdev/gpio.h> #include <subdev/gpio.h>
#include <subdev/i2c.h> #include <subdev/i2c.h>
#include <subdev/fuse.h>
#include <subdev/clock.h> #include <subdev/clock.h>
#include <subdev/therm.h> #include <subdev/therm.h>
#include <subdev/mxm.h> #include <subdev/mxm.h>
@ -62,6 +63,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv50_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -87,6 +89,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -115,6 +118,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -141,8 +145,9 @@ nv50_identify(struct nouveau_device *device)
case 0x92: case 0x92:
device->cname = "G92"; device->cname = "G92";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -169,8 +174,9 @@ nv50_identify(struct nouveau_device *device)
case 0x94: case 0x94:
device->cname = "G94"; device->cname = "G94";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -197,8 +203,9 @@ nv50_identify(struct nouveau_device *device)
case 0x96: case 0x96:
device->cname = "G96"; device->cname = "G96";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -225,8 +232,9 @@ nv50_identify(struct nouveau_device *device)
case 0x98: case 0x98:
device->cname = "G98"; device->cname = "G98";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -253,8 +261,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa0: case 0xa0:
device->cname = "G200"; device->cname = "G200";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -281,8 +290,9 @@ nv50_identify(struct nouveau_device *device)
case 0xaa: case 0xaa:
device->cname = "MCP77/MCP78"; device->cname = "MCP77/MCP78";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -309,8 +319,9 @@ nv50_identify(struct nouveau_device *device)
case 0xac: case 0xac:
device->cname = "MCP79/MCP7A"; device->cname = "MCP79/MCP7A";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -337,8 +348,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa3: case 0xa3:
device->cname = "GT215"; device->cname = "GT215";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -367,8 +379,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa5: case 0xa5:
device->cname = "GT216"; device->cname = "GT216";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -396,8 +409,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa8: case 0xa8:
device->cname = "GT218"; device->cname = "GT218";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -425,8 +439,9 @@ nv50_identify(struct nouveau_device *device)
case 0xaf: case 0xaf:
device->cname = "MCP89"; device->cname = "MCP89";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;

View File

@ -26,6 +26,7 @@
#include <subdev/bus.h> #include <subdev/bus.h>
#include <subdev/gpio.h> #include <subdev/gpio.h>
#include <subdev/i2c.h> #include <subdev/i2c.h>
#include <subdev/fuse.h>
#include <subdev/clock.h> #include <subdev/clock.h>
#include <subdev/therm.h> #include <subdev/therm.h>
#include <subdev/mxm.h> #include <subdev/mxm.h>
@ -60,8 +61,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc0: case 0xc0:
device->cname = "GF100"; device->cname = "GF100";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -92,8 +94,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc4: case 0xc4:
device->cname = "GF104"; device->cname = "GF104";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -124,8 +127,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc3: case 0xc3:
device->cname = "GF106"; device->cname = "GF106";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -155,8 +159,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xce: case 0xce:
device->cname = "GF114"; device->cname = "GF114";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -187,8 +192,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xcf: case 0xcf:
device->cname = "GF116"; device->cname = "GF116";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -219,8 +225,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc1: case 0xc1:
device->cname = "GF108"; device->cname = "GF108";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -250,8 +257,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc8: case 0xc8:
device->cname = "GF110"; device->cname = "GF110";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -284,6 +292,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -315,6 +324,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = gf117_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = gf117_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;

View File

@ -26,6 +26,7 @@
#include <subdev/bus.h> #include <subdev/bus.h>
#include <subdev/gpio.h> #include <subdev/gpio.h>
#include <subdev/i2c.h> #include <subdev/i2c.h>
#include <subdev/fuse.h>
#include <subdev/clock.h> #include <subdev/clock.h>
#include <subdev/therm.h> #include <subdev/therm.h>
#include <subdev/mxm.h> #include <subdev/mxm.h>
@ -62,6 +63,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -95,6 +97,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -128,6 +131,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -161,6 +165,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_CLOCK ] = &gk20a_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &gk20a_clock_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass;
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = gk20a_fb_oclass; device->oclass[NVDEV_SUBDEV_FB ] = gk20a_fb_oclass;
device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
@ -180,6 +185,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -213,6 +219,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@ -246,6 +253,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;

View File

@ -32,7 +32,8 @@
#include "conn.h" #include "conn.h"
int int
nouveau_disp_vblank_ctor(void *data, u32 size, struct nvkm_notify *notify) nouveau_disp_vblank_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{ {
struct nouveau_disp *disp = struct nouveau_disp *disp =
container_of(notify->event, typeof(*disp), vblank); container_of(notify->event, typeof(*disp), vblank);
@ -61,7 +62,8 @@ nouveau_disp_vblank(struct nouveau_disp *disp, int head)
} }
static int static int
nouveau_disp_hpd_ctor(void *data, u32 size, struct nvkm_notify *notify) nouveau_disp_hpd_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{ {
struct nouveau_disp *disp = struct nouveau_disp *disp =
container_of(notify->event, typeof(*disp), hpd); container_of(notify->event, typeof(*disp), hpd);

View File

@ -126,8 +126,8 @@ nvkm_connector_create_(struct nouveau_object *parent,
return 0; return 0;
} }
ret = nvkm_notify_init(&gpio->event, nvkm_connector_hpd, true, ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd,
&(struct nvkm_gpio_ntfy_req) { true, &(struct nvkm_gpio_ntfy_req) {
.mask = NVKM_GPIO_TOGGLED, .mask = NVKM_GPIO_TOGGLED,
.line = func.line, .line = func.line,
}, },

View File

@ -68,6 +68,10 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = gm107_disp_base_oclass; nv_engine(priv)->sclass = gm107_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr; nv_subdev(priv)->intr = nvd0_disp_intr;
@ -80,7 +84,7 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense; priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power; priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl; priv->sor.hdmi = nve0_hdmi_ctrl;
return 0; return 0;
} }

View File

@ -26,6 +26,8 @@
#include <nvif/unpack.h> #include <nvif/unpack.h>
#include <nvif/class.h> #include <nvif/class.h>
#include <subdev/timer.h>
#include "nv50.h" #include "nv50.h"
int int
@ -46,16 +48,21 @@ nva3_hda_eld(NV50_DISP_MTHD_V1)
return ret; return ret;
if (size && args->v0.data[0]) { if (size && args->v0.data[0]) {
if (outp->info.type == DCB_OUTPUT_DP) {
nv_mask(priv, 0x61c1e0 + soff, 0x8000000d, 0x80000001);
nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000);
}
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
nv_wr32(priv, 0x61c440 + soff, (i << 8) | args->v0.data[0]); nv_wr32(priv, 0x61c440 + soff, (i << 8) | args->v0.data[0]);
for (; i < 0x60; i++) for (; i < 0x60; i++)
nv_wr32(priv, 0x61c440 + soff, (i << 8)); nv_wr32(priv, 0x61c440 + soff, (i << 8));
nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000003); nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000003);
} else
if (size) {
nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000001);
} else { } else {
nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000); if (outp->info.type == DCB_OUTPUT_DP) {
nv_mask(priv, 0x61c1e0 + soff, 0x80000001, 0x80000000);
nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000);
}
nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size);
} }
return 0; return 0;

View File

@ -26,10 +26,7 @@
#include <nvif/unpack.h> #include <nvif/unpack.h>
#include <nvif/class.h> #include <nvif/class.h>
#include <subdev/bios.h> #include <subdev/timer.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/dp.h>
#include <subdev/bios/init.h>
#include "nv50.h" #include "nv50.h"
@ -40,6 +37,7 @@ nvd0_hda_eld(NV50_DISP_MTHD_V1)
struct nv50_disp_sor_hda_eld_v0 v0; struct nv50_disp_sor_hda_eld_v0 v0;
} *args = data; } *args = data;
const u32 soff = outp->or * 0x030; const u32 soff = outp->or * 0x030;
const u32 hoff = head * 0x800;
int ret, i; int ret, i;
nv_ioctl(object, "disp sor hda eld size %d\n", size); nv_ioctl(object, "disp sor hda eld size %d\n", size);
@ -51,16 +49,22 @@ nvd0_hda_eld(NV50_DISP_MTHD_V1)
return ret; return ret;
if (size && args->v0.data[0]) { if (size && args->v0.data[0]) {
if (outp->info.type == DCB_OUTPUT_DP) {
nv_mask(priv, 0x616618 + hoff, 0x8000000c, 0x80000001);
nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000);
}
nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000);
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
nv_wr32(priv, 0x10ec00 + soff, (i << 8) | args->v0.data[i]); nv_wr32(priv, 0x10ec00 + soff, (i << 8) | args->v0.data[i]);
for (; i < 0x60; i++) for (; i < 0x60; i++)
nv_wr32(priv, 0x10ec00 + soff, (i << 8)); nv_wr32(priv, 0x10ec00 + soff, (i << 8));
nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000003); nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000003);
} else
if (size) {
nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000001);
} else { } else {
nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000); if (outp->info.type == DCB_OUTPUT_DP) {
nv_mask(priv, 0x616618 + hoff, 0x80000001, 0x80000000);
nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000);
}
nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size);
} }
return 0; return 0;

View File

@ -75,8 +75,5 @@ nvd0_hdmi_ctrl(NV50_DISP_MTHD_V1)
/* HDMI_CTRL */ /* HDMI_CTRL */
nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl); nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl);
/* NFI, audio doesn't work without it though.. */
nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000);
return 0; return 0;
} }

View File

@ -0,0 +1,83 @@
/*
* 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 <core/client.h>
#include <nvif/unpack.h>
#include <nvif/class.h>
#include "nv50.h"
int
nve0_hdmi_ctrl(NV50_DISP_MTHD_V1)
{
const u32 hoff = (head * 0x800);
const u32 hdmi = (head * 0x400);
union {
struct nv50_disp_sor_hdmi_pwr_v0 v0;
} *args = data;
u32 ctrl;
int ret;
nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
if (nvif_unpack(args->v0, 0, 0, false)) {
nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
"max_ac_packet %d rekey %d\n",
args->v0.version, args->v0.state,
args->v0.max_ac_packet, args->v0.rekey);
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
return -EINVAL;
ctrl = 0x40000000 * !!args->v0.state;
ctrl |= args->v0.max_ac_packet << 16;
ctrl |= args->v0.rekey;
} else
return ret;
if (!(ctrl & 0x40000000)) {
nv_mask(priv, 0x616798 + hoff, 0x40000000, 0x00000000);
nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000);
return 0;
}
/* AVI InfoFrame */
nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000);
nv_wr32(priv, 0x690008 + hdmi, 0x000d0282);
nv_wr32(priv, 0x69000c + hdmi, 0x0000006f);
nv_wr32(priv, 0x690010 + hdmi, 0x00000000);
nv_wr32(priv, 0x690014 + hdmi, 0x00000000);
nv_wr32(priv, 0x690018 + hdmi, 0x00000000);
nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000001);
/* ??? InfoFrame? */
nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
nv_wr32(priv, 0x6900cc + hdmi, 0x00000010);
nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000001);
/* ??? */
nv_wr32(priv, 0x690080 + hdmi, 0x82000000);
/* HDMI_CTRL */
nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl);
return 0;
}

View File

@ -29,6 +29,7 @@
#include <core/enum.h> #include <core/enum.h>
#include <nvif/unpack.h> #include <nvif/unpack.h>
#include <nvif/class.h> #include <nvif/class.h>
#include <nvif/event.h>
#include <subdev/bios.h> #include <subdev/bios.h>
#include <subdev/bios/dcb.h> #include <subdev/bios/dcb.h>
@ -82,6 +83,71 @@ nv50_disp_chan_destroy(struct nv50_disp_chan *chan)
nouveau_namedb_destroy(&chan->base); nouveau_namedb_destroy(&chan->base);
} }
static void
nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index)
{
struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000000 << index);
}
static void
nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index)
{
struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000001 << index);
}
void
nv50_disp_chan_uevent_send(struct nv50_disp_priv *priv, int chid)
{
struct nvif_notify_uevent_rep {
} rep;
nvkm_event_send(&priv->uevent, 1, chid, &rep, sizeof(rep));
}
int
nv50_disp_chan_uevent_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{
struct nv50_disp_dmac *dmac = (void *)object;
union {
struct nvif_notify_uevent_req none;
} *args = data;
int ret;
if (nvif_unvers(args->none)) {
notify->size = sizeof(struct nvif_notify_uevent_rep);
notify->types = 1;
notify->index = dmac->base.chid;
return 0;
}
return ret;
}
const struct nvkm_event_func
nv50_disp_chan_uevent = {
.ctor = nv50_disp_chan_uevent_ctor,
.init = nv50_disp_chan_uevent_init,
.fini = nv50_disp_chan_uevent_fini,
};
int
nv50_disp_chan_ntfy(struct nouveau_object *object, u32 type,
struct nvkm_event **pevent)
{
struct nv50_disp_priv *priv = (void *)object->engine;
switch (type) {
case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT:
*pevent = &priv->uevent;
return 0;
default:
break;
}
return -EINVAL;
}
int int
nv50_disp_chan_map(struct nouveau_object *object, u64 *addr, u32 *size) nv50_disp_chan_map(struct nouveau_object *object, u64 *addr, u32 *size)
{ {
@ -195,7 +261,7 @@ nv50_disp_dmac_init(struct nouveau_object *object)
return ret; return ret;
/* enable error reporting */ /* enable error reporting */
nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid); nv_mask(priv, 0x610028, 0x00010000 << chid, 0x00010000 << chid);
/* initialise channel for dma command submission */ /* initialise channel for dma command submission */
nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push); nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push);
@ -232,7 +298,7 @@ nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend)
return -EBUSY; return -EBUSY;
} }
/* disable error reporting */ /* disable error reporting and completion notifications */
nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid); nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid);
return nv50_disp_chan_fini(&dmac->base, suspend); return nv50_disp_chan_fini(&dmac->base, suspend);
@ -454,7 +520,7 @@ nv50_disp_mast_init(struct nouveau_object *object)
return ret; return ret;
/* enable error reporting */ /* enable error reporting */
nv_mask(priv, 0x610028, 0x00010001, 0x00010001); nv_mask(priv, 0x610028, 0x00010000, 0x00010000);
/* attempt to unstick channel from some unknown state */ /* attempt to unstick channel from some unknown state */
if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000) if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000)
@ -494,7 +560,7 @@ nv50_disp_mast_fini(struct nouveau_object *object, bool suspend)
return -EBUSY; return -EBUSY;
} }
/* disable error reporting */ /* disable error reporting and completion notifications */
nv_mask(priv, 0x610028, 0x00010001, 0x00000000); nv_mask(priv, 0x610028, 0x00010001, 0x00000000);
return nv50_disp_chan_fini(&mast->base, suspend); return nv50_disp_chan_fini(&mast->base, suspend);
@ -507,6 +573,7 @@ nv50_disp_mast_ofuncs = {
.base.init = nv50_disp_mast_init, .base.init = nv50_disp_mast_init,
.base.fini = nv50_disp_mast_fini, .base.fini = nv50_disp_mast_fini,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.ntfy = nv50_disp_chan_ntfy,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
.chid = 0, .chid = 0,
@ -607,6 +674,7 @@ nv50_disp_sync_ofuncs = {
.base.dtor = nv50_disp_dmac_dtor, .base.dtor = nv50_disp_dmac_dtor,
.base.init = nv50_disp_dmac_init, .base.init = nv50_disp_dmac_init,
.base.fini = nv50_disp_dmac_fini, .base.fini = nv50_disp_dmac_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -696,6 +764,7 @@ nv50_disp_ovly_ofuncs = {
.base.dtor = nv50_disp_dmac_dtor, .base.dtor = nv50_disp_dmac_dtor,
.base.init = nv50_disp_dmac_init, .base.init = nv50_disp_dmac_init,
.base.fini = nv50_disp_dmac_fini, .base.fini = nv50_disp_dmac_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -813,6 +882,7 @@ nv50_disp_oimm_ofuncs = {
.base.dtor = nv50_disp_pioc_dtor, .base.dtor = nv50_disp_pioc_dtor,
.base.init = nv50_disp_pioc_init, .base.init = nv50_disp_pioc_init,
.base.fini = nv50_disp_pioc_fini, .base.fini = nv50_disp_pioc_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -860,6 +930,7 @@ nv50_disp_curs_ofuncs = {
.base.dtor = nv50_disp_pioc_dtor, .base.dtor = nv50_disp_pioc_dtor,
.base.init = nv50_disp_pioc_init, .base.init = nv50_disp_pioc_init,
.base.fini = nv50_disp_pioc_fini, .base.fini = nv50_disp_pioc_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -1559,7 +1630,7 @@ nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head)
} }
static void static void
nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, int head,
struct dcb_output *outp, u32 pclk) struct dcb_output *outp, u32 pclk)
{ {
const int link = !(outp->sorconf.link & 1); const int link = !(outp->sorconf.link & 1);
@ -1568,24 +1639,36 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
const u32 loff = (link * 0x080) + soff; const u32 loff = (link * 0x080) + soff;
const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8)); const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8));
const u32 symbol = 100000; const u32 symbol = 100000;
u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x0000f0000; const s32 vactive = nv_rd32(priv, 0x610af8 + (head * 0x540)) & 0xffff;
const s32 vblanke = nv_rd32(priv, 0x610ae8 + (head * 0x540)) & 0xffff;
const s32 vblanks = nv_rd32(priv, 0x610af0 + (head * 0x540)) & 0xffff;
u32 dpctrl = nv_rd32(priv, 0x61c10c + loff);
u32 clksor = nv_rd32(priv, 0x614300 + soff); u32 clksor = nv_rd32(priv, 0x614300 + soff);
int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
int TU, VTUi, VTUf, VTUa; int TU, VTUi, VTUf, VTUa;
u64 link_data_rate, link_ratio, unk; u64 link_data_rate, link_ratio, unk;
u32 best_diff = 64 * symbol; u32 best_diff = 64 * symbol;
u32 link_nr, link_bw, bits; u32 link_nr, link_bw, bits;
u64 value;
/* calculate packed data rate for each lane */ link_bw = (clksor & 0x000c0000) ? 270000 : 162000;
if (dpctrl > 0x00030000) link_nr = 4; link_nr = hweight32(dpctrl & 0x000f0000);
else if (dpctrl > 0x00010000) link_nr = 2;
else link_nr = 1;
if (clksor & 0x000c0000) /* symbols/hblank - algorithm taken from comments in tegra driver */
link_bw = 270000; value = vblanke + vactive - vblanks - 7;
else value = value * link_bw;
link_bw = 162000; do_div(value, pclk);
value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
nv_mask(priv, 0x61c1e8 + soff, 0x0000ffff, value);
/* symbols/vblank - algorithm taken from comments in tegra driver */
value = vblanks - vblanke - 25;
value = value * link_bw;
do_div(value, pclk);
value = value - ((36 / link_nr) + 3) - 1;
nv_mask(priv, 0x61c1ec + soff, 0x00ffffff, value);
/* watermark / activesym */
if ((ctrl & 0xf0000) == 0x60000) bits = 30; if ((ctrl & 0xf0000) == 0x60000) bits = 30;
else if ((ctrl & 0xf0000) == 0x50000) bits = 24; else if ((ctrl & 0xf0000) == 0x50000) bits = 24;
else bits = 18; else bits = 18;
@ -1731,7 +1814,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
} else } else
if (!outp->info.location) { if (!outp->info.location) {
if (outp->info.type == DCB_OUTPUT_DP) if (outp->info.type == DCB_OUTPUT_DP)
nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk); nv50_disp_intr_unk20_2_dp(priv, head, &outp->info, pclk);
oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
hval = 0x00000000; hval = 0x00000000;
@ -1846,6 +1929,12 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
intr0 &= ~(0x00010000 << chid); intr0 &= ~(0x00010000 << chid);
} }
while (intr0 & 0x0000001f) {
u32 chid = __ffs(intr0 & 0x0000001f);
nv50_disp_chan_uevent_send(priv, chid);
intr0 &= ~(0x00000001 << chid);
}
if (intr1 & 0x00000004) { if (intr1 & 0x00000004) {
nouveau_disp_vblank(&priv->base, 0); nouveau_disp_vblank(&priv->base, 0);
nv_wr32(priv, 0x610024, 0x00000004); nv_wr32(priv, 0x610024, 0x00000004);
@ -1880,6 +1969,10 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nv50_disp_base_oclass; nv_engine(priv)->sclass = nv50_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr; nv_subdev(priv)->intr = nv50_disp_intr;

View File

@ -26,6 +26,8 @@ struct nv50_disp_priv {
struct work_struct supervisor; struct work_struct supervisor;
u32 super; u32 super;
struct nvkm_event uevent;
struct { struct {
int nr; int nr;
} head; } head;
@ -75,6 +77,7 @@ int nvd0_hda_eld(NV50_DISP_MTHD_V1);
int nv84_hdmi_ctrl(NV50_DISP_MTHD_V1); int nv84_hdmi_ctrl(NV50_DISP_MTHD_V1);
int nva3_hdmi_ctrl(NV50_DISP_MTHD_V1); int nva3_hdmi_ctrl(NV50_DISP_MTHD_V1);
int nvd0_hdmi_ctrl(NV50_DISP_MTHD_V1); int nvd0_hdmi_ctrl(NV50_DISP_MTHD_V1);
int nve0_hdmi_ctrl(NV50_DISP_MTHD_V1);
int nv50_sor_power(NV50_DISP_MTHD_V1); int nv50_sor_power(NV50_DISP_MTHD_V1);
@ -116,9 +119,16 @@ struct nv50_disp_chan {
int chid; int chid;
}; };
int nv50_disp_chan_ntfy(struct nouveau_object *, u32, struct nvkm_event **);
int nv50_disp_chan_map(struct nouveau_object *, u64 *, u32 *); int nv50_disp_chan_map(struct nouveau_object *, u64 *, u32 *);
u32 nv50_disp_chan_rd32(struct nouveau_object *, u64); u32 nv50_disp_chan_rd32(struct nouveau_object *, u64);
void nv50_disp_chan_wr32(struct nouveau_object *, u64, u32); void nv50_disp_chan_wr32(struct nouveau_object *, u64, u32);
extern const struct nvkm_event_func nv50_disp_chan_uevent;
int nv50_disp_chan_uevent_ctor(struct nouveau_object *, void *, u32,
struct nvkm_notify *);
void nv50_disp_chan_uevent_send(struct nv50_disp_priv *, int);
extern const struct nvkm_event_func nvd0_disp_chan_uevent;
#define nv50_disp_chan_init(a) \ #define nv50_disp_chan_init(a) \
nouveau_namedb_init(&(a)->base) nouveau_namedb_init(&(a)->base)

View File

@ -236,6 +236,10 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nv84_disp_base_oclass; nv_engine(priv)->sclass = nv84_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr; nv_subdev(priv)->intr = nv50_disp_intr;

View File

@ -95,6 +95,10 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nv94_disp_base_oclass; nv_engine(priv)->sclass = nv94_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr; nv_subdev(priv)->intr = nv50_disp_intr;

View File

@ -112,6 +112,10 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nva0_disp_base_oclass; nv_engine(priv)->sclass = nva0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr; nv_subdev(priv)->intr = nv50_disp_intr;

View File

@ -67,6 +67,10 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nva3_disp_base_oclass; nv_engine(priv)->sclass = nva3_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr; nv_subdev(priv)->intr = nv50_disp_intr;

View File

@ -42,6 +42,31 @@
#include "nv50.h" #include "nv50.h"
/*******************************************************************************
* EVO channel base class
******************************************************************************/
static void
nvd0_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index)
{
struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000000 << index);
}
static void
nvd0_disp_chan_uevent_init(struct nvkm_event *event, int types, int index)
{
struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000001 << index);
}
const struct nvkm_event_func
nvd0_disp_chan_uevent = {
.ctor = nv50_disp_chan_uevent_ctor,
.init = nvd0_disp_chan_uevent_init,
.fini = nvd0_disp_chan_uevent_fini,
};
/******************************************************************************* /*******************************************************************************
* EVO DMA channel base class * EVO DMA channel base class
******************************************************************************/ ******************************************************************************/
@ -77,7 +102,6 @@ nvd0_disp_dmac_init(struct nouveau_object *object)
return ret; return ret;
/* enable error reporting */ /* enable error reporting */
nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
/* initialise channel for dma command submission */ /* initialise channel for dma command submission */
@ -115,7 +139,7 @@ nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend)
return -EBUSY; return -EBUSY;
} }
/* disable error reporting */ /* disable error reporting and completion notification */
nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
@ -278,7 +302,6 @@ nvd0_disp_mast_init(struct nouveau_object *object)
return ret; return ret;
/* enable error reporting */ /* enable error reporting */
nv_mask(priv, 0x610090, 0x00000001, 0x00000001);
nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001); nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001);
/* initialise channel for dma command submission */ /* initialise channel for dma command submission */
@ -313,7 +336,7 @@ nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend)
return -EBUSY; return -EBUSY;
} }
/* disable error reporting */ /* disable error reporting and completion notification */
nv_mask(priv, 0x610090, 0x00000001, 0x00000000); nv_mask(priv, 0x610090, 0x00000001, 0x00000000);
nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000);
@ -326,6 +349,7 @@ nvd0_disp_mast_ofuncs = {
.base.dtor = nv50_disp_dmac_dtor, .base.dtor = nv50_disp_dmac_dtor,
.base.init = nvd0_disp_mast_init, .base.init = nvd0_disp_mast_init,
.base.fini = nvd0_disp_mast_fini, .base.fini = nvd0_disp_mast_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -419,6 +443,7 @@ nvd0_disp_sync_ofuncs = {
.base.dtor = nv50_disp_dmac_dtor, .base.dtor = nv50_disp_dmac_dtor,
.base.init = nvd0_disp_dmac_init, .base.init = nvd0_disp_dmac_init,
.base.fini = nvd0_disp_dmac_fini, .base.fini = nvd0_disp_dmac_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -499,6 +524,7 @@ nvd0_disp_ovly_ofuncs = {
.base.dtor = nv50_disp_dmac_dtor, .base.dtor = nv50_disp_dmac_dtor,
.base.init = nvd0_disp_dmac_init, .base.init = nvd0_disp_dmac_init,
.base.fini = nvd0_disp_dmac_fini, .base.fini = nvd0_disp_dmac_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -524,7 +550,6 @@ nvd0_disp_pioc_init(struct nouveau_object *object)
return ret; return ret;
/* enable error reporting */ /* enable error reporting */
nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
/* activate channel */ /* activate channel */
@ -553,7 +578,7 @@ nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend)
return -EBUSY; return -EBUSY;
} }
/* disable error reporting */ /* disable error reporting and completion notification */
nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
@ -570,6 +595,7 @@ nvd0_disp_oimm_ofuncs = {
.base.dtor = nv50_disp_pioc_dtor, .base.dtor = nv50_disp_pioc_dtor,
.base.init = nvd0_disp_pioc_init, .base.init = nvd0_disp_pioc_init,
.base.fini = nvd0_disp_pioc_fini, .base.fini = nvd0_disp_pioc_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -586,6 +612,7 @@ nvd0_disp_curs_ofuncs = {
.base.dtor = nv50_disp_pioc_dtor, .base.dtor = nv50_disp_pioc_dtor,
.base.init = nvd0_disp_pioc_init, .base.init = nvd0_disp_pioc_init,
.base.fini = nvd0_disp_pioc_fini, .base.fini = nvd0_disp_pioc_fini,
.base.ntfy = nv50_disp_chan_ntfy,
.base.map = nv50_disp_chan_map, .base.map = nv50_disp_chan_map,
.base.rd32 = nv50_disp_chan_rd32, .base.rd32 = nv50_disp_chan_rd32,
.base.wr32 = nv50_disp_chan_wr32, .base.wr32 = nv50_disp_chan_wr32,
@ -949,6 +976,9 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
const int or = ffs(outp->or) - 1; const int or = ffs(outp->or) - 1;
const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020)); const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020));
const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300)); const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300));
const s32 vactive = nv_rd32(priv, 0x660414 + (head * 0x300)) & 0xffff;
const s32 vblanke = nv_rd32(priv, 0x66041c + (head * 0x300)) & 0xffff;
const s32 vblanks = nv_rd32(priv, 0x660420 + (head * 0x300)) & 0xffff;
const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
const u32 hoff = (head * 0x800); const u32 hoff = (head * 0x800);
@ -956,23 +986,35 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
const u32 loff = (link * 0x080) + soff; const u32 loff = (link * 0x080) + soff;
const u32 symbol = 100000; const u32 symbol = 100000;
const u32 TU = 64; const u32 TU = 64;
u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000; u32 dpctrl = nv_rd32(priv, 0x61c10c + loff);
u32 clksor = nv_rd32(priv, 0x612300 + soff); u32 clksor = nv_rd32(priv, 0x612300 + soff);
u32 datarate, link_nr, link_bw, bits; u32 datarate, link_nr, link_bw, bits;
u64 ratio, value; u64 ratio, value;
link_nr = hweight32(dpctrl & 0x000f0000);
link_bw = (clksor & 0x007c0000) >> 18;
link_bw *= 27000;
/* symbols/hblank - algorithm taken from comments in tegra driver */
value = vblanke + vactive - vblanks - 7;
value = value * link_bw;
do_div(value, pclk);
value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
nv_mask(priv, 0x616620 + hoff, 0x0000ffff, value);
/* symbols/vblank - algorithm taken from comments in tegra driver */
value = vblanks - vblanke - 25;
value = value * link_bw;
do_div(value, pclk);
value = value - ((36 / link_nr) + 3) - 1;
nv_mask(priv, 0x616624 + hoff, 0x00ffffff, value);
/* watermark */
if ((conf & 0x3c0) == 0x180) bits = 30; if ((conf & 0x3c0) == 0x180) bits = 30;
else if ((conf & 0x3c0) == 0x140) bits = 24; else if ((conf & 0x3c0) == 0x140) bits = 24;
else bits = 18; else bits = 18;
datarate = (pclk * bits) / 8; datarate = (pclk * bits) / 8;
if (dpctrl > 0x00030000) link_nr = 4;
else if (dpctrl > 0x00010000) link_nr = 2;
else link_nr = 1;
link_bw = (clksor & 0x007c0000) >> 18;
link_bw *= 27000;
ratio = datarate; ratio = datarate;
ratio *= symbol; ratio *= symbol;
do_div(ratio, link_nr * link_bw); do_div(ratio, link_nr * link_bw);
@ -1153,7 +1195,11 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
if (intr & 0x00000001) { if (intr & 0x00000001) {
u32 stat = nv_rd32(priv, 0x61008c); u32 stat = nv_rd32(priv, 0x61008c);
nv_wr32(priv, 0x61008c, stat); while (stat) {
int chid = __ffs(stat); stat &= ~(1 << chid);
nv50_disp_chan_uevent_send(priv, chid);
nv_wr32(priv, 0x61008c, 1 << chid);
}
intr &= ~0x00000001; intr &= ~0x00000001;
} }
@ -1209,6 +1255,10 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nvd0_disp_base_oclass; nv_engine(priv)->sclass = nvd0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr; nv_subdev(priv)->intr = nvd0_disp_intr;

View File

@ -233,6 +233,10 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nve0_disp_base_oclass; nv_engine(priv)->sclass = nve0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr; nv_subdev(priv)->intr = nvd0_disp_intr;
@ -245,7 +249,7 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense; priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power; priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl; priv->sor.hdmi = nve0_hdmi_ctrl;
return 0; return 0;
} }

View File

@ -68,6 +68,10 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
if (ret)
return ret;
nv_engine(priv)->sclass = nvf0_disp_base_oclass; nv_engine(priv)->sclass = nvf0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass; nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr; nv_subdev(priv)->intr = nvd0_disp_intr;
@ -80,7 +84,7 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense; priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power; priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld; priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl; priv->sor.hdmi = nve0_hdmi_ctrl;
return 0; return 0;
} }

View File

@ -254,7 +254,7 @@ nvkm_output_dp_create_(struct nouveau_object *parent,
atomic_set(&outp->lt.done, 0); atomic_set(&outp->lt.done, 0);
/* link maintenance */ /* link maintenance */
ret = nvkm_notify_init(&i2c->event, nvkm_output_dp_irq, true, ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true,
&(struct nvkm_i2c_ntfy_req) { &(struct nvkm_i2c_ntfy_req) {
.mask = NVKM_I2C_IRQ, .mask = NVKM_I2C_IRQ,
.port = outp->base.edid->index, .port = outp->base.edid->index,
@ -268,7 +268,7 @@ nvkm_output_dp_create_(struct nouveau_object *parent,
} }
/* hotplug detect, replaces gpio-based mechanism with aux events */ /* hotplug detect, replaces gpio-based mechanism with aux events */
ret = nvkm_notify_init(&i2c->event, nvkm_output_dp_hpd, true, ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true,
&(struct nvkm_i2c_ntfy_req) { &(struct nvkm_i2c_ntfy_req) {
.mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
.port = outp->base.edid->index, .port = outp->base.edid->index,

View File

@ -40,7 +40,8 @@ int _nouveau_disp_fini(struct nouveau_object *, bool);
extern struct nouveau_oclass *nvkm_output_oclass; extern struct nouveau_oclass *nvkm_output_oclass;
extern struct nouveau_oclass *nvkm_connector_oclass; extern struct nouveau_oclass *nvkm_connector_oclass;
int nouveau_disp_vblank_ctor(void *data, u32 size, struct nvkm_notify *); int nouveau_disp_vblank_ctor(struct nouveau_object *, void *data, u32 size,
struct nvkm_notify *);
void nouveau_disp_vblank(struct nouveau_disp *, int head); void nouveau_disp_vblank(struct nouveau_disp *, int head);
int nouveau_disp_ntfy(struct nouveau_object *, u32, struct nvkm_event **); int nouveau_disp_ntfy(struct nouveau_object *, u32, struct nvkm_event **);

View File

@ -34,7 +34,8 @@
#include <engine/fifo.h> #include <engine/fifo.h>
static int static int
nouveau_fifo_event_ctor(void *data, u32 size, struct nvkm_notify *notify) nouveau_fifo_event_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{ {
if (size == 0) { if (size == 0) {
notify->size = 0; notify->size = 0;
@ -170,7 +171,8 @@ _nouveau_fifo_channel_wr32(struct nouveau_object *object, u64 addr, u32 data)
} }
int int
nouveau_fifo_uevent_ctor(void *data, u32 size, struct nvkm_notify *notify) nouveau_fifo_uevent_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{ {
union { union {
struct nvif_notify_uevent_req none; struct nvif_notify_uevent_req none;

View File

@ -175,7 +175,8 @@ nv50_software_context_ctor(struct nouveau_object *parent,
return ret; return ret;
for (i = 0; pdisp && i < pdisp->vblank.index_nr; i++) { for (i = 0; pdisp && i < pdisp->vblank.index_nr; i++) {
ret = nvkm_notify_init(&pdisp->vblank, pclass->vblank, false, ret = nvkm_notify_init(NULL, &pdisp->vblank, pclass->vblank,
false,
&(struct nvif_notify_head_req_v0) { &(struct nvif_notify_head_req_v0) {
.head = i, .head = i,
}, },

View File

@ -48,7 +48,7 @@ int nouveau_client_init(struct nouveau_client *);
int nouveau_client_fini(struct nouveau_client *, bool suspend); int nouveau_client_fini(struct nouveau_client *, bool suspend);
const char *nouveau_client_name(void *obj); const char *nouveau_client_name(void *obj);
int nvkm_client_notify_new(struct nouveau_client *, struct nvkm_event *, int nvkm_client_notify_new(struct nouveau_object *, struct nvkm_event *,
void *data, u32 size); void *data, u32 size);
int nvkm_client_notify_del(struct nouveau_client *, int index); int nvkm_client_notify_del(struct nouveau_client *, int index);
int nvkm_client_notify_get(struct nouveau_client *, int index); int nvkm_client_notify_get(struct nouveau_client *, int index);

View File

@ -24,6 +24,7 @@ enum nv_subdev_type {
* been created, and are allowed to assume any subdevs in the * been created, and are allowed to assume any subdevs in the
* list above them exist and have been initialised. * list above them exist and have been initialised.
*/ */
NVDEV_SUBDEV_FUSE,
NVDEV_SUBDEV_MXM, NVDEV_SUBDEV_MXM,
NVDEV_SUBDEV_MC, NVDEV_SUBDEV_MC,
NVDEV_SUBDEV_BUS, NVDEV_SUBDEV_BUS,

View File

@ -4,7 +4,8 @@
#include <core/notify.h> #include <core/notify.h>
struct nvkm_event_func { struct nvkm_event_func {
int (*ctor)(void *data, u32 size, struct nvkm_notify *); int (*ctor)(struct nouveau_object *, void *data, u32 size,
struct nvkm_notify *);
void (*send)(void *data, u32 size, struct nvkm_notify *); void (*send)(void *data, u32 size, struct nvkm_notify *);
void (*init)(struct nvkm_event *, int type, int index); void (*init)(struct nvkm_event *, int type, int index);
void (*fini)(struct nvkm_event *, int type, int index); void (*fini)(struct nvkm_event *, int type, int index);

View File

@ -6,6 +6,10 @@ struct nouveau_mm_node {
struct list_head fl_entry; struct list_head fl_entry;
struct list_head rl_entry; struct list_head rl_entry;
#define NVKM_MM_HEAP_ANY 0x00
u8 heap;
#define NVKM_MM_TYPE_NONE 0x00
#define NVKM_MM_TYPE_HOLE 0xff
u8 type; u8 type;
u32 offset; u32 offset;
u32 length; u32 length;
@ -27,10 +31,10 @@ nouveau_mm_initialised(struct nouveau_mm *mm)
int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block); int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block);
int nouveau_mm_fini(struct nouveau_mm *); int nouveau_mm_fini(struct nouveau_mm *);
int nouveau_mm_head(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min, int nouveau_mm_head(struct nouveau_mm *, u8 heap, u8 type, u32 size_max,
u32 align, struct nouveau_mm_node **); u32 size_min, u32 align, struct nouveau_mm_node **);
int nouveau_mm_tail(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min, int nouveau_mm_tail(struct nouveau_mm *, u8 heap, u8 type, u32 size_max,
u32 align, struct nouveau_mm_node **); u32 size_min, u32 align, struct nouveau_mm_node **);
void nouveau_mm_free(struct nouveau_mm *, struct nouveau_mm_node **); void nouveau_mm_free(struct nouveau_mm *, struct nouveau_mm_node **);
#endif #endif

View File

@ -25,8 +25,9 @@ struct nvkm_notify {
const void *data; const void *data;
}; };
int nvkm_notify_init(struct nvkm_event *, int (*func)(struct nvkm_notify *), int nvkm_notify_init(struct nouveau_object *, struct nvkm_event *,
bool work, void *data, u32 size, u32 reply, int (*func)(struct nvkm_notify *), bool work,
void *data, u32 size, u32 reply,
struct nvkm_notify *); struct nvkm_notify *);
void nvkm_notify_fini(struct nvkm_notify *); void nvkm_notify_fini(struct nvkm_notify *);
void nvkm_notify_get(struct nvkm_notify *); void nvkm_notify_get(struct nvkm_notify *);

View File

@ -116,7 +116,8 @@ extern struct nouveau_oclass *nve0_fifo_oclass;
extern struct nouveau_oclass *gk20a_fifo_oclass; extern struct nouveau_oclass *gk20a_fifo_oclass;
extern struct nouveau_oclass *nv108_fifo_oclass; extern struct nouveau_oclass *nv108_fifo_oclass;
int nouveau_fifo_uevent_ctor(void *, u32, struct nvkm_notify *); int nouveau_fifo_uevent_ctor(struct nouveau_object *, void *, u32,
struct nvkm_notify *);
void nouveau_fifo_uevent(struct nouveau_fifo *); void nouveau_fifo_uevent(struct nouveau_fifo *);
void nv04_fifo_intr(struct nouveau_subdev *); void nv04_fifo_intr(struct nouveau_subdev *);

View File

@ -12,7 +12,6 @@ struct nouveau_bar {
int (*alloc)(struct nouveau_bar *, struct nouveau_object *, int (*alloc)(struct nouveau_bar *, struct nouveau_object *,
struct nouveau_mem *, struct nouveau_object **); struct nouveau_mem *, struct nouveau_object **);
void __iomem *iomem;
int (*kmap)(struct nouveau_bar *, struct nouveau_mem *, int (*kmap)(struct nouveau_bar *, struct nouveau_mem *,
u32 flags, struct nouveau_vma *); u32 flags, struct nouveau_vma *);

View File

@ -0,0 +1,32 @@
#ifndef __NVBIOS_M0205_H__
#define __NVBIOS_M0205_H__
struct nvbios_M0205T {
u16 freq;
};
u32 nvbios_M0205Te(struct nouveau_bios *,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz);
u32 nvbios_M0205Tp(struct nouveau_bios *,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz,
struct nvbios_M0205T *);
struct nvbios_M0205E {
u8 type;
};
u32 nvbios_M0205Ee(struct nouveau_bios *, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
u32 nvbios_M0205Ep(struct nouveau_bios *, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_M0205E *);
struct nvbios_M0205S {
u8 data;
};
u32 nvbios_M0205Se(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr);
u32 nvbios_M0205Sp(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr,
struct nvbios_M0205S *);
#endif

View File

@ -0,0 +1,30 @@
#ifndef __NVBIOS_M0209_H__
#define __NVBIOS_M0209_H__
u32 nvbios_M0209Te(struct nouveau_bios *,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz);
struct nvbios_M0209E {
u8 v00_40;
u8 bits;
u8 modulo;
u8 v02_40;
u8 v02_07;
u8 v03;
};
u32 nvbios_M0209Ee(struct nouveau_bios *, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
u32 nvbios_M0209Ep(struct nouveau_bios *, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_M0209E *);
struct nvbios_M0209S {
u32 data[0x200];
};
u32 nvbios_M0209Se(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr);
u32 nvbios_M0209Sp(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr,
struct nvbios_M0209S *);
#endif

View File

@ -0,0 +1,8 @@
#ifndef __NVBIOS_FAN_H__
#define __NVBIOS_FAN_H__
#include <subdev/bios/therm.h>
u16 nvbios_fan_parse(struct nouveau_bios *bios, struct nvbios_therm_fan *fan);
#endif

View File

@ -4,60 +4,118 @@
struct nouveau_bios; struct nouveau_bios;
struct nvbios_ramcfg { struct nvbios_ramcfg {
unsigned rammap_11_08_01:1; unsigned rammap_ver;
unsigned rammap_11_08_0c:2; unsigned rammap_hdr;
unsigned rammap_11_08_10:1; unsigned rammap_min;
unsigned rammap_11_11_0c:2; unsigned rammap_max;
union {
struct {
unsigned rammap_10_04_02:1;
unsigned rammap_10_04_08:1;
};
struct {
unsigned rammap_11_08_01:1;
unsigned rammap_11_08_0c:2;
unsigned rammap_11_08_10:1;
unsigned rammap_11_09_01ff:9;
unsigned rammap_11_0a_03fe:9;
unsigned rammap_11_0a_0400:1;
unsigned rammap_11_0a_0800:1;
unsigned rammap_11_0b_01f0:5;
unsigned rammap_11_0b_0200:1;
unsigned rammap_11_0b_0400:1;
unsigned rammap_11_0b_0800:1;
unsigned rammap_11_0d:8;
unsigned rammap_11_0e:8;
unsigned rammap_11_0f:8;
unsigned rammap_11_11_0c:2;
};
};
unsigned ramcfg_11_01_01:1; unsigned ramcfg_ver;
unsigned ramcfg_11_01_02:1; unsigned ramcfg_hdr;
unsigned ramcfg_11_01_04:1; unsigned ramcfg_timing;
unsigned ramcfg_11_01_08:1; union {
unsigned ramcfg_11_01_10:1; struct {
unsigned ramcfg_11_01_20:1; unsigned ramcfg_10_02_01:1;
unsigned ramcfg_11_01_40:1; unsigned ramcfg_10_02_02:1;
unsigned ramcfg_11_01_80:1; unsigned ramcfg_10_02_04:1;
unsigned ramcfg_11_02_03:2; unsigned ramcfg_10_02_08:1;
unsigned ramcfg_11_02_04:1; unsigned ramcfg_10_02_10:1;
unsigned ramcfg_11_02_08:1; unsigned ramcfg_10_02_20:1;
unsigned ramcfg_11_02_10:1; unsigned ramcfg_10_02_40:1;
unsigned ramcfg_11_02_40:1; unsigned ramcfg_10_03_0f:4;
unsigned ramcfg_11_02_80:1; unsigned ramcfg_10_05:8;
unsigned ramcfg_11_03_0f:4; unsigned ramcfg_10_06:8;
unsigned ramcfg_11_03_30:2; unsigned ramcfg_10_07:8;
unsigned ramcfg_11_03_c0:2; unsigned ramcfg_10_08:8;
unsigned ramcfg_11_03_f0:4; unsigned ramcfg_10_09_0f:4;
unsigned ramcfg_11_04:8; unsigned ramcfg_10_09_f0:4;
unsigned ramcfg_11_06:8; };
unsigned ramcfg_11_07_02:1; struct {
unsigned ramcfg_11_07_04:1; unsigned ramcfg_11_01_01:1;
unsigned ramcfg_11_07_08:1; unsigned ramcfg_11_01_02:1;
unsigned ramcfg_11_07_10:1; unsigned ramcfg_11_01_04:1;
unsigned ramcfg_11_07_40:1; unsigned ramcfg_11_01_08:1;
unsigned ramcfg_11_07_80:1; unsigned ramcfg_11_01_10:1;
unsigned ramcfg_11_08_01:1; unsigned ramcfg_11_01_20:1;
unsigned ramcfg_11_08_02:1; unsigned ramcfg_11_01_40:1;
unsigned ramcfg_11_08_04:1; unsigned ramcfg_11_01_80:1;
unsigned ramcfg_11_08_08:1; unsigned ramcfg_11_02_03:2;
unsigned ramcfg_11_08_10:1; unsigned ramcfg_11_02_04:1;
unsigned ramcfg_11_08_20:1; unsigned ramcfg_11_02_08:1;
unsigned ramcfg_11_09:8; unsigned ramcfg_11_02_10:1;
unsigned ramcfg_11_02_40:1;
unsigned ramcfg_11_02_80:1;
unsigned ramcfg_11_03_0f:4;
unsigned ramcfg_11_03_30:2;
unsigned ramcfg_11_03_c0:2;
unsigned ramcfg_11_03_f0:4;
unsigned ramcfg_11_04:8;
unsigned ramcfg_11_06:8;
unsigned ramcfg_11_07_02:1;
unsigned ramcfg_11_07_04:1;
unsigned ramcfg_11_07_08:1;
unsigned ramcfg_11_07_10:1;
unsigned ramcfg_11_07_40:1;
unsigned ramcfg_11_07_80:1;
unsigned ramcfg_11_08_01:1;
unsigned ramcfg_11_08_02:1;
unsigned ramcfg_11_08_04:1;
unsigned ramcfg_11_08_08:1;
unsigned ramcfg_11_08_10:1;
unsigned ramcfg_11_08_20:1;
unsigned ramcfg_11_09:8;
};
};
unsigned timing_ver;
unsigned timing_hdr;
unsigned timing[11]; unsigned timing[11];
unsigned timing_20_2e_03:2; union {
unsigned timing_20_2e_30:2; struct {
unsigned timing_20_2e_c0:2; unsigned timing_10_WR:8;
unsigned timing_20_2f_03:2; unsigned timing_10_CL:8;
unsigned timing_20_2c_003f:6; unsigned timing_10_ODT:3;
unsigned timing_20_2c_1fc0:7; unsigned timing_10_CWL:8;
unsigned timing_20_30_f8:5; };
unsigned timing_20_30_07:3; struct {
unsigned timing_20_31_0007:3; unsigned timing_20_2e_03:2;
unsigned timing_20_31_0078:4; unsigned timing_20_2e_30:2;
unsigned timing_20_31_0780:4; unsigned timing_20_2e_c0:2;
unsigned timing_20_31_0800:1; unsigned timing_20_2f_03:2;
unsigned timing_20_31_7000:3; unsigned timing_20_2c_003f:6;
unsigned timing_20_31_8000:1; unsigned timing_20_2c_1fc0:7;
unsigned timing_20_30_f8:5;
unsigned timing_20_30_07:3;
unsigned timing_20_31_0007:3;
unsigned timing_20_31_0078:4;
unsigned timing_20_31_0780:4;
unsigned timing_20_31_0800:1;
unsigned timing_20_31_7000:3;
unsigned timing_20_31_8000:1;
};
};
}; };
u8 nvbios_ramcfg_count(struct nouveau_bios *); u8 nvbios_ramcfg_count(struct nouveau_bios *);

View File

@ -8,9 +8,10 @@ u32 nvbios_rammapTe(struct nouveau_bios *, u8 *ver, u8 *hdr,
u32 nvbios_rammapEe(struct nouveau_bios *, int idx, u32 nvbios_rammapEe(struct nouveau_bios *, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len); u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
u32 nvbios_rammapEp(struct nouveau_bios *, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_ramcfg *);
u32 nvbios_rammapEm(struct nouveau_bios *, u16 mhz, u32 nvbios_rammapEm(struct nouveau_bios *, u16 mhz,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
u32 nvbios_rammapEp(struct nouveau_bios *, u16 mhz,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_ramcfg *); struct nvbios_ramcfg *);

View File

@ -23,6 +23,12 @@ struct nvbios_therm_sensor {
struct nvbios_therm_threshold thrs_shutdown; struct nvbios_therm_threshold thrs_shutdown;
}; };
enum nvbios_therm_fan_type {
NVBIOS_THERM_FAN_UNK = 0,
NVBIOS_THERM_FAN_TOGGLE = 1,
NVBIOS_THERM_FAN_PWM = 2,
};
/* no vbios have more than 6 */ /* no vbios have more than 6 */
#define NOUVEAU_TEMP_FAN_TRIP_MAX 10 #define NOUVEAU_TEMP_FAN_TRIP_MAX 10
struct nouveau_therm_trip_point { struct nouveau_therm_trip_point {
@ -38,7 +44,9 @@ enum nvbios_therm_fan_mode {
}; };
struct nvbios_therm_fan { struct nvbios_therm_fan {
u16 pwm_freq; enum nvbios_therm_fan_type type;
u32 pwm_freq;
u8 min_duty; u8 min_duty;
u8 max_duty; u8 max_duty;

View File

@ -29,6 +29,7 @@ enum nv_clk_src {
nv_clk_src_mdiv, nv_clk_src_mdiv,
nv_clk_src_core, nv_clk_src_core,
nv_clk_src_core_intm,
nv_clk_src_shader, nv_clk_src_shader,
nv_clk_src_mem, nv_clk_src_mem,

View File

@ -111,6 +111,7 @@ extern struct nouveau_oclass *gm107_fb_oclass;
#include <subdev/bios/ramcfg.h> #include <subdev/bios/ramcfg.h>
struct nouveau_ram_data { struct nouveau_ram_data {
struct list_head head;
struct nvbios_ramcfg bios; struct nvbios_ramcfg bios;
u32 freq; u32 freq;
}; };
@ -136,6 +137,7 @@ struct nouveau_ram {
int ranks; int ranks;
int parts; int parts;
int part_mask;
int (*get)(struct nouveau_fb *, u64 size, u32 align, int (*get)(struct nouveau_fb *, u64 size, u32 align,
u32 size_nc, u32 type, struct nouveau_mem **); u32 size_nc, u32 type, struct nouveau_mem **);
@ -144,11 +146,6 @@ struct nouveau_ram {
int (*calc)(struct nouveau_fb *, u32 freq); int (*calc)(struct nouveau_fb *, u32 freq);
int (*prog)(struct nouveau_fb *); int (*prog)(struct nouveau_fb *);
void (*tidy)(struct nouveau_fb *); void (*tidy)(struct nouveau_fb *);
struct {
u8 version;
u32 data;
u8 size;
} rammap, ramcfg, timing;
u32 freq; u32 freq;
u32 mr[16]; u32 mr[16];
u32 mr1_nuts; u32 mr1_nuts;

View File

@ -0,0 +1,21 @@
#ifndef __NOUVEAU_FB_REGS_04_H__
#define __NOUVEAU_FB_REGS_04_H__
#define NV04_PFB_BOOT_0 0x00100000
# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
#endif

View File

@ -0,0 +1,30 @@
#ifndef __NOUVEAU_FUSE_H__
#define __NOUVEAU_FUSE_H__
#include <core/subdev.h>
#include <core/device.h>
struct nouveau_fuse {
struct nouveau_subdev base;
};
static inline struct nouveau_fuse *
nouveau_fuse(void *obj)
{
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_FUSE];
}
#define nouveau_fuse_create(p, e, o, d) \
nouveau_fuse_create_((p), (e), (o), sizeof(**d), (void **)d)
int nouveau_fuse_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, int, void **);
void _nouveau_fuse_dtor(struct nouveau_object *);
int _nouveau_fuse_init(struct nouveau_object *);
#define _nouveau_fuse_fini _nouveau_subdev_fini
extern struct nouveau_oclass g80_fuse_oclass;
extern struct nouveau_oclass gf100_fuse_oclass;
extern struct nouveau_oclass gm107_fuse_oclass;
#endif

View File

@ -40,7 +40,7 @@ nouveau_gpio(void *obj)
extern struct nouveau_oclass *nv10_gpio_oclass; extern struct nouveau_oclass *nv10_gpio_oclass;
extern struct nouveau_oclass *nv50_gpio_oclass; extern struct nouveau_oclass *nv50_gpio_oclass;
extern struct nouveau_oclass *nv92_gpio_oclass; extern struct nouveau_oclass *nv94_gpio_oclass;
extern struct nouveau_oclass *nvd0_gpio_oclass; extern struct nouveau_oclass *nvd0_gpio_oclass;
extern struct nouveau_oclass *nve0_gpio_oclass; extern struct nouveau_oclass *nve0_gpio_oclass;

View File

@ -47,5 +47,8 @@ void nouveau_memx_wr32(struct nouveau_memx *, u32 addr, u32 data);
void nouveau_memx_wait(struct nouveau_memx *, void nouveau_memx_wait(struct nouveau_memx *,
u32 addr, u32 mask, u32 data, u32 nsec); u32 addr, u32 mask, u32 data, u32 nsec);
void nouveau_memx_nsec(struct nouveau_memx *, u32 nsec); void nouveau_memx_nsec(struct nouveau_memx *, u32 nsec);
void nouveau_memx_wait_vblank(struct nouveau_memx *);
void nouveau_memx_block(struct nouveau_memx *);
void nouveau_memx_unblock(struct nouveau_memx *);
#endif #endif

View File

@ -78,5 +78,6 @@ extern struct nouveau_oclass nv50_therm_oclass;
extern struct nouveau_oclass nv84_therm_oclass; extern struct nouveau_oclass nv84_therm_oclass;
extern struct nouveau_oclass nva3_therm_oclass; extern struct nouveau_oclass nva3_therm_oclass;
extern struct nouveau_oclass nvd0_therm_oclass; extern struct nouveau_oclass nvd0_therm_oclass;
extern struct nouveau_oclass gm107_therm_oclass;
#endif #endif

View File

@ -38,10 +38,12 @@ struct nouveau_barobj {
static int static int
nouveau_barobj_ctor(struct nouveau_object *parent, nouveau_barobj_ctor(struct nouveau_object *parent,
struct nouveau_object *engine, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *mem, u32 size, struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject) struct nouveau_object **pobject)
{ {
struct nouveau_device *device = nv_device(parent);
struct nouveau_bar *bar = (void *)engine; struct nouveau_bar *bar = (void *)engine;
struct nouveau_mem *mem = data;
struct nouveau_barobj *barobj; struct nouveau_barobj *barobj;
int ret; int ret;
@ -54,7 +56,13 @@ nouveau_barobj_ctor(struct nouveau_object *parent,
if (ret) if (ret)
return ret; return ret;
barobj->iomem = bar->iomem + (u32)barobj->vma.offset; barobj->iomem = ioremap(nv_device_resource_start(device, 3) +
(u32)barobj->vma.offset, mem->size << 12);
if (!barobj->iomem) {
nv_warn(bar, "PRAMIN ioremap failed\n");
return -ENOMEM;
}
return 0; return 0;
} }
@ -63,8 +71,11 @@ nouveau_barobj_dtor(struct nouveau_object *object)
{ {
struct nouveau_bar *bar = (void *)object->engine; struct nouveau_bar *bar = (void *)object->engine;
struct nouveau_barobj *barobj = (void *)object; struct nouveau_barobj *barobj = (void *)object;
if (barobj->vma.node) if (barobj->vma.node) {
if (barobj->iomem)
iounmap(barobj->iomem);
bar->unmap(bar, &barobj->vma); bar->unmap(bar, &barobj->vma);
}
nouveau_object_destroy(&barobj->base); nouveau_object_destroy(&barobj->base);
} }
@ -99,12 +110,11 @@ nouveau_bar_alloc(struct nouveau_bar *bar, struct nouveau_object *parent,
struct nouveau_mem *mem, struct nouveau_object **pobject) struct nouveau_mem *mem, struct nouveau_object **pobject)
{ {
struct nouveau_object *engine = nv_object(bar); struct nouveau_object *engine = nv_object(bar);
int ret = -ENOMEM; struct nouveau_object *gpuobj;
if (bar->iomem) { int ret = nouveau_object_ctor(parent, engine, &nouveau_barobj_oclass,
ret = nouveau_object_ctor(parent, engine, mem, 0, &gpuobj);
&nouveau_barobj_oclass, if (ret == 0)
mem, 0, pobject); *pobject = gpuobj;
}
return ret; return ret;
} }
@ -113,7 +123,6 @@ nouveau_bar_create_(struct nouveau_object *parent,
struct nouveau_object *engine, struct nouveau_object *engine,
struct nouveau_oclass *oclass, int length, void **pobject) struct nouveau_oclass *oclass, int length, void **pobject)
{ {
struct nouveau_device *device = nv_device(parent);
struct nouveau_bar *bar; struct nouveau_bar *bar;
int ret; int ret;
@ -123,21 +132,12 @@ nouveau_bar_create_(struct nouveau_object *parent,
if (ret) if (ret)
return ret; return ret;
if (nv_device_resource_len(device, 3) != 0) {
bar->iomem = ioremap(nv_device_resource_start(device, 3),
nv_device_resource_len(device, 3));
if (!bar->iomem)
nv_warn(bar, "PRAMIN ioremap failed\n");
}
return 0; return 0;
} }
void void
nouveau_bar_destroy(struct nouveau_bar *bar) nouveau_bar_destroy(struct nouveau_bar *bar)
{ {
if (bar->iomem)
iounmap(bar->iomem);
nouveau_subdev_destroy(&bar->base); nouveau_subdev_destroy(&bar->base);
} }

View File

@ -0,0 +1,136 @@
/*
* Copyright 2013 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/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/M0205.h>
u32
nvbios_M0205Te(struct nouveau_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_M;
u32 data = 0x00000000;
if (!bit_entry(bios, 'M', &bit_M)) {
if (bit_M.version == 2 && bit_M.length > 0x08)
data = nv_ro32(bios, bit_M.offset + 0x05);
if (data) {
*ver = nv_ro08(bios, data + 0x00);
switch (*ver) {
case 0x10:
*hdr = nv_ro08(bios, data + 0x01);
*len = nv_ro08(bios, data + 0x02);
*ssz = nv_ro08(bios, data + 0x03);
*snr = nv_ro08(bios, data + 0x04);
*cnt = nv_ro08(bios, data + 0x05);
return data;
default:
break;
}
}
}
return 0x00000000;
}
u32
nvbios_M0205Tp(struct nouveau_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz,
struct nvbios_M0205T *info)
{
u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, snr, ssz);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->freq = nv_ro16(bios, data + 0x06);
break;
default:
break;
}
return data;
}
u32
nvbios_M0205Ee(struct nouveau_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, &snr, &ssz);
if (data && idx < *cnt) {
data = data + *hdr + idx * (*len + (snr * ssz));
*hdr = *len;
*cnt = snr;
*len = ssz;
return data;
}
return 0x00000000;
}
u32
nvbios_M0205Ep(struct nouveau_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_M0205E *info)
{
u32 data = nvbios_M0205Ee(bios, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->type = nv_ro08(bios, data + 0x00) & 0x0f;
return data;
default:
break;
}
return 0x00000000;
}
u32
nvbios_M0205Se(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len;
u32 data = nvbios_M0205Ee(bios, ent, ver, hdr, &cnt, &len);
if (data && idx < cnt) {
data = data + *hdr + idx * len;
*hdr = len;
return data;
}
return 0x00000000;
}
u32
nvbios_M0205Sp(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
struct nvbios_M0205S *info)
{
u32 data = nvbios_M0205Se(bios, ent, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->data = nv_ro08(bios, data + 0x00);
return data;
default:
break;
}
return 0x00000000;
}

View File

@ -0,0 +1,137 @@
/*
* Copyright 2013 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/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/M0209.h>
u32
nvbios_M0209Te(struct nouveau_bios *bios,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
{
struct bit_entry bit_M;
u32 data = 0x00000000;
if (!bit_entry(bios, 'M', &bit_M)) {
if (bit_M.version == 2 && bit_M.length > 0x0c)
data = nv_ro32(bios, bit_M.offset + 0x09);
if (data) {
*ver = nv_ro08(bios, data + 0x00);
switch (*ver) {
case 0x10:
*hdr = nv_ro08(bios, data + 0x01);
*len = nv_ro08(bios, data + 0x02);
*ssz = nv_ro08(bios, data + 0x03);
*snr = 1;
*cnt = nv_ro08(bios, data + 0x04);
return data;
default:
break;
}
}
}
return 0x00000000;
}
u32
nvbios_M0209Ee(struct nouveau_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
u8 snr, ssz;
u32 data = nvbios_M0209Te(bios, ver, hdr, cnt, len, &snr, &ssz);
if (data && idx < *cnt) {
data = data + *hdr + idx * (*len + (snr * ssz));
*hdr = *len;
*cnt = snr;
*len = ssz;
return data;
}
return 0x00000000;
}
u32
nvbios_M0209Ep(struct nouveau_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_M0209E *info)
{
u32 data = nvbios_M0209Ee(bios, idx, ver, hdr, cnt, len);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
info->v00_40 = (nv_ro08(bios, data + 0x00) & 0x40) >> 6;
info->bits = nv_ro08(bios, data + 0x00) & 0x3f;
info->modulo = nv_ro08(bios, data + 0x01);
info->v02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6;
info->v02_07 = nv_ro08(bios, data + 0x02) & 0x07;
info->v03 = nv_ro08(bios, data + 0x03);
return data;
default:
break;
}
return 0x00000000;
}
u32
nvbios_M0209Se(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
{
u8 cnt, len;
u32 data = nvbios_M0209Ee(bios, ent, ver, hdr, &cnt, &len);
if (data && idx < cnt) {
data = data + *hdr + idx * len;
*hdr = len;
return data;
}
return 0x00000000;
}
u32
nvbios_M0209Sp(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
struct nvbios_M0209S *info)
{
struct nvbios_M0209E M0209E;
u8 cnt, len;
u32 data = nvbios_M0209Ep(bios, ent, ver, hdr, &cnt, &len, &M0209E);
if (data) {
u32 i, data = nvbios_M0209Se(bios, ent, idx, ver, hdr);
memset(info, 0x00, sizeof(*info));
switch (!!data * *ver) {
case 0x10:
for (i = 0; i < ARRAY_SIZE(info->data); i++) {
u32 bits = (i % M0209E.modulo) * M0209E.bits;
u32 mask = (1ULL << M0209E.bits) - 1;
u16 off = bits / 8;
u8 mod = bits % 8;
info->data[i] = nv_ro32(bios, data + off);
info->data[i] = info->data[i] >> mod;
info->data[i] = info->data[i] & mask;
}
return data;
default:
break;
}
}
return 0x00000000;
}

View File

@ -124,6 +124,7 @@ dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
struct dcb_output *outp) struct dcb_output *outp)
{ {
u16 dcb = dcb_outp(bios, idx, ver, len); u16 dcb = dcb_outp(bios, idx, ver, len);
memset(outp, 0x00, sizeof(*outp));
if (dcb) { if (dcb) {
if (*ver >= 0x20) { if (*ver >= 0x20) {
u32 conn = nv_ro32(bios, dcb + 0x00); u32 conn = nv_ro32(bios, dcb + 0x00);

View File

@ -0,0 +1,93 @@
/*
* Copyright 2014 Martin Peres
*
* 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: Martin Peres
*/
#include <subdev/bios.h>
#include <subdev/bios/bit.h>
#include <subdev/bios/fan.h>
u16
nvbios_fan_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
struct bit_entry bit_P;
u16 fan = 0x0000;
if (!bit_entry(bios, 'P', &bit_P)) {
if (bit_P.version == 2 && bit_P.length >= 0x5a)
fan = nv_ro16(bios, bit_P.offset + 0x58);
if (fan) {
*ver = nv_ro08(bios, fan + 0);
switch (*ver) {
case 0x10:
*hdr = nv_ro08(bios, fan + 1);
*len = nv_ro08(bios, fan + 2);
*cnt = nv_ro08(bios, fan + 3);
return fan;
default:
break;
}
}
}
return 0x0000;
}
u16
nvbios_fan_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr,
u8 *cnt, u8 *len)
{
u16 data = nvbios_fan_table(bios, ver, hdr, cnt, len);
if (data && idx < *cnt)
return data + *hdr + (idx * (*len));
return 0x0000;
}
u16
nvbios_fan_parse(struct nouveau_bios *bios, struct nvbios_therm_fan *fan)
{
u8 ver, hdr, cnt, len;
u16 data = nvbios_fan_entry(bios, 0, &ver, &hdr, &cnt, &len);
if (data) {
u8 type = nv_ro08(bios, data + 0x00);
switch (type) {
case 0:
fan->type = NVBIOS_THERM_FAN_TOGGLE;
break;
case 1:
case 2:
/* TODO: Understand the difference between the two! */
fan->type = NVBIOS_THERM_FAN_PWM;
break;
default:
fan->type = NVBIOS_THERM_FAN_UNK;
}
fan->min_duty = nv_ro08(bios, data + 0x02);
fan->max_duty = nv_ro08(bios, data + 0x03);
fan->pwm_freq = nv_ro32(bios, data + 0x0b) & 0xffffff;
}
return data;
}

View File

@ -75,31 +75,39 @@ nvbios_rammapEe(struct nouveau_bios *bios, int idx,
} }
u32 u32
nvbios_rammapEm(struct nouveau_bios *bios, u16 khz, nvbios_rammapEp(struct nouveau_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
int idx = 0;
u32 data;
while ((data = nvbios_rammapEe(bios, idx++, ver, hdr, cnt, len))) {
if (khz >= nv_ro16(bios, data + 0x00) &&
khz <= nv_ro16(bios, data + 0x02))
break;
}
return data;
}
u32
nvbios_rammapEp(struct nouveau_bios *bios, u16 khz,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_ramcfg *p) struct nvbios_ramcfg *p)
{ {
u32 data = nvbios_rammapEm(bios, khz, ver, hdr, cnt, len); u32 data = nvbios_rammapEe(bios, idx, ver, hdr, cnt, len), temp;
memset(p, 0x00, sizeof(*p)); memset(p, 0x00, sizeof(*p));
p->rammap_ver = *ver;
p->rammap_hdr = *hdr;
switch (!!data * *ver) { switch (!!data * *ver) {
case 0x10:
p->rammap_min = nv_ro16(bios, data + 0x00);
p->rammap_max = nv_ro16(bios, data + 0x02);
p->rammap_10_04_02 = (nv_ro08(bios, data + 0x04) & 0x02) >> 1;
p->rammap_10_04_08 = (nv_ro08(bios, data + 0x04) & 0x08) >> 3;
break;
case 0x11: case 0x11:
p->rammap_min = nv_ro16(bios, data + 0x00);
p->rammap_max = nv_ro16(bios, data + 0x02);
p->rammap_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0; p->rammap_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0;
p->rammap_11_08_0c = (nv_ro08(bios, data + 0x08) & 0x0c) >> 2; p->rammap_11_08_0c = (nv_ro08(bios, data + 0x08) & 0x0c) >> 2;
p->rammap_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4; p->rammap_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4;
temp = nv_ro32(bios, data + 0x09);
p->rammap_11_09_01ff = (temp & 0x000001ff) >> 0;
p->rammap_11_0a_03fe = (temp & 0x0003fe00) >> 9;
p->rammap_11_0a_0400 = (temp & 0x00040000) >> 18;
p->rammap_11_0a_0800 = (temp & 0x00080000) >> 19;
p->rammap_11_0b_01f0 = (temp & 0x01f00000) >> 20;
p->rammap_11_0b_0200 = (temp & 0x02000000) >> 25;
p->rammap_11_0b_0400 = (temp & 0x04000000) >> 26;
p->rammap_11_0b_0800 = (temp & 0x08000000) >> 27;
p->rammap_11_0d = nv_ro08(bios, data + 0x0d);
p->rammap_11_0e = nv_ro08(bios, data + 0x0e);
p->rammap_11_0f = nv_ro08(bios, data + 0x0f);
p->rammap_11_11_0c = (nv_ro08(bios, data + 0x11) & 0x0c) >> 2; p->rammap_11_11_0c = (nv_ro08(bios, data + 0x11) & 0x0c) >> 2;
break; break;
default: default:
@ -109,6 +117,20 @@ nvbios_rammapEp(struct nouveau_bios *bios, u16 khz,
return data; return data;
} }
u32
nvbios_rammapEm(struct nouveau_bios *bios, u16 mhz,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_ramcfg *info)
{
int idx = 0;
u32 data;
while ((data = nvbios_rammapEp(bios, idx++, ver, hdr, cnt, len, info))) {
if (mhz >= info->rammap_min && mhz <= info->rammap_max)
break;
}
return data;
}
u32 u32
nvbios_rammapSe(struct nouveau_bios *bios, u32 data, nvbios_rammapSe(struct nouveau_bios *bios, u32 data,
u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx,
@ -129,8 +151,28 @@ nvbios_rammapSp(struct nouveau_bios *bios, u32 data,
u8 *ver, u8 *hdr, struct nvbios_ramcfg *p) u8 *ver, u8 *hdr, struct nvbios_ramcfg *p)
{ {
data = nvbios_rammapSe(bios, data, ever, ehdr, ecnt, elen, idx, ver, hdr); data = nvbios_rammapSe(bios, data, ever, ehdr, ecnt, elen, idx, ver, hdr);
p->ramcfg_ver = *ver;
p->ramcfg_hdr = *hdr;
switch (!!data * *ver) { switch (!!data * *ver) {
case 0x10:
p->ramcfg_timing = nv_ro08(bios, data + 0x01);
p->ramcfg_10_02_01 = (nv_ro08(bios, data + 0x02) & 0x01) >> 0;
p->ramcfg_10_02_02 = (nv_ro08(bios, data + 0x02) & 0x02) >> 1;
p->ramcfg_10_02_04 = (nv_ro08(bios, data + 0x02) & 0x04) >> 2;
p->ramcfg_10_02_08 = (nv_ro08(bios, data + 0x02) & 0x08) >> 3;
p->ramcfg_10_02_10 = (nv_ro08(bios, data + 0x02) & 0x10) >> 4;
p->ramcfg_10_02_20 = (nv_ro08(bios, data + 0x02) & 0x20) >> 5;
p->ramcfg_10_02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6;
p->ramcfg_10_03_0f = (nv_ro08(bios, data + 0x03) & 0x0f) >> 0;
p->ramcfg_10_05 = (nv_ro08(bios, data + 0x05) & 0xff) >> 0;
p->ramcfg_10_06 = (nv_ro08(bios, data + 0x06) & 0xff) >> 0;
p->ramcfg_10_07 = (nv_ro08(bios, data + 0x07) & 0xff) >> 0;
p->ramcfg_10_08 = (nv_ro08(bios, data + 0x08) & 0xff) >> 0;
p->ramcfg_10_09_0f = (nv_ro08(bios, data + 0x09) & 0x0f) >> 0;
p->ramcfg_10_09_f0 = (nv_ro08(bios, data + 0x09) & 0xf0) >> 4;
break;
case 0x11: case 0x11:
p->ramcfg_timing = nv_ro08(bios, data + 0x00);
p->ramcfg_11_01_01 = (nv_ro08(bios, data + 0x01) & 0x01) >> 0; p->ramcfg_11_01_01 = (nv_ro08(bios, data + 0x01) & 0x01) >> 0;
p->ramcfg_11_01_02 = (nv_ro08(bios, data + 0x01) & 0x02) >> 1; p->ramcfg_11_01_02 = (nv_ro08(bios, data + 0x01) & 0x02) >> 1;
p->ramcfg_11_01_04 = (nv_ro08(bios, data + 0x01) & 0x04) >> 2; p->ramcfg_11_01_04 = (nv_ro08(bios, data + 0x01) & 0x04) >> 2;

View File

@ -89,7 +89,15 @@ nvbios_timingEp(struct nouveau_bios *bios, int idx,
struct nvbios_ramcfg *p) struct nvbios_ramcfg *p)
{ {
u16 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp; u16 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp;
p->timing_ver = *ver;
p->timing_hdr = *hdr;
switch (!!data * *ver) { switch (!!data * *ver) {
case 0x10:
p->timing_10_WR = nv_ro08(bios, data + 0x00);
p->timing_10_CL = nv_ro08(bios, data + 0x02);
p->timing_10_ODT = nv_ro08(bios, data + 0x0e) & 0x07;
p->timing_10_CWL = nv_ro08(bios, data + 0x13);
break;
case 0x20: case 0x20:
p->timing[0] = nv_ro32(bios, data + 0x00); p->timing[0] = nv_ro32(bios, data + 0x00);
p->timing[1] = nv_ro32(bios, data + 0x04); p->timing[1] = nv_ro32(bios, data + 0x04);

View File

@ -573,7 +573,7 @@ nouveau_clock_create_(struct nouveau_object *parent,
clk->allow_reclock = allow_reclock; clk->allow_reclock = allow_reclock;
ret = nvkm_notify_init(&device->event, nouveau_clock_pwrsrc, true, ret = nvkm_notify_init(NULL, &device->event, nouveau_clock_pwrsrc, true,
NULL, 0, 0, &clk->pwrsrc_ntfy); NULL, 0, 0, &clk->pwrsrc_ntfy);
if (ret) if (ret)
return ret; return ret;

View File

@ -20,8 +20,10 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
* *
* Authors: Ben Skeggs * Authors: Ben Skeggs
* Roy Spliet
*/ */
#include <engine/fifo.h>
#include <subdev/bios.h> #include <subdev/bios.h>
#include <subdev/bios/pll.h> #include <subdev/bios/pll.h>
#include <subdev/timer.h> #include <subdev/timer.h>
@ -42,9 +44,17 @@ static u32
read_vco(struct nva3_clock_priv *priv, int clk) read_vco(struct nva3_clock_priv *priv, int clk)
{ {
u32 sctl = nv_rd32(priv, 0x4120 + (clk * 4)); u32 sctl = nv_rd32(priv, 0x4120 + (clk * 4));
if ((sctl & 0x00000030) != 0x00000030)
switch (sctl & 0x00000030) {
case 0x00000000:
return nv_device(priv)->crystal;
case 0x00000020:
return read_pll(priv, 0x41, 0x00e820); return read_pll(priv, 0x41, 0x00e820);
return read_pll(priv, 0x42, 0x00e8a0); case 0x00000030:
return read_pll(priv, 0x42, 0x00e8a0);
default:
return 0;
}
} }
static u32 static u32
@ -66,14 +76,25 @@ read_clk(struct nva3_clock_priv *priv, int clk, bool ignore_en)
if (!ignore_en && !(sctl & 0x00000100)) if (!ignore_en && !(sctl & 0x00000100))
return 0; return 0;
/* out_alt */
if (sctl & 0x00000400)
return 108000;
/* vco_out */
switch (sctl & 0x00003000) { switch (sctl & 0x00003000) {
case 0x00000000: case 0x00000000:
return nv_device(priv)->crystal; if (!(sctl & 0x00000200))
return nv_device(priv)->crystal;
return 0;
case 0x00002000: case 0x00002000:
if (sctl & 0x00000040) if (sctl & 0x00000040)
return 108000; return 108000;
return 100000; return 100000;
case 0x00003000: case 0x00003000:
/* vco_enable */
if (!(sctl & 0x00000001))
return 0;
sclk = read_vco(priv, clk); sclk = read_vco(priv, clk);
sdiv = ((sctl & 0x003f0000) >> 16) + 2; sdiv = ((sctl & 0x003f0000) >> 16) + 2;
return (sclk * 2) / sdiv; return (sclk * 2) / sdiv;
@ -95,7 +116,9 @@ read_pll(struct nva3_clock_priv *priv, int clk, u32 pll)
N = (coef & 0x0000ff00) >> 8; N = (coef & 0x0000ff00) >> 8;
P = (coef & 0x003f0000) >> 16; P = (coef & 0x003f0000) >> 16;
/* no post-divider on these.. */ /* no post-divider on these..
* XXX: it looks more like two post-"dividers" that
* cross each other out in the default RPLL config */
if ((pll & 0x00ff00) == 0x00e800) if ((pll & 0x00ff00) == 0x00e800)
P = 1; P = 1;
@ -114,13 +137,13 @@ static int
nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src) nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
{ {
struct nva3_clock_priv *priv = (void *)clk; struct nva3_clock_priv *priv = (void *)clk;
u32 hsrc;
switch (src) { switch (src) {
case nv_clk_src_crystal: case nv_clk_src_crystal:
return nv_device(priv)->crystal; return nv_device(priv)->crystal;
case nv_clk_src_href:
return 100000;
case nv_clk_src_core: case nv_clk_src_core:
case nv_clk_src_core_intm:
return read_pll(priv, 0x00, 0x4200); return read_pll(priv, 0x00, 0x4200);
case nv_clk_src_shader: case nv_clk_src_shader:
return read_pll(priv, 0x01, 0x4220); return read_pll(priv, 0x01, 0x4220);
@ -132,24 +155,33 @@ nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
return read_clk(priv, 0x21, false); return read_clk(priv, 0x21, false);
case nv_clk_src_daemon: case nv_clk_src_daemon:
return read_clk(priv, 0x25, false); return read_clk(priv, 0x25, false);
case nv_clk_src_host:
hsrc = (nv_rd32(priv, 0xc040) & 0x30000000) >> 28;
switch (hsrc) {
case 0:
return read_clk(priv, 0x1d, false);
case 2:
case 3:
return 277000;
default:
nv_error(clk, "unknown HOST clock source %d\n", hsrc);
return -EINVAL;
}
default: default:
nv_error(clk, "invalid clock source %d\n", src); nv_error(clk, "invalid clock source %d\n", src);
return -EINVAL; return -EINVAL;
} }
return 0;
} }
int int
nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz, nva3_clk_info(struct nouveau_clock *clock, int clk, u32 khz,
struct nva3_clock_info *info) struct nva3_clock_info *info)
{ {
struct nouveau_bios *bios = nouveau_bios(clock);
struct nva3_clock_priv *priv = (void *)clock; struct nva3_clock_priv *priv = (void *)clock;
struct nvbios_pll limits; u32 oclk, sclk, sdiv, diff;
u32 oclk, sclk, sdiv;
int P, N, M, diff;
int ret;
info->pll = 0;
info->clk = 0; info->clk = 0;
switch (khz) { switch (khz) {
@ -164,43 +196,69 @@ nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
return khz; return khz;
default: default:
sclk = read_vco(priv, clk); sclk = read_vco(priv, clk);
sdiv = min((sclk * 2) / (khz - 2999), (u32)65); sdiv = min((sclk * 2) / khz, (u32)65);
/* if the clock has a PLL attached, and we can get a within oclk = (sclk * 2) / sdiv;
* [-2, 3) MHz of a divider, we'll disable the PLL and use diff = ((khz + 3000) - oclk);
* the divider instead.
* /* When imprecise, play it safe and aim for a clock lower than
* divider can go as low as 2, limited here because NVIDIA * desired rather than higher */
* and the VBIOS on my NVA8 seem to prefer using the PLL if (diff < 0) {
* for 810MHz - is there a good reason? sdiv++;
*/
if (sdiv > 4) {
oclk = (sclk * 2) / sdiv; oclk = (sclk * 2) / sdiv;
diff = khz - oclk;
if (!pll || (diff >= -2000 && diff < 3000)) {
info->clk = (((sdiv - 2) << 16) | 0x00003100);
return oclk;
}
} }
if (!pll) /* divider can go as low as 2, limited here because NVIDIA
return -ERANGE; * and the VBIOS on my NVA8 seem to prefer using the PLL
* for 810MHz - is there a good reason?
* XXX: PLLs with refclk 810MHz? */
if (sdiv > 4) {
info->clk = (((sdiv - 2) << 16) | 0x00003100);
return oclk;
}
break; break;
} }
return -ERANGE;
}
int
nva3_pll_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
struct nva3_clock_info *info)
{
struct nouveau_bios *bios = nouveau_bios(clock);
struct nva3_clock_priv *priv = (void *)clock;
struct nvbios_pll limits;
int P, N, M, diff;
int ret;
info->pll = 0;
/* If we can get a within [-2, 3) MHz of a divider, we'll disable the
* PLL and use the divider instead. */
ret = nva3_clk_info(clock, clk, khz, info);
diff = khz - ret;
if (!pll || (diff >= -2000 && diff < 3000)) {
goto out;
}
/* Try with PLL */
ret = nvbios_pll_parse(bios, pll, &limits); ret = nvbios_pll_parse(bios, pll, &limits);
if (ret) if (ret)
return ret; return ret;
limits.refclk = read_clk(priv, clk - 0x10, true); ret = nva3_clk_info(clock, clk - 0x10, limits.refclk, info);
if (!limits.refclk) if (ret != limits.refclk)
return -EINVAL; return -EINVAL;
ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P); ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P);
if (ret >= 0) { if (ret >= 0) {
info->clk = nv_rd32(priv, 0x4120 + (clk * 4));
info->pll = (P << 16) | (N << 8) | M; info->pll = (P << 16) | (N << 8) | M;
} }
out:
info->fb_delay = max(((khz + 7566) / 15133), (u32) 18);
return ret ? ret : -ERANGE; return ret ? ret : -ERANGE;
} }
@ -208,13 +266,76 @@ static int
calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate, calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate,
int clk, u32 pll, int idx) int clk, u32 pll, int idx)
{ {
int ret = nva3_clock_info(&priv->base, clk, pll, cstate->domain[idx], int ret = nva3_pll_info(&priv->base, clk, pll, cstate->domain[idx],
&priv->eng[idx]); &priv->eng[idx]);
if (ret >= 0) if (ret >= 0)
return 0; return 0;
return ret; return ret;
} }
static int
calc_host(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate)
{
int ret = 0;
u32 kHz = cstate->domain[nv_clk_src_host];
struct nva3_clock_info *info = &priv->eng[nv_clk_src_host];
if (kHz == 277000) {
info->clk = 0;
info->host_out = NVA3_HOST_277;
return 0;
}
info->host_out = NVA3_HOST_CLK;
ret = nva3_clk_info(&priv->base, 0x1d, kHz, info);
if (ret >= 0)
return 0;
return ret;
}
int
nva3_clock_pre(struct nouveau_clock *clk, unsigned long *flags)
{
struct nouveau_fifo *pfifo = nouveau_fifo(clk);
/* halt and idle execution engines */
nv_mask(clk, 0x020060, 0x00070000, 0x00000000);
nv_mask(clk, 0x002504, 0x00000001, 0x00000001);
/* Wait until the interrupt handler is finished */
if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000))
return -EBUSY;
if (pfifo)
pfifo->pause(pfifo, flags);
if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010))
return -EIO;
if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f))
return -EIO;
return 0;
}
void
nva3_clock_post(struct nouveau_clock *clk, unsigned long *flags)
{
struct nouveau_fifo *pfifo = nouveau_fifo(clk);
if (pfifo && flags)
pfifo->start(pfifo, flags);
nv_mask(clk, 0x002504, 0x00000001, 0x00000000);
nv_mask(clk, 0x020060, 0x00070000, 0x00040000);
}
static void
disable_clk_src(struct nva3_clock_priv *priv, u32 src)
{
nv_mask(priv, src, 0x00000100, 0x00000000);
nv_mask(priv, src, 0x00000001, 0x00000000);
}
static void static void
prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx) prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
{ {
@ -223,24 +344,35 @@ prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
const u32 src1 = 0x004160 + (clk * 4); const u32 src1 = 0x004160 + (clk * 4);
const u32 ctrl = pll + 0; const u32 ctrl = pll + 0;
const u32 coef = pll + 4; const u32 coef = pll + 4;
u32 bypass;
if (info->pll) { if (info->pll) {
nv_mask(priv, src0, 0x00000101, 0x00000101); /* Always start from a non-PLL clock */
bypass = nv_rd32(priv, ctrl) & 0x00000008;
if (!bypass) {
nv_mask(priv, src1, 0x00000101, 0x00000101);
nv_mask(priv, ctrl, 0x00000008, 0x00000008);
udelay(20);
}
nv_mask(priv, src0, 0x003f3141, 0x00000101 | info->clk);
nv_wr32(priv, coef, info->pll); nv_wr32(priv, coef, info->pll);
nv_mask(priv, ctrl, 0x00000015, 0x00000015); nv_mask(priv, ctrl, 0x00000015, 0x00000015);
nv_mask(priv, ctrl, 0x00000010, 0x00000000); nv_mask(priv, ctrl, 0x00000010, 0x00000000);
nv_wait(priv, ctrl, 0x00020000, 0x00020000); if (!nv_wait(priv, ctrl, 0x00020000, 0x00020000)) {
nv_mask(priv, ctrl, 0x00000010, 0x00000010);
nv_mask(priv, src0, 0x00000101, 0x00000000);
return;
}
nv_mask(priv, ctrl, 0x00000010, 0x00000010); nv_mask(priv, ctrl, 0x00000010, 0x00000010);
nv_mask(priv, ctrl, 0x00000008, 0x00000000); nv_mask(priv, ctrl, 0x00000008, 0x00000000);
nv_mask(priv, src1, 0x00000100, 0x00000000); disable_clk_src(priv, src1);
nv_mask(priv, src1, 0x00000001, 0x00000000);
} else { } else {
nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk); nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk);
nv_mask(priv, ctrl, 0x00000018, 0x00000018); nv_mask(priv, ctrl, 0x00000018, 0x00000018);
udelay(20); udelay(20);
nv_mask(priv, ctrl, 0x00000001, 0x00000000); nv_mask(priv, ctrl, 0x00000001, 0x00000000);
nv_mask(priv, src0, 0x00000100, 0x00000000); disable_clk_src(priv, src0);
nv_mask(priv, src0, 0x00000001, 0x00000000);
} }
} }
@ -251,18 +383,72 @@ prog_clk(struct nva3_clock_priv *priv, int clk, int idx)
nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk); nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk);
} }
static void
prog_host(struct nva3_clock_priv *priv)
{
struct nva3_clock_info *info = &priv->eng[nv_clk_src_host];
u32 hsrc = (nv_rd32(priv, 0xc040));
switch (info->host_out) {
case NVA3_HOST_277:
if ((hsrc & 0x30000000) == 0) {
nv_wr32(priv, 0xc040, hsrc | 0x20000000);
disable_clk_src(priv, 0x4194);
}
break;
case NVA3_HOST_CLK:
prog_clk(priv, 0x1d, nv_clk_src_host);
if ((hsrc & 0x30000000) >= 0x20000000) {
nv_wr32(priv, 0xc040, hsrc & ~0x30000000);
}
break;
default:
break;
}
/* This seems to be a clock gating factor on idle, always set to 64 */
nv_wr32(priv, 0xc044, 0x3e);
}
static void
prog_core(struct nva3_clock_priv *priv, int idx)
{
struct nva3_clock_info *info = &priv->eng[idx];
u32 fb_delay = nv_rd32(priv, 0x10002c);
if (fb_delay < info->fb_delay)
nv_wr32(priv, 0x10002c, info->fb_delay);
prog_pll(priv, 0x00, 0x004200, idx);
if (fb_delay > info->fb_delay)
nv_wr32(priv, 0x10002c, info->fb_delay);
}
static int static int
nva3_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate) nva3_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
{ {
struct nva3_clock_priv *priv = (void *)clk; struct nva3_clock_priv *priv = (void *)clk;
struct nva3_clock_info *core = &priv->eng[nv_clk_src_core];
int ret; int ret;
if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) || if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) ||
(ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) || (ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) ||
(ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) || (ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) ||
(ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec))) (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)) ||
(ret = calc_host(priv, cstate)))
return ret; return ret;
/* XXX: Should be reading the highest bit in the VBIOS clock to decide
* whether to use a PLL or not... but using a PLL defeats the purpose */
if (core->pll) {
ret = nva3_clk_info(clk, 0x10,
cstate->domain[nv_clk_src_core_intm],
&priv->eng[nv_clk_src_core_intm]);
if (ret < 0)
return ret;
}
return 0; return 0;
} }
@ -270,11 +456,31 @@ static int
nva3_clock_prog(struct nouveau_clock *clk) nva3_clock_prog(struct nouveau_clock *clk)
{ {
struct nva3_clock_priv *priv = (void *)clk; struct nva3_clock_priv *priv = (void *)clk;
prog_pll(priv, 0x00, 0x004200, nv_clk_src_core); struct nva3_clock_info *core = &priv->eng[nv_clk_src_core];
int ret = 0;
unsigned long flags;
unsigned long *f = &flags;
ret = nva3_clock_pre(clk, f);
if (ret)
goto out;
if (core->pll)
prog_core(priv, nv_clk_src_core_intm);
prog_core(priv, nv_clk_src_core);
prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader); prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader);
prog_clk(priv, 0x20, nv_clk_src_disp); prog_clk(priv, 0x20, nv_clk_src_disp);
prog_clk(priv, 0x21, nv_clk_src_vdec); prog_clk(priv, 0x21, nv_clk_src_vdec);
return 0; prog_host(priv);
out:
if (ret == -EBUSY)
f = NULL;
nva3_clock_post(clk, f);
return ret;
} }
static void static void
@ -284,13 +490,14 @@ nva3_clock_tidy(struct nouveau_clock *clk)
static struct nouveau_clocks static struct nouveau_clocks
nva3_domain[] = { nva3_domain[] = {
{ nv_clk_src_crystal, 0xff }, { nv_clk_src_crystal , 0xff },
{ nv_clk_src_href , 0xff }, { nv_clk_src_core , 0x00, 0, "core", 1000 },
{ nv_clk_src_core , 0x00, 0, "core", 1000 }, { nv_clk_src_shader , 0x01, 0, "shader", 1000 },
{ nv_clk_src_shader , 0x01, 0, "shader", 1000 }, { nv_clk_src_mem , 0x02, 0, "memory", 1000 },
{ nv_clk_src_mem , 0x02, 0, "memory", 1000 }, { nv_clk_src_vdec , 0x03 },
{ nv_clk_src_vdec , 0x03 }, { nv_clk_src_disp , 0x04 },
{ nv_clk_src_disp , 0x04 }, { nv_clk_src_host , 0x05 },
{ nv_clk_src_core_intm, 0x06 },
{ nv_clk_src_max } { nv_clk_src_max }
}; };

View File

@ -6,9 +6,15 @@
struct nva3_clock_info { struct nva3_clock_info {
u32 clk; u32 clk;
u32 pll; u32 pll;
enum {
NVA3_HOST_277,
NVA3_HOST_CLK,
} host_out;
u32 fb_delay;
}; };
int nva3_clock_info(struct nouveau_clock *, int, u32, u32, int nva3_pll_info(struct nouveau_clock *, int, u32, u32,
struct nva3_clock_info *); struct nva3_clock_info *);
int nva3_clock_pre(struct nouveau_clock *clk, unsigned long *flags);
void nva3_clock_post(struct nouveau_clock *clk, unsigned long *flags);
#endif #endif

View File

@ -28,6 +28,7 @@
#include <subdev/timer.h> #include <subdev/timer.h>
#include <subdev/clock.h> #include <subdev/clock.h>
#include "nva3.h"
#include "pll.h" #include "pll.h"
struct nvaa_clock_priv { struct nvaa_clock_priv {
@ -299,25 +300,14 @@ static int
nvaa_clock_prog(struct nouveau_clock *clk) nvaa_clock_prog(struct nouveau_clock *clk)
{ {
struct nvaa_clock_priv *priv = (void *)clk; struct nvaa_clock_priv *priv = (void *)clk;
struct nouveau_fifo *pfifo = nouveau_fifo(clk); u32 pllmask = 0, mast;
unsigned long flags; unsigned long flags;
u32 pllmask = 0, mast, ptherm_gate; unsigned long *f = &flags;
int ret = -EBUSY; int ret = 0;
/* halt and idle execution engines */ ret = nva3_clock_pre(clk, f);
ptherm_gate = nv_mask(clk, 0x020060, 0x00070000, 0x00000000); if (ret)
nv_mask(clk, 0x002504, 0x00000001, 0x00000001); goto out;
/* Wait until the interrupt handler is finished */
if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000))
goto resume;
if (pfifo)
pfifo->pause(pfifo, &flags);
if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010))
goto resume;
if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f))
goto resume;
/* First switch to safe clocks: href */ /* First switch to safe clocks: href */
mast = nv_mask(clk, 0xc054, 0x03400e70, 0x03400640); mast = nv_mask(clk, 0xc054, 0x03400e70, 0x03400640);
@ -375,15 +365,8 @@ nvaa_clock_prog(struct nouveau_clock *clk)
} }
nv_wr32(clk, 0xc054, mast); nv_wr32(clk, 0xc054, mast);
ret = 0;
resume: resume:
if (pfifo)
pfifo->start(pfifo, &flags);
nv_mask(clk, 0x002504, 0x00000001, 0x00000000);
nv_wr32(clk, 0x020060, ptherm_gate);
/* Disable some PLLs and dividers when unused */ /* Disable some PLLs and dividers when unused */
if (priv->csrc != nv_clk_src_core) { if (priv->csrc != nv_clk_src_core) {
nv_wr32(clk, 0x4040, 0x00000000); nv_wr32(clk, 0x4040, 0x00000000);
@ -395,6 +378,12 @@ resume:
nv_mask(clk, 0x4020, 0x80000000, 0x00000000); nv_mask(clk, 0x4020, 0x80000000, 0x00000000);
} }
out:
if (ret == -EBUSY)
f = NULL;
nva3_clock_post(clk, f);
return ret; return ret;
} }

View File

@ -26,22 +26,8 @@
#include <core/device.h> #include <core/device.h>
#define NV04_PFB_BOOT_0 0x00100000 #include <subdev/fb/regsnv04.h>
# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
#define NV04_PFB_DEBUG_0 0x00100080 #define NV04_PFB_DEBUG_0 0x00100080
# define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001 # define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001
# define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010 # define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010

View File

@ -40,7 +40,7 @@ nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts)
int WL, CL, WR, at[2], dt, ds; int WL, CL, WR, at[2], dt, ds;
int rq = ram->freq < 1000000; /* XXX */ int rq = ram->freq < 1000000; /* XXX */
switch (ram->ramcfg.version) { switch (ram->next->bios.ramcfg_ver) {
case 0x11: case 0x11:
pd = ram->next->bios.ramcfg_11_01_80; pd = ram->next->bios.ramcfg_11_01_80;
lf = ram->next->bios.ramcfg_11_01_40; lf = ram->next->bios.ramcfg_11_01_40;
@ -54,7 +54,7 @@ nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts)
return -ENOSYS; return -ENOSYS;
} }
switch (ram->timing.version) { switch (ram->next->bios.timing_ver) {
case 0x20: case 0x20:
WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7;
CL = (ram->next->bios.timing[1] & 0x0000001f); CL = (ram->next->bios.timing[1] & 0x0000001f);

View File

@ -45,7 +45,7 @@ nv20_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{ {
u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */ if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */
else tile->zcomp = 0x04000000; /* Z24S8 */ else tile->zcomp = 0x04000000; /* Z24S8 */
tile->zcomp |= tile->tag->offset; tile->zcomp |= tile->tag->offset;

View File

@ -32,7 +32,7 @@ nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{ {
u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */ if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */
else tile->zcomp = 0x00200000; /* Z24S8 */ else tile->zcomp = 0x00200000; /* Z24S8 */
tile->zcomp |= tile->tag->offset; tile->zcomp |= tile->tag->offset;

View File

@ -51,7 +51,7 @@ nv30_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{ {
u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */ if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */
else tile->zcomp |= 0x02000000; /* Z24S8 */ else tile->zcomp |= 0x02000000; /* Z24S8 */
tile->zcomp |= ((tile->tag->offset ) >> 6); tile->zcomp |= ((tile->tag->offset ) >> 6);

View File

@ -32,7 +32,7 @@ nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{ {
u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */ if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */
else tile->zcomp |= 0x08000000; /* Z24S8 */ else tile->zcomp |= 0x08000000; /* Z24S8 */
tile->zcomp |= ((tile->tag->offset ) >> 6); tile->zcomp |= ((tile->tag->offset ) >> 6);

View File

@ -32,7 +32,7 @@ nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{ {
u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */ if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */
else tile->zcomp |= 0x20000000; /* Z24S8 */ else tile->zcomp |= 0x20000000; /* Z24S8 */
tile->zcomp |= ((tile->tag->offset ) >> 6); tile->zcomp |= ((tile->tag->offset ) >> 6);

View File

@ -33,7 +33,7 @@ nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
u32 tiles = DIV_ROUND_UP(size, 0x80); u32 tiles = DIV_ROUND_UP(size, 0x80);
u32 tags = round_up(tiles / pfb->ram->parts, 0x100); u32 tags = round_up(tiles / pfb->ram->parts, 0x100);
if ( (flags & 2) && if ( (flags & 2) &&
!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { !nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */ tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */
tile->zcomp |= ((tile->tag->offset ) >> 8); tile->zcomp |= ((tile->tag->offset ) >> 8);
tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13; tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13;

View File

@ -35,6 +35,7 @@ extern struct nouveau_oclass nve0_ram_oclass;
extern struct nouveau_oclass gk20a_ram_oclass; extern struct nouveau_oclass gk20a_ram_oclass;
extern struct nouveau_oclass gm107_ram_oclass; extern struct nouveau_oclass gm107_ram_oclass;
int nouveau_sddr2_calc(struct nouveau_ram *ram);
int nouveau_sddr3_calc(struct nouveau_ram *ram); int nouveau_sddr3_calc(struct nouveau_ram *ram);
int nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts); int nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts);

View File

@ -12,16 +12,32 @@ struct ramfuc {
struct ramfuc_reg { struct ramfuc_reg {
int sequence; int sequence;
bool force; bool force;
u32 addr[2]; u32 addr;
u32 stride; /* in bytes */
u32 mask;
u32 data; u32 data;
}; };
static inline struct ramfuc_reg
ramfuc_stride(u32 addr, u32 stride, u32 mask)
{
return (struct ramfuc_reg) {
.sequence = 0,
.addr = addr,
.stride = stride,
.mask = mask,
.data = 0xdeadbeef,
};
}
static inline struct ramfuc_reg static inline struct ramfuc_reg
ramfuc_reg2(u32 addr1, u32 addr2) ramfuc_reg2(u32 addr1, u32 addr2)
{ {
return (struct ramfuc_reg) { return (struct ramfuc_reg) {
.sequence = 0, .sequence = 0,
.addr = { addr1, addr2 }, .addr = addr1,
.stride = addr2 - addr1,
.mask = 0x3,
.data = 0xdeadbeef, .data = 0xdeadbeef,
}; };
} }
@ -29,7 +45,13 @@ ramfuc_reg2(u32 addr1, u32 addr2)
static noinline struct ramfuc_reg static noinline struct ramfuc_reg
ramfuc_reg(u32 addr) ramfuc_reg(u32 addr)
{ {
return ramfuc_reg2(addr, addr); return (struct ramfuc_reg) {
.sequence = 0,
.addr = addr,
.stride = 0,
.mask = 0x1,
.data = 0xdeadbeef,
};
} }
static inline int static inline int
@ -62,18 +84,25 @@ static inline u32
ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg) ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg)
{ {
if (reg->sequence != ram->sequence) if (reg->sequence != ram->sequence)
reg->data = nv_rd32(ram->pfb, reg->addr[0]); reg->data = nv_rd32(ram->pfb, reg->addr);
return reg->data; return reg->data;
} }
static inline void static inline void
ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data) ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data)
{ {
unsigned int mask, off = 0;
reg->sequence = ram->sequence; reg->sequence = ram->sequence;
reg->data = data; reg->data = data;
if (reg->addr[0] != reg->addr[1])
nouveau_memx_wr32(ram->memx, reg->addr[1], reg->data); for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) {
nouveau_memx_wr32(ram->memx, reg->addr[0], reg->data); if (mask & 1) {
nouveau_memx_wr32(ram->memx, reg->addr+off, reg->data);
}
off += reg->stride;
}
} }
static inline void static inline void
@ -105,14 +134,35 @@ ramfuc_nsec(struct ramfuc *ram, u32 nsec)
nouveau_memx_nsec(ram->memx, nsec); nouveau_memx_nsec(ram->memx, nsec);
} }
#define ram_init(s,p) ramfuc_init(&(s)->base, (p)) static inline void
#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e)) ramfuc_wait_vblank(struct ramfuc *ram)
#define ram_have(s,r) ((s)->r_##r.addr[0] != 0x000000) {
#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r) nouveau_memx_wait_vblank(ram->memx);
#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d)) }
#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r)
#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d)) static inline void
#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n)) ramfuc_block(struct ramfuc *ram)
#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n)) {
nouveau_memx_block(ram->memx);
}
static inline void
ramfuc_unblock(struct ramfuc *ram)
{
nouveau_memx_unblock(ram->memx);
}
#define ram_init(s,p) ramfuc_init(&(s)->base, (p))
#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e))
#define ram_have(s,r) ((s)->r_##r.addr != 0x000000)
#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r)
#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d))
#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r)
#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d))
#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n))
#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n))
#define ram_wait_vblank(s) ramfuc_wait_vblank(&(s)->base)
#define ram_block(s) ramfuc_block(&(s)->base)
#define ram_unblock(s) ramfuc_unblock(&(s)->base)
#endif #endif

View File

@ -22,22 +22,7 @@
* Authors: Ben Skeggs * Authors: Ben Skeggs
*/ */
#define NV04_PFB_BOOT_0 0x00100000 #include <subdev/fb/regsnv04.h>
# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
#include "priv.h" #include "priv.h"

View File

@ -280,7 +280,7 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
if (align == 16) { if (align == 16) {
int n = (max >> 4) * comp; int n = (max >> 4) * comp;
ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag); ret = nouveau_mm_head(tags, 0, 1, n, n, 1, &mem->tag);
if (ret) if (ret)
mem->tag = NULL; mem->tag = NULL;
} }
@ -296,9 +296,9 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
type = nv50_fb_memtype[type]; type = nv50_fb_memtype[type];
do { do {
if (back) if (back)
ret = nouveau_mm_tail(heap, type, max, min, align, &r); ret = nouveau_mm_tail(heap, 0, type, max, min, align, &r);
else else
ret = nouveau_mm_head(heap, type, max, min, align, &r); ret = nouveau_mm_head(heap, 0, type, max, min, align, &r);
if (ret) { if (ret) {
mutex_unlock(&pfb->base.mutex); mutex_unlock(&pfb->base.mutex);
pfb->ram->put(pfb, &mem); pfb->ram->put(pfb, &mem);
@ -319,27 +319,22 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
static u32 static u32
nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram) nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram)
{ {
int i, parts, colbits, rowbitsa, rowbitsb, banks; int colbits, rowbitsa, rowbitsb, banks;
u64 rowsize, predicted; u64 rowsize, predicted;
u32 r0, r4, rt, ru, rblock_size; u32 r0, r4, rt, rblock_size;
r0 = nv_rd32(pfb, 0x100200); r0 = nv_rd32(pfb, 0x100200);
r4 = nv_rd32(pfb, 0x100204); r4 = nv_rd32(pfb, 0x100204);
rt = nv_rd32(pfb, 0x100250); rt = nv_rd32(pfb, 0x100250);
ru = nv_rd32(pfb, 0x001540); nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt,
nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru); nv_rd32(pfb, 0x001540));
for (i = 0, parts = 0; i < 8; i++) {
if (ru & (0x00010000 << i))
parts++;
}
colbits = (r4 & 0x0000f000) >> 12; colbits = (r4 & 0x0000f000) >> 12;
rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
banks = 1 << (((r4 & 0x03000000) >> 24) + 2); banks = 1 << (((r4 & 0x03000000) >> 24) + 2);
rowsize = parts * banks * (1 << colbits) * 8; rowsize = ram->parts * banks * (1 << colbits) * 8;
predicted = rowsize << rowbitsa; predicted = rowsize << rowbitsa;
if (r0 & 0x00000004) if (r0 & 0x00000004)
predicted += rowsize << rowbitsb; predicted += rowsize << rowbitsb;
@ -376,6 +371,9 @@ nv50_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
ram->size = nv_rd32(pfb, 0x10020c); ram->size = nv_rd32(pfb, 0x10020c);
ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32); ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32);
ram->part_mask = (nv_rd32(pfb, 0x001540) & 0x00ff0000) >> 16;
ram->parts = hweight8(ram->part_mask);
switch (nv_rd32(pfb, 0x100714) & 0x00000007) { switch (nv_rd32(pfb, 0x100714) & 0x00000007) {
case 0: ram->type = NV_MEM_TYPE_DDR1; break; case 0: ram->type = NV_MEM_TYPE_DDR1; break;
case 1: case 1:

View File

@ -79,20 +79,27 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
struct nva3_ram *ram = (void *)pfb->ram; struct nva3_ram *ram = (void *)pfb->ram;
struct nva3_ramfuc *fuc = &ram->fuc; struct nva3_ramfuc *fuc = &ram->fuc;
struct nva3_clock_info mclk; struct nva3_clock_info mclk;
u8 ver, cnt, len, strap; struct nouveau_ram_data *next;
u8 ver, hdr, cnt, len, strap;
u32 data; u32 data;
struct {
u32 data;
u8 size;
} rammap, ramcfg, timing;
u32 r004018, r100760, ctrl; u32 r004018, r100760, ctrl;
u32 unk714, unk718, unk71c; u32 unk714, unk718, unk71c;
int ret; int ret, i;
next = &ram->base.target;
next->freq = freq;
ram->base.next = next;
/* lookup memory config data relevant to the target frequency */ /* lookup memory config data relevant to the target frequency */
rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size, i = 0;
&cnt, &ramcfg.size); while ((data = nvbios_rammapEp(bios, i++, &ver, &hdr, &cnt, &len,
if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) { &next->bios))) {
if (freq / 1000 >= next->bios.rammap_min &&
freq / 1000 <= next->bios.rammap_max)
break;
}
if (!data || ver != 0x10 || hdr < 0x0e) {
nv_error(pfb, "invalid/missing rammap entry\n"); nv_error(pfb, "invalid/missing rammap entry\n");
return -EINVAL; return -EINVAL;
} }
@ -104,26 +111,25 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
return -EINVAL; return -EINVAL;
} }
ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size); data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap,
if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) { &ver, &hdr, &next->bios);
if (!data || ver != 0x10 || hdr < 0x0e) {
nv_error(pfb, "invalid/missing ramcfg entry\n"); nv_error(pfb, "invalid/missing ramcfg entry\n");
return -EINVAL; return -EINVAL;
} }
/* lookup memory timings, if bios says they're present */ /* lookup memory timings, if bios says they're present */
strap = nv_ro08(bios, ramcfg.data + 0x01); if (next->bios.ramcfg_timing != 0xff) {
if (strap != 0xff) { data = nvbios_timingEp(bios, next->bios.ramcfg_timing,
timing.data = nvbios_timingEe(bios, strap, &ver, &timing.size, &ver, &hdr, &cnt, &len,
&cnt, &len); &next->bios);
if (!timing.data || ver != 0x10 || timing.size < 0x19) { if (!data || ver != 0x10 || hdr < 0x19) {
nv_error(pfb, "invalid/missing timing entry\n"); nv_error(pfb, "invalid/missing timing entry\n");
return -EINVAL; return -EINVAL;
} }
} else {
timing.data = 0;
} }
ret = nva3_clock_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk); ret = nva3_pll_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk);
if (ret < 0) { if (ret < 0) {
nv_error(pfb, "failed mclk calculation\n"); nv_error(pfb, "failed mclk calculation\n");
return ret; return ret;
@ -163,17 +169,17 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x004168, 0x003f3141, ctrl); ram_mask(fuc, 0x004168, 0x003f3141, ctrl);
} }
if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) { if (next->bios.ramcfg_10_02_10) {
ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); ram_mask(fuc, 0x111104, 0x00000600, 0x00000000);
} else { } else {
ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); ram_mask(fuc, 0x111100, 0x40000000, 0x40000000);
ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); ram_mask(fuc, 0x111104, 0x00000180, 0x00000000);
} }
if (!(nv_ro08(bios, rammap.data + 0x04) & 0x02)) if (!next->bios.rammap_10_04_02)
ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); ram_mask(fuc, 0x100200, 0x00000800, 0x00000000);
ram_wr32(fuc, 0x611200, 0x00003300); ram_wr32(fuc, 0x611200, 0x00003300);
if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) if (!next->bios.ramcfg_10_02_10)
ram_wr32(fuc, 0x111100, 0x4c020000); /*XXX*/ ram_wr32(fuc, 0x111100, 0x4c020000); /*XXX*/
ram_wr32(fuc, 0x1002d4, 0x00000001); ram_wr32(fuc, 0x1002d4, 0x00000001);
@ -202,17 +208,16 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_wr32(fuc, 0x004018, 0x0000d000 | r004018); ram_wr32(fuc, 0x004018, 0x0000d000 | r004018);
} }
if ( (nv_ro08(bios, rammap.data + 0x04) & 0x08)) { if (next->bios.rammap_10_04_08) {
u32 unk5a0 = (nv_ro16(bios, ramcfg.data + 0x05) << 8) | ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 |
nv_ro08(bios, ramcfg.data + 0x05); next->bios.ramcfg_10_05 << 8 |
u32 unk5a4 = (nv_ro16(bios, ramcfg.data + 0x07)); next->bios.ramcfg_10_05);
u32 unk804 = (nv_ro08(bios, ramcfg.data + 0x09) & 0xf0) << 16 | ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 |
(nv_ro08(bios, ramcfg.data + 0x03) & 0x0f) << 16 | next->bios.ramcfg_10_07);
(nv_ro08(bios, ramcfg.data + 0x09) & 0x0f) | ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 |
0x80000000; next->bios.ramcfg_10_03_0f << 16 |
ram_wr32(fuc, 0x1005a0, unk5a0); next->bios.ramcfg_10_09_0f |
ram_wr32(fuc, 0x1005a4, unk5a4); 0x80000000);
ram_wr32(fuc, 0x10f804, unk804);
ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000);
} else { } else {
ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000); ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000);
@ -250,27 +255,26 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x100220[0], 0x00000000, 0x00000000); ram_mask(fuc, 0x100220[0], 0x00000000, 0x00000000);
ram_mask(fuc, 0x100220[8], 0x00000000, 0x00000000); ram_mask(fuc, 0x100220[8], 0x00000000, 0x00000000);
data = (nv_ro08(bios, ramcfg.data + 0x02) & 0x08) ? 0x00000000 : 0x00001000; ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12);
ram_mask(fuc, 0x100200, 0x00001000, data);
unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000010; unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000010;
unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100;
unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100;
if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x20)) if (next->bios.ramcfg_10_02_20)
unk714 |= 0xf0000000; unk714 |= 0xf0000000;
if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x04)) if (!next->bios.ramcfg_10_02_04)
unk714 |= 0x00000010; unk714 |= 0x00000010;
ram_wr32(fuc, 0x100714, unk714); ram_wr32(fuc, 0x100714, unk714);
if (nv_ro08(bios, ramcfg.data + 0x02) & 0x01) if (next->bios.ramcfg_10_02_01)
unk71c |= 0x00000100; unk71c |= 0x00000100;
ram_wr32(fuc, 0x10071c, unk71c); ram_wr32(fuc, 0x10071c, unk71c);
if (nv_ro08(bios, ramcfg.data + 0x02) & 0x02) if (next->bios.ramcfg_10_02_02)
unk718 |= 0x00000100; unk718 |= 0x00000100;
ram_wr32(fuc, 0x100718, unk718); ram_wr32(fuc, 0x100718, unk718);
if (nv_ro08(bios, ramcfg.data + 0x02) & 0x10) if (next->bios.ramcfg_10_02_10)
ram_wr32(fuc, 0x111100, 0x48000000); /*XXX*/ ram_wr32(fuc, 0x111100, 0x48000000); /*XXX*/
ram_mask(fuc, mr[0], 0x100, 0x100); ram_mask(fuc, mr[0], 0x100, 0x100);
@ -282,9 +286,9 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_nsec(fuc, 12000); ram_nsec(fuc, 12000);
ram_wr32(fuc, 0x611200, 0x00003330); ram_wr32(fuc, 0x611200, 0x00003330);
if ( (nv_ro08(bios, rammap.data + 0x04) & 0x02)) if (next->bios.rammap_10_04_02)
ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); ram_mask(fuc, 0x100200, 0x00000800, 0x00000800);
if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) { if (next->bios.ramcfg_10_02_10) {
ram_mask(fuc, 0x111104, 0x00000180, 0x00000180); ram_mask(fuc, 0x111104, 0x00000180, 0x00000180);
ram_mask(fuc, 0x111100, 0x40000000, 0x00000000); ram_mask(fuc, 0x111100, 0x40000000, 0x00000000);
} else { } else {
@ -404,11 +408,11 @@ nva3_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
ram->fuc.r_0x100714 = ramfuc_reg(0x100714); ram->fuc.r_0x100714 = ramfuc_reg(0x100714);
ram->fuc.r_0x100718 = ramfuc_reg(0x100718); ram->fuc.r_0x100718 = ramfuc_reg(0x100718);
ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); ram->fuc.r_0x10071c = ramfuc_reg(0x10071c);
ram->fuc.r_0x100760 = ramfuc_reg(0x100760); ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask);
ram->fuc.r_0x1007a0 = ramfuc_reg(0x1007a0); ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask);
ram->fuc.r_0x1007e0 = ramfuc_reg(0x1007e0); ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask);
ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804);
ram->fuc.r_0x1110e0 = ramfuc_reg(0x1110e0); ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask);
ram->fuc.r_0x111100 = ramfuc_reg(0x111100); ram->fuc.r_0x111100 = ramfuc_reg(0x111100);
ram->fuc.r_0x111104 = ramfuc_reg(0x111104); ram->fuc.r_0x111104 = ramfuc_reg(0x111104);
ram->fuc.r_0x611200 = ramfuc_reg(0x611200); ram->fuc.r_0x611200 = ramfuc_reg(0x611200);

View File

@ -133,6 +133,7 @@ nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq)
struct nouveau_bios *bios = nouveau_bios(pfb); struct nouveau_bios *bios = nouveau_bios(pfb);
struct nvc0_ram *ram = (void *)pfb->ram; struct nvc0_ram *ram = (void *)pfb->ram;
struct nvc0_ramfuc *fuc = &ram->fuc; struct nvc0_ramfuc *fuc = &ram->fuc;
struct nvbios_ramcfg cfg;
u8 ver, cnt, len, strap; u8 ver, cnt, len, strap;
struct { struct {
u32 data; u32 data;
@ -145,7 +146,7 @@ nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq)
/* lookup memory config data relevant to the target frequency */ /* lookup memory config data relevant to the target frequency */
rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size, rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size,
&cnt, &ramcfg.size); &cnt, &ramcfg.size, &cfg);
if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) { if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) {
nv_error(pfb, "invalid/missing rammap entry\n"); nv_error(pfb, "invalid/missing rammap entry\n");
return -EINVAL; return -EINVAL;
@ -483,9 +484,9 @@ nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
do { do {
if (back) if (back)
ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r); ret = nouveau_mm_tail(mm, 0, 1, size, ncmin, align, &r);
else else
ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r); ret = nouveau_mm_head(mm, 0, 1, size, ncmin, align, &r);
if (ret) { if (ret) {
mutex_unlock(&pfb->base.mutex); mutex_unlock(&pfb->base.mutex);
pfb->ram->put(pfb, &mem); pfb->ram->put(pfb, &mem);
@ -562,7 +563,7 @@ nvc0_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
offset = (0x0200000000ULL >> 12) + (bsize << 8); offset = (0x0200000000ULL >> 12) + (bsize << 8);
length = (ram->size >> 12) - ((bsize * parts) << 8) - rsvd_tail; length = (ram->size >> 12) - ((bsize * parts) << 8) - rsvd_tail;
ret = nouveau_mm_init(&pfb->vram, offset, length, 0); ret = nouveau_mm_init(&pfb->vram, offset, length, 1);
if (ret) if (ret)
nouveau_mm_fini(&pfb->vram); nouveau_mm_fini(&pfb->vram);
} }

View File

@ -29,6 +29,8 @@
#include <subdev/bios/init.h> #include <subdev/bios/init.h>
#include <subdev/bios/rammap.h> #include <subdev/bios/rammap.h>
#include <subdev/bios/timing.h> #include <subdev/bios/timing.h>
#include <subdev/bios/M0205.h>
#include <subdev/bios/M0209.h>
#include <subdev/clock.h> #include <subdev/clock.h>
#include <subdev/clock/pll.h> #include <subdev/clock/pll.h>
@ -41,14 +43,6 @@
#include "ramfuc.h" #include "ramfuc.h"
/* binary driver only executes this path if the condition (a) is true
* for any configuration (combination of rammap+ramcfg+timing) that
* can be reached on a given card. for now, we will execute the branch
* unconditionally in the hope that a "false everywhere" in the bios
* tables doesn't actually mean "don't touch this".
*/
#define NOTE00(a) 1
struct nve0_ramfuc { struct nve0_ramfuc {
struct ramfuc base; struct ramfuc base;
@ -134,10 +128,12 @@ struct nve0_ram {
struct nouveau_ram base; struct nouveau_ram base;
struct nve0_ramfuc fuc; struct nve0_ramfuc fuc;
struct list_head cfg;
u32 parts; u32 parts;
u32 pmask; u32 pmask;
u32 pnuts; u32 pnuts;
struct nvbios_ramcfg diff;
int from; int from;
int mode; int mode;
int N1, fN1, M1, P1; int N1, fN1, M1, P1;
@ -241,7 +237,7 @@ nve0_ram_nuts(struct nve0_ram *ram, struct ramfuc_reg *reg,
{ {
struct nve0_fb_priv *priv = (void *)nouveau_fb(ram); struct nve0_fb_priv *priv = (void *)nouveau_fb(ram);
struct ramfuc *fuc = &ram->fuc.base; struct ramfuc *fuc = &ram->fuc.base;
u32 addr = 0x110000 + (reg->addr[0] & 0xfff); u32 addr = 0x110000 + (reg->addr & 0xfff);
u32 mask = _mask | _copy; u32 mask = _mask | _copy;
u32 data = (_data & _mask) | (reg->data & _copy); u32 data = (_data & _mask) | (reg->data & _copy);
u32 i; u32 i;
@ -268,6 +264,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
u32 mask, data; u32 mask, data;
ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
ram_block(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0000); ram_wr32(fuc, 0x62c000, 0x0f0f0000);
/* MR1: turn termination on early, for some reason.. */ /* MR1: turn termination on early, for some reason.. */
@ -478,7 +475,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]);
data = mask = 0x00000000; data = mask = 0x00000000;
if (NOTE00(ramcfg_08_20)) { if (ram->diff.ramcfg_11_08_20) {
if (next->bios.ramcfg_11_08_20) if (next->bios.ramcfg_11_08_20)
data |= 0x01000000; data |= 0x01000000;
mask |= 0x01000000; mask |= 0x01000000;
@ -486,11 +483,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f200, mask, data); ram_mask(fuc, 0x10f200, mask, data);
data = mask = 0x00000000; data = mask = 0x00000000;
if (NOTE00(ramcfg_02_03 != 0)) { if (ram->diff.ramcfg_11_02_03) {
data |= next->bios.ramcfg_11_02_03 << 8; data |= next->bios.ramcfg_11_02_03 << 8;
mask |= 0x00000300; mask |= 0x00000300;
} }
if (NOTE00(ramcfg_01_10)) { if (ram->diff.ramcfg_11_01_10) {
if (next->bios.ramcfg_11_01_10) if (next->bios.ramcfg_11_01_10)
data |= 0x70000000; data |= 0x70000000;
mask |= 0x70000000; mask |= 0x70000000;
@ -498,11 +495,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f604, mask, data); ram_mask(fuc, 0x10f604, mask, data);
data = mask = 0x00000000; data = mask = 0x00000000;
if (NOTE00(timing_30_07 != 0)) { if (ram->diff.timing_20_30_07) {
data |= next->bios.timing_20_30_07 << 28; data |= next->bios.timing_20_30_07 << 28;
mask |= 0x70000000; mask |= 0x70000000;
} }
if (NOTE00(ramcfg_01_01)) { if (ram->diff.ramcfg_11_01_01) {
if (next->bios.ramcfg_11_01_01) if (next->bios.ramcfg_11_01_01)
data |= 0x00000100; data |= 0x00000100;
mask |= 0x00000100; mask |= 0x00000100;
@ -510,11 +507,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f614, mask, data); ram_mask(fuc, 0x10f614, mask, data);
data = mask = 0x00000000; data = mask = 0x00000000;
if (NOTE00(timing_30_07 != 0)) { if (ram->diff.timing_20_30_07) {
data |= next->bios.timing_20_30_07 << 28; data |= next->bios.timing_20_30_07 << 28;
mask |= 0x70000000; mask |= 0x70000000;
} }
if (NOTE00(ramcfg_01_02)) { if (ram->diff.ramcfg_11_01_02) {
if (next->bios.ramcfg_11_01_02) if (next->bios.ramcfg_11_01_02)
data |= 0x00000100; data |= 0x00000100;
mask |= 0x00000100; mask |= 0x00000100;
@ -548,11 +545,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f);
data = mask = 0x00000000; data = mask = 0x00000000;
if (NOTE00(ramcfg_02_03 != 0)) { if (ram->diff.ramcfg_11_02_03) {
data |= next->bios.ramcfg_11_02_03; data |= next->bios.ramcfg_11_02_03;
mask |= 0x00000003; mask |= 0x00000003;
} }
if (NOTE00(ramcfg_01_10)) { if (ram->diff.ramcfg_11_01_10) {
if (next->bios.ramcfg_11_01_10) if (next->bios.ramcfg_11_01_10)
data |= 0x00000004; data |= 0x00000004;
mask |= 0x00000004; mask |= 0x00000004;
@ -666,6 +663,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
if (next->bios.ramcfg_11_07_02) if (next->bios.ramcfg_11_07_02)
nve0_ram_train(fuc, 0x80020000, 0x01000000); nve0_ram_train(fuc, 0x80020000, 0x01000000);
ram_unblock(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0f00); ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
if (next->bios.rammap_11_08_01) if (next->bios.rammap_11_08_01)
@ -695,6 +693,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
u32 mask, data; u32 mask, data;
ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
ram_block(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0000); ram_wr32(fuc, 0x62c000, 0x0f0f0000);
if (vc == 1 && ram_have(fuc, gpio2E)) { if (vc == 1 && ram_have(fuc, gpio2E)) {
@ -917,6 +916,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
ram_nsec(fuc, 1000); ram_nsec(fuc, 1000);
ram_unblock(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0f00); ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
if (next->bios.rammap_11_08_01) if (next->bios.rammap_11_08_01)
@ -932,58 +932,24 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
******************************************************************************/ ******************************************************************************/
static int static int
nve0_ram_calc_data(struct nouveau_fb *pfb, u32 freq, nve0_ram_calc_data(struct nouveau_fb *pfb, u32 khz,
struct nouveau_ram_data *data) struct nouveau_ram_data *data)
{ {
struct nouveau_bios *bios = nouveau_bios(pfb);
struct nve0_ram *ram = (void *)pfb->ram; struct nve0_ram *ram = (void *)pfb->ram;
u8 strap, cnt, len; struct nouveau_ram_data *cfg;
u32 mhz = khz / 1000;
/* lookup memory config data relevant to the target frequency */ list_for_each_entry(cfg, &ram->cfg, head) {
ram->base.rammap.data = nvbios_rammapEp(bios, freq / 1000, if (mhz >= cfg->bios.rammap_min &&
&ram->base.rammap.version, mhz <= cfg->bios.rammap_max) {
&ram->base.rammap.size, *data = *cfg;
&cnt, &len, &data->bios); data->freq = khz;
if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 || return 0;
ram->base.rammap.size < 0x09) {
nv_error(pfb, "invalid/missing rammap entry\n");
return -EINVAL;
}
/* locate specific data set for the attached memory */
strap = nvbios_ramcfg_index(nv_subdev(pfb));
ram->base.ramcfg.data = nvbios_rammapSp(bios, ram->base.rammap.data,
ram->base.rammap.version,
ram->base.rammap.size,
cnt, len, strap,
&ram->base.ramcfg.version,
&ram->base.ramcfg.size,
&data->bios);
if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 ||
ram->base.ramcfg.size < 0x08) {
nv_error(pfb, "invalid/missing ramcfg entry\n");
return -EINVAL;
}
/* lookup memory timings, if bios says they're present */
strap = nv_ro08(bios, ram->base.ramcfg.data + 0x00);
if (strap != 0xff) {
ram->base.timing.data =
nvbios_timingEp(bios, strap, &ram->base.timing.version,
&ram->base.timing.size, &cnt, &len,
&data->bios);
if (!ram->base.timing.data ||
ram->base.timing.version != 0x20 ||
ram->base.timing.size < 0x33) {
nv_error(pfb, "invalid/missing timing entry\n");
return -EINVAL;
} }
} else {
ram->base.timing.data = 0;
} }
data->freq = freq; nv_error(ram, "ramcfg data for %dMHz not found\n", mhz);
return 0; return -EINVAL;
} }
static int static int
@ -1106,13 +1072,99 @@ nve0_ram_calc(struct nouveau_fb *pfb, u32 freq)
return nve0_ram_calc_xits(pfb, ram->base.next); return nve0_ram_calc_xits(pfb, ram->base.next);
} }
static void
nve0_ram_prog_0(struct nouveau_fb *pfb, u32 freq)
{
struct nve0_ram *ram = (void *)pfb->ram;
struct nouveau_ram_data *cfg;
u32 mhz = freq / 1000;
u32 mask, data;
list_for_each_entry(cfg, &ram->cfg, head) {
if (mhz >= cfg->bios.rammap_min &&
mhz <= cfg->bios.rammap_max)
break;
}
if (&cfg->head == &ram->cfg)
return;
if (mask = 0, data = 0, ram->diff.rammap_11_0a_03fe) {
data |= cfg->bios.rammap_11_0a_03fe << 12;
mask |= 0x001ff000;
}
if (ram->diff.rammap_11_09_01ff) {
data |= cfg->bios.rammap_11_09_01ff;
mask |= 0x000001ff;
}
nv_mask(pfb, 0x10f468, mask, data);
if (mask = 0, data = 0, ram->diff.rammap_11_0a_0400) {
data |= cfg->bios.rammap_11_0a_0400;
mask |= 0x00000001;
}
nv_mask(pfb, 0x10f420, mask, data);
if (mask = 0, data = 0, ram->diff.rammap_11_0a_0800) {
data |= cfg->bios.rammap_11_0a_0800;
mask |= 0x00000001;
}
nv_mask(pfb, 0x10f430, mask, data);
if (mask = 0, data = 0, ram->diff.rammap_11_0b_01f0) {
data |= cfg->bios.rammap_11_0b_01f0;
mask |= 0x0000001f;
}
nv_mask(pfb, 0x10f400, mask, data);
if (mask = 0, data = 0, ram->diff.rammap_11_0b_0200) {
data |= cfg->bios.rammap_11_0b_0200 << 9;
mask |= 0x00000200;
}
nv_mask(pfb, 0x10f410, mask, data);
if (mask = 0, data = 0, ram->diff.rammap_11_0d) {
data |= cfg->bios.rammap_11_0d << 16;
mask |= 0x00ff0000;
}
if (ram->diff.rammap_11_0f) {
data |= cfg->bios.rammap_11_0f << 8;
mask |= 0x0000ff00;
}
nv_mask(pfb, 0x10f440, mask, data);
if (mask = 0, data = 0, ram->diff.rammap_11_0e) {
data |= cfg->bios.rammap_11_0e << 8;
mask |= 0x0000ff00;
}
if (ram->diff.rammap_11_0b_0800) {
data |= cfg->bios.rammap_11_0b_0800 << 7;
mask |= 0x00000080;
}
if (ram->diff.rammap_11_0b_0400) {
data |= cfg->bios.rammap_11_0b_0400 << 5;
mask |= 0x00000020;
}
nv_mask(pfb, 0x10f444, mask, data);
}
static int static int
nve0_ram_prog(struct nouveau_fb *pfb) nve0_ram_prog(struct nouveau_fb *pfb)
{ {
struct nouveau_device *device = nv_device(pfb); struct nouveau_device *device = nv_device(pfb);
struct nve0_ram *ram = (void *)pfb->ram; struct nve0_ram *ram = (void *)pfb->ram;
struct nve0_ramfuc *fuc = &ram->fuc; struct nve0_ramfuc *fuc = &ram->fuc;
ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true)); struct nouveau_ram_data *next = ram->base.next;
if (!nouveau_boolopt(device->cfgopt, "NvMemExec", true)) {
ram_exec(fuc, false);
return (ram->base.next == &ram->base.xition);
}
nve0_ram_prog_0(pfb, 1000);
ram_exec(fuc, true);
nve0_ram_prog_0(pfb, next->freq);
return (ram->base.next == &ram->base.xition); return (ram->base.next == &ram->base.xition);
} }
@ -1125,24 +1177,147 @@ nve0_ram_tidy(struct nouveau_fb *pfb)
ram_exec(fuc, false); ram_exec(fuc, false);
} }
struct nve0_ram_train {
u16 mask;
struct nvbios_M0209S remap;
struct nvbios_M0209S type00;
struct nvbios_M0209S type01;
struct nvbios_M0209S type04;
struct nvbios_M0209S type06;
struct nvbios_M0209S type07;
struct nvbios_M0209S type08;
struct nvbios_M0209S type09;
};
static int
nve0_ram_train_type(struct nouveau_fb *pfb, int i, u8 ramcfg,
struct nve0_ram_train *train)
{
struct nouveau_bios *bios = nouveau_bios(pfb);
struct nvbios_M0205E M0205E;
struct nvbios_M0205S M0205S;
struct nvbios_M0209E M0209E;
struct nvbios_M0209S *remap = &train->remap;
struct nvbios_M0209S *value;
u8 ver, hdr, cnt, len;
u32 data;
/* determine type of data for this index */
if (!(data = nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)))
return -ENOENT;
switch (M0205E.type) {
case 0x00: value = &train->type00; break;
case 0x01: value = &train->type01; break;
case 0x04: value = &train->type04; break;
case 0x06: value = &train->type06; break;
case 0x07: value = &train->type07; break;
case 0x08: value = &train->type08; break;
case 0x09: value = &train->type09; break;
default:
return 0;
}
/* training data index determined by ramcfg strap */
if (!(data = nvbios_M0205Sp(bios, i, ramcfg, &ver, &hdr, &M0205S)))
return -EINVAL;
i = M0205S.data;
/* training data format information */
if (!(data = nvbios_M0209Ep(bios, i, &ver, &hdr, &cnt, &len, &M0209E)))
return -EINVAL;
/* ... and the raw data */
if (!(data = nvbios_M0209Sp(bios, i, 0, &ver, &hdr, value)))
return -EINVAL;
if (M0209E.v02_07 == 2) {
/* of course! why wouldn't we have a pointer to another entry
* in the same table, and use the first one as an array of
* remap indices...
*/
if (!(data = nvbios_M0209Sp(bios, M0209E.v03, 0, &ver, &hdr,
remap)))
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(value->data); i++)
value->data[i] = remap->data[value->data[i]];
} else
if (M0209E.v02_07 != 1)
return -EINVAL;
train->mask |= 1 << M0205E.type;
return 0;
}
static int
nve0_ram_train_init_0(struct nouveau_fb *pfb, struct nve0_ram_train *train)
{
int i, j;
if ((train->mask & 0x03d3) != 0x03d3) {
nv_warn(pfb, "missing link training data\n");
return -EINVAL;
}
for (i = 0; i < 0x30; i++) {
for (j = 0; j < 8; j += 4) {
nv_wr32(pfb, 0x10f968 + j, 0x00000000 | (i << 8));
nv_wr32(pfb, 0x10f920 + j, 0x00000000 |
train->type08.data[i] << 4 |
train->type06.data[i]);
nv_wr32(pfb, 0x10f918 + j, train->type00.data[i]);
nv_wr32(pfb, 0x10f920 + j, 0x00000100 |
train->type09.data[i] << 4 |
train->type07.data[i]);
nv_wr32(pfb, 0x10f918 + j, train->type01.data[i]);
}
}
for (j = 0; j < 8; j += 4) {
for (i = 0; i < 0x100; i++) {
nv_wr32(pfb, 0x10f968 + j, i);
nv_wr32(pfb, 0x10f900 + j, train->type04.data[i]);
}
}
return 0;
}
static int
nve0_ram_train_init(struct nouveau_fb *pfb)
{
u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb));
struct nve0_ram_train *train;
int ret = -ENOMEM, i;
if ((train = kzalloc(sizeof(*train), GFP_KERNEL))) {
for (i = 0; i < 0x100; i++) {
ret = nve0_ram_train_type(pfb, i, ramcfg, train);
if (ret && ret != -ENOENT)
break;
}
}
switch (pfb->ram->type) {
case NV_MEM_TYPE_GDDR5:
ret = nve0_ram_train_init_0(pfb, train);
break;
default:
ret = 0;
break;
}
kfree(train);
return ret;
}
int int
nve0_ram_init(struct nouveau_object *object) nve0_ram_init(struct nouveau_object *object)
{ {
struct nouveau_fb *pfb = (void *)object->parent; struct nouveau_fb *pfb = (void *)object->parent;
struct nve0_ram *ram = (void *)object; struct nve0_ram *ram = (void *)object;
struct nouveau_bios *bios = nouveau_bios(pfb); struct nouveau_bios *bios = nouveau_bios(pfb);
static const u8 train0[] = {
0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
};
static const u32 train1[] = {
0x00000000, 0xffffffff,
0x55555555, 0xaaaaaaaa,
0x33333333, 0xcccccccc,
0xf0f0f0f0, 0x0f0f0f0f,
0x00ff00ff, 0xff00ff00,
0x0000ffff, 0xffff0000,
};
u8 ver, hdr, cnt, len, snr, ssz; u8 ver, hdr, cnt, len, snr, ssz;
u32 data, save; u32 data, save;
int ret, i; int ret, i;
@ -1168,51 +1343,107 @@ nve0_ram_init(struct nouveau_object *object)
cnt = nv_ro08(bios, data + 0x14); /* guess at count */ cnt = nv_ro08(bios, data + 0x14); /* guess at count */
data = nv_ro32(bios, data + 0x10); /* guess u32... */ data = nv_ro32(bios, data + 0x10); /* guess u32... */
save = nv_rd32(pfb, 0x10f65c); save = nv_rd32(pfb, 0x10f65c) & 0x000000f0;
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++, data += 4) {
nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4); if (i != save >> 4) {
nvbios_exec(&(struct nvbios_init) { nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4);
.subdev = nv_subdev(pfb), nvbios_exec(&(struct nvbios_init) {
.bios = bios, .subdev = nv_subdev(pfb),
.offset = nv_ro32(bios, data), /* guess u32 */ .bios = bios,
.execute = 1, .offset = nv_ro32(bios, data),
}); .execute = 1,
data += 4; });
}
} }
nv_wr32(pfb, 0x10f65c, save); nv_mask(pfb, 0x10f65c, 0x000000f0, save);
nv_mask(pfb, 0x10f584, 0x11000000, 0x00000000); nv_mask(pfb, 0x10f584, 0x11000000, 0x00000000);
nv_wr32(pfb, 0x10ecc0, 0xffffffff);
nv_mask(pfb, 0x10f160, 0x00000010, 0x00000010);
switch (ram->base.type) { return nve0_ram_train_init(pfb);
case NV_MEM_TYPE_GDDR5: }
for (i = 0; i < 0x30; i++) {
nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8));
nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]);
nv_wr32(pfb, 0x10f918, train1[i % 12]);
nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]);
nv_wr32(pfb, 0x10f918, train1[i % 12]);
nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8)); static int
nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]); nve0_ram_ctor_data(struct nve0_ram *ram, u8 ramcfg, int i)
nv_wr32(pfb, 0x10f91c, train1[i % 12]); {
nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]); struct nouveau_fb *pfb = (void *)nv_object(ram)->parent;
nv_wr32(pfb, 0x10f91c, train1[i % 12]); struct nouveau_bios *bios = nouveau_bios(pfb);
} struct nouveau_ram_data *cfg;
struct nvbios_ramcfg *d = &ram->diff;
struct nvbios_ramcfg *p, *n;
u8 ver, hdr, cnt, len;
u32 data;
int ret;
for (i = 0; i < 0x100; i++) { if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL)))
nv_wr32(pfb, 0x10f968, i); return -ENOMEM;
nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]); p = &list_last_entry(&ram->cfg, typeof(*cfg), head)->bios;
} n = &cfg->bios;
for (i = 0; i < 0x100; i++) { /* memory config data for a range of target frequencies */
nv_wr32(pfb, 0x10f96c, i); data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios);
nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]); if (ret = -ENOENT, !data)
} goto done;
break; if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12)
default: goto done;
break;
/* ... and a portion specific to the attached memory */
data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg,
&ver, &hdr, &cfg->bios);
if (ret = -EINVAL, !data)
goto done;
if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a)
goto done;
/* lookup memory timings, if bios says they're present */
if (cfg->bios.ramcfg_timing != 0xff) {
data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing,
&ver, &hdr, &cnt, &len,
&cfg->bios);
if (ret = -EINVAL, !data)
goto done;
if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33)
goto done;
} }
return 0; list_add_tail(&cfg->head, &ram->cfg);
if (ret = 0, i == 0)
goto done;
d->rammap_11_0a_03fe |= p->rammap_11_0a_03fe != n->rammap_11_0a_03fe;
d->rammap_11_09_01ff |= p->rammap_11_09_01ff != n->rammap_11_09_01ff;
d->rammap_11_0a_0400 |= p->rammap_11_0a_0400 != n->rammap_11_0a_0400;
d->rammap_11_0a_0800 |= p->rammap_11_0a_0800 != n->rammap_11_0a_0800;
d->rammap_11_0b_01f0 |= p->rammap_11_0b_01f0 != n->rammap_11_0b_01f0;
d->rammap_11_0b_0200 |= p->rammap_11_0b_0200 != n->rammap_11_0b_0200;
d->rammap_11_0d |= p->rammap_11_0d != n->rammap_11_0d;
d->rammap_11_0f |= p->rammap_11_0f != n->rammap_11_0f;
d->rammap_11_0e |= p->rammap_11_0e != n->rammap_11_0e;
d->rammap_11_0b_0800 |= p->rammap_11_0b_0800 != n->rammap_11_0b_0800;
d->rammap_11_0b_0400 |= p->rammap_11_0b_0400 != n->rammap_11_0b_0400;
d->ramcfg_11_01_01 |= p->ramcfg_11_01_01 != n->ramcfg_11_01_01;
d->ramcfg_11_01_02 |= p->ramcfg_11_01_02 != n->ramcfg_11_01_02;
d->ramcfg_11_01_10 |= p->ramcfg_11_01_10 != n->ramcfg_11_01_10;
d->ramcfg_11_02_03 |= p->ramcfg_11_02_03 != n->ramcfg_11_02_03;
d->ramcfg_11_08_20 |= p->ramcfg_11_08_20 != n->ramcfg_11_08_20;
d->timing_20_30_07 |= p->timing_20_30_07 != n->timing_20_30_07;
done:
if (ret)
kfree(cfg);
return ret;
}
static void
nve0_ram_dtor(struct nouveau_object *object)
{
struct nve0_ram *ram = (void *)object;
struct nouveau_ram_data *cfg, *tmp;
list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) {
kfree(cfg);
}
nouveau_ram_destroy(&ram->base);
} }
static int static int
@ -1226,6 +1457,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct dcb_gpio_func func; struct dcb_gpio_func func;
struct nve0_ram *ram; struct nve0_ram *ram;
int ret, i; int ret, i;
u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb));
u32 tmp; u32 tmp;
ret = nvc0_ram_create(parent, engine, oclass, 0x022554, &ram); ret = nvc0_ram_create(parent, engine, oclass, 0x022554, &ram);
@ -1233,6 +1465,8 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
INIT_LIST_HEAD(&ram->cfg);
switch (ram->base.type) { switch (ram->base.type) {
case NV_MEM_TYPE_DDR3: case NV_MEM_TYPE_DDR3:
case NV_MEM_TYPE_GDDR5: case NV_MEM_TYPE_GDDR5:
@ -1264,7 +1498,26 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
} }
} }
// parse bios data for both pll's /* parse bios data for all rammap table entries up-front, and
* build information on whether certain fields differ between
* any of the entries.
*
* the binary driver appears to completely ignore some fields
* when all entries contain the same value. at first, it was
* hoped that these were mere optimisations and the bios init
* tables had configured as per the values here, but there is
* evidence now to suggest that this isn't the case and we do
* need to treat this condition as a "don't touch" indicator.
*/
for (i = 0; !ret; i++) {
ret = nve0_ram_ctor_data(ram, ramcfg, i);
if (ret && ret != -ENOENT) {
nv_error(pfb, "failed to parse ramcfg data\n");
return ret;
}
}
/* parse bios data for both pll's */
ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll); ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll);
if (ret) { if (ret) {
nv_error(pfb, "mclk refpll data not found\n"); nv_error(pfb, "mclk refpll data not found\n");
@ -1277,6 +1530,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return ret; return ret;
} }
/* lookup memory voltage gpios */
ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func); ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func);
if (ret == 0) { if (ret == 0) {
ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04)); ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04));
@ -1385,7 +1639,7 @@ nve0_ram_oclass = {
.handle = 0, .handle = 0,
.ofuncs = &(struct nouveau_ofuncs) { .ofuncs = &(struct nouveau_ofuncs) {
.ctor = nve0_ram_ctor, .ctor = nve0_ram_ctor,
.dtor = _nouveau_ram_dtor, .dtor = nve0_ram_dtor,
.init = nve0_ram_init, .init = nve0_ram_init,
.fini = _nouveau_ram_fini, .fini = _nouveau_ram_fini,
} }

View File

@ -0,0 +1,94 @@
/*
* Copyright 2014 Roy Spliet
*
* 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: Roy Spliet <rspliet@eclipso.eu>
* Ben Skeggs
*/
#include "priv.h"
struct ramxlat {
int id;
u8 enc;
};
static inline int
ramxlat(const struct ramxlat *xlat, int id)
{
while (xlat->id >= 0) {
if (xlat->id == id)
return xlat->enc;
xlat++;
}
return -EINVAL;
}
static const struct ramxlat
ramddr2_cl[] = {
{ 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 },
/* The following are available in some, but not all DDR2 docs */
{ 7, 7 },
{ -1 }
};
static const struct ramxlat
ramddr2_wr[] = {
{ 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 },
/* The following are available in some, but not all DDR2 docs */
{ 7, 6 },
{ -1 }
};
int
nouveau_sddr2_calc(struct nouveau_ram *ram)
{
int CL, WR, DLL = 0, ODT = 0;
switch (ram->next->bios.timing_ver) {
case 0x10:
CL = ram->next->bios.timing_10_CL;
WR = ram->next->bios.timing_10_WR;
DLL = !ram->next->bios.ramcfg_10_02_40;
ODT = ram->next->bios.timing_10_ODT & 3;
break;
case 0x20:
CL = (ram->next->bios.timing[1] & 0x0000001f);
WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
break;
default:
return -ENOSYS;
}
CL = ramxlat(ramddr2_cl, CL);
WR = ramxlat(ramddr2_wr, WR);
if (CL < 0 || WR < 0)
return -EINVAL;
ram->mr[0] &= ~0xf70;
ram->mr[0] |= (WR & 0x07) << 9;
ram->mr[0] |= (CL & 0x07) << 4;
ram->mr[1] &= ~0x045;
ram->mr[1] |= (ODT & 0x1) << 2;
ram->mr[1] |= (ODT & 0x2) << 5;
ram->mr[1] |= !DLL;
return 0;
}

View File

@ -20,9 +20,9 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
* *
* Authors: Ben Skeggs <bskeggs@redhat.com> * Authors: Ben Skeggs <bskeggs@redhat.com>
* Roy Spliet <rspliet@eclipso.eu>
*/ */
#include <subdev/bios.h>
#include "priv.h" #include "priv.h"
struct ramxlat { struct ramxlat {
@ -69,31 +69,52 @@ ramddr3_cwl[] = {
int int
nouveau_sddr3_calc(struct nouveau_ram *ram) nouveau_sddr3_calc(struct nouveau_ram *ram)
{ {
struct nouveau_bios *bios = nouveau_bios(ram); int CWL, CL, WR, DLL = 0, ODT = 0;
int WL, CL, WR;
switch (!!ram->timing.data * ram->timing.version) { switch (ram->next->bios.timing_ver) {
case 0x10:
if (ram->next->bios.timing_hdr < 0x17) {
/* XXX: NV50: Get CWL from the timing register */
return -ENOSYS;
}
CWL = ram->next->bios.timing_10_CWL;
CL = ram->next->bios.timing_10_CL;
WR = ram->next->bios.timing_10_WR;
DLL = !ram->next->bios.ramcfg_10_02_40;
ODT = ram->next->bios.timing_10_ODT;
break;
case 0x20: case 0x20:
WL = (nv_ro16(bios, ram->timing.data + 0x04) & 0x0f80) >> 7; CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7;
CL = nv_ro08(bios, ram->timing.data + 0x04) & 0x1f; CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0;
WR = nv_ro08(bios, ram->timing.data + 0x0a) & 0x7f; WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
/* XXX: Get these values from the VBIOS instead */
DLL = !(ram->mr[1] & 0x1);
ODT = (ram->mr[1] & 0x004) >> 2 |
(ram->mr[1] & 0x040) >> 5 |
(ram->mr[1] & 0x200) >> 7;
break; break;
default: default:
return -ENOSYS; return -ENOSYS;
} }
WL = ramxlat(ramddr3_cwl, WL); CWL = ramxlat(ramddr3_cwl, CWL);
CL = ramxlat(ramddr3_cl, CL); CL = ramxlat(ramddr3_cl, CL);
WR = ramxlat(ramddr3_wr, WR); WR = ramxlat(ramddr3_wr, WR);
if (WL < 0 || CL < 0 || WR < 0) if (CL < 0 || CWL < 0 || WR < 0)
return -EINVAL; return -EINVAL;
ram->mr[0] &= ~0xe74; ram->mr[0] &= ~0xf74;
ram->mr[0] |= (WR & 0x07) << 9; ram->mr[0] |= (WR & 0x07) << 9;
ram->mr[0] |= (CL & 0x0e) << 3; ram->mr[0] |= (CL & 0x0e) << 3;
ram->mr[0] |= (CL & 0x01) << 2; ram->mr[0] |= (CL & 0x01) << 2;
ram->mr[1] &= ~0x245;
ram->mr[1] |= (ODT & 0x1) << 2;
ram->mr[1] |= (ODT & 0x2) << 5;
ram->mr[1] |= (ODT & 0x4) << 7;
ram->mr[1] |= !DLL;
ram->mr[2] &= ~0x038; ram->mr[2] &= ~0x038;
ram->mr[2] |= (WL & 0x07) << 3; ram->mr[2] |= (CWL & 0x07) << 3;
return 0; return 0;
} }

View File

@ -0,0 +1,54 @@
/*
* Copyright 2014 Martin Peres
*
* 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: Martin Peres
*/
#include <subdev/fuse.h>
int
_nouveau_fuse_init(struct nouveau_object *object)
{
struct nouveau_fuse *fuse = (void *)object;
return nouveau_subdev_init(&fuse->base);
}
void
_nouveau_fuse_dtor(struct nouveau_object *object)
{
struct nouveau_fuse *fuse = (void *)object;
nouveau_subdev_destroy(&fuse->base);
}
int
nouveau_fuse_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, int length, void **pobject)
{
struct nouveau_fuse *fuse;
int ret;
ret = nouveau_subdev_create_(parent, engine, oclass, 0, "FUSE",
"fuse", length, pobject);
fuse = *pobject;
return ret;
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2014 Martin Peres
*
* 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: Martin Peres
*/
#include "priv.h"
struct g80_fuse_priv {
struct nouveau_fuse base;
spinlock_t fuse_enable_lock;
};
static u32
g80_fuse_rd32(struct nouveau_object *object, u64 addr)
{
struct g80_fuse_priv *priv = (void *)object;
unsigned long flags;
u32 fuse_enable, val;
spin_lock_irqsave(&priv->fuse_enable_lock, flags);
/* racy if another part of nouveau start writing to this reg */
fuse_enable = nv_mask(priv, 0x1084, 0x800, 0x800);
val = nv_rd32(priv, 0x21000 + addr);
nv_wr32(priv, 0x1084, fuse_enable);
spin_unlock_irqrestore(&priv->fuse_enable_lock, flags);
return val;
}
static int
g80_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct g80_fuse_priv *priv;
int ret;
ret = nouveau_fuse_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
spin_lock_init(&priv->fuse_enable_lock);
return 0;
}
struct nouveau_oclass
g80_fuse_oclass = {
.handle = NV_SUBDEV(FUSE, 0x50),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = g80_fuse_ctor,
.dtor = _nouveau_fuse_dtor,
.init = _nouveau_fuse_init,
.fini = _nouveau_fuse_fini,
.rd32 = g80_fuse_rd32,
},
};

View File

@ -0,0 +1,83 @@
/*
* Copyright 2014 Martin Peres
*
* 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: Martin Peres
*/
#include "priv.h"
struct gf100_fuse_priv {
struct nouveau_fuse base;
spinlock_t fuse_enable_lock;
};
static u32
gf100_fuse_rd32(struct nouveau_object *object, u64 addr)
{
struct gf100_fuse_priv *priv = (void *)object;
unsigned long flags;
u32 fuse_enable, unk, val;
spin_lock_irqsave(&priv->fuse_enable_lock, flags);
/* racy if another part of nouveau start writing to these regs */
fuse_enable = nv_mask(priv, 0x22400, 0x800, 0x800);
unk = nv_mask(priv, 0x21000, 0x1, 0x1);
val = nv_rd32(priv, 0x21100 + addr);
nv_wr32(priv, 0x21000, unk);
nv_wr32(priv, 0x22400, fuse_enable);
spin_unlock_irqrestore(&priv->fuse_enable_lock, flags);
return val;
}
static int
gf100_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct gf100_fuse_priv *priv;
int ret;
ret = nouveau_fuse_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
spin_lock_init(&priv->fuse_enable_lock);
return 0;
}
struct nouveau_oclass
gf100_fuse_oclass = {
.handle = NV_SUBDEV(FUSE, 0xC0),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = gf100_fuse_ctor,
.dtor = _nouveau_fuse_dtor,
.init = _nouveau_fuse_init,
.fini = _nouveau_fuse_fini,
.rd32 = gf100_fuse_rd32,
},
};

View File

@ -0,0 +1,66 @@
/*
* Copyright 2014 Martin Peres
*
* 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: Martin Peres
*/
#include "priv.h"
struct gm107_fuse_priv {
struct nouveau_fuse base;
};
static u32
gm107_fuse_rd32(struct nouveau_object *object, u64 addr)
{
struct gf100_fuse_priv *priv = (void *)object;
return nv_rd32(priv, 0x21100 + addr);
}
static int
gm107_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
struct gm107_fuse_priv *priv;
int ret;
ret = nouveau_fuse_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
return 0;
}
struct nouveau_oclass
gm107_fuse_oclass = {
.handle = NV_SUBDEV(FUSE, 0x117),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = gm107_fuse_ctor,
.dtor = _nouveau_fuse_dtor,
.init = _nouveau_fuse_init,
.fini = _nouveau_fuse_fini,
.rd32 = gm107_fuse_rd32,
},
};

View File

@ -0,0 +1,9 @@
#ifndef __NVKM_FUSE_PRIV_H__
#define __NVKM_FUSE_PRIV_H__
#include <subdev/fuse.h>
int _nouveau_fuse_init(struct nouveau_object *object);
void _nouveau_fuse_dtor(struct nouveau_object *object);
#endif

View File

@ -122,7 +122,8 @@ nouveau_gpio_intr_init(struct nvkm_event *event, int type, int index)
} }
static int static int
nouveau_gpio_intr_ctor(void *data, u32 size, struct nvkm_notify *notify) nouveau_gpio_intr_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{ {
struct nvkm_gpio_ntfy_req *req = data; struct nvkm_gpio_ntfy_req *req = data;
if (!WARN_ON(size != sizeof(*req))) { if (!WARN_ON(size != sizeof(*req))) {

View File

@ -25,7 +25,7 @@
#include "priv.h" #include "priv.h"
void void
nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) nv94_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
{ {
u32 intr0 = nv_rd32(gpio, 0x00e054); u32 intr0 = nv_rd32(gpio, 0x00e054);
u32 intr1 = nv_rd32(gpio, 0x00e074); u32 intr1 = nv_rd32(gpio, 0x00e074);
@ -38,7 +38,7 @@ nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
} }
void void
nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) nv94_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
{ {
u32 inte0 = nv_rd32(gpio, 0x00e050); u32 inte0 = nv_rd32(gpio, 0x00e050);
u32 inte1 = nv_rd32(gpio, 0x00e070); u32 inte1 = nv_rd32(gpio, 0x00e070);
@ -57,8 +57,8 @@ nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
} }
struct nouveau_oclass * struct nouveau_oclass *
nv92_gpio_oclass = &(struct nouveau_gpio_impl) { nv94_gpio_oclass = &(struct nouveau_gpio_impl) {
.base.handle = NV_SUBDEV(GPIO, 0x92), .base.handle = NV_SUBDEV(GPIO, 0x94),
.base.ofuncs = &(struct nouveau_ofuncs) { .base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = _nouveau_gpio_ctor, .ctor = _nouveau_gpio_ctor,
.dtor = _nouveau_gpio_dtor, .dtor = _nouveau_gpio_dtor,
@ -66,8 +66,8 @@ nv92_gpio_oclass = &(struct nouveau_gpio_impl) {
.fini = _nouveau_gpio_fini, .fini = _nouveau_gpio_fini,
}, },
.lines = 32, .lines = 32,
.intr_stat = nv92_gpio_intr_stat, .intr_stat = nv94_gpio_intr_stat,
.intr_mask = nv92_gpio_intr_mask, .intr_mask = nv94_gpio_intr_mask,
.drive = nv50_gpio_drive, .drive = nv50_gpio_drive,
.sense = nv50_gpio_sense, .sense = nv50_gpio_sense,
.reset = nv50_gpio_reset, .reset = nv50_gpio_reset,

View File

@ -77,8 +77,8 @@ nvd0_gpio_oclass = &(struct nouveau_gpio_impl) {
.fini = _nouveau_gpio_fini, .fini = _nouveau_gpio_fini,
}, },
.lines = 32, .lines = 32,
.intr_stat = nv92_gpio_intr_stat, .intr_stat = nv94_gpio_intr_stat,
.intr_mask = nv92_gpio_intr_mask, .intr_mask = nv94_gpio_intr_mask,
.drive = nvd0_gpio_drive, .drive = nvd0_gpio_drive,
.sense = nvd0_gpio_sense, .sense = nvd0_gpio_sense,
.reset = nvd0_gpio_reset, .reset = nvd0_gpio_reset,

View File

@ -56,8 +56,8 @@ void nv50_gpio_reset(struct nouveau_gpio *, u8);
int nv50_gpio_drive(struct nouveau_gpio *, int, int, int); int nv50_gpio_drive(struct nouveau_gpio *, int, int, int);
int nv50_gpio_sense(struct nouveau_gpio *, int); int nv50_gpio_sense(struct nouveau_gpio *, int);
void nv92_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *); void nv94_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *);
void nv92_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32); void nv94_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32);
void nvd0_gpio_reset(struct nouveau_gpio *, u8); void nvd0_gpio_reset(struct nouveau_gpio *, u8);
int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int); int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int);

View File

@ -23,6 +23,7 @@
*/ */
#include <core/option.h> #include <core/option.h>
#include <core/object.h>
#include <core/event.h> #include <core/event.h>
#include <subdev/bios.h> #include <subdev/bios.h>
@ -346,7 +347,8 @@ nouveau_i2c_intr_init(struct nvkm_event *event, int type, int index)
} }
static int static int
nouveau_i2c_intr_ctor(void *data, u32 size, struct nvkm_notify *notify) nouveau_i2c_intr_ctor(struct nouveau_object *object, void *data, u32 size,
struct nvkm_notify *notify)
{ {
struct nvkm_i2c_ntfy_req *req = data; struct nvkm_i2c_ntfy_req *req = data;
if (!WARN_ON(size != sizeof(*req))) { if (!WARN_ON(size != sizeof(*req))) {

View File

@ -69,7 +69,7 @@ nv04_instobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret) if (ret)
return ret; return ret;
ret = nouveau_mm_head(&priv->heap, 1, args->size, args->size, ret = nouveau_mm_head(&priv->heap, 0, 1, args->size, args->size,
args->align, &node->mem); args->align, &node->mem);
if (ret) if (ret)
return ret; return ret;

View File

@ -31,7 +31,7 @@ nvkm_ltc_tags_alloc(struct nouveau_ltc *ltc, u32 n,
struct nvkm_ltc_priv *priv = (void *)ltc; struct nvkm_ltc_priv *priv = (void *)ltc;
int ret; int ret;
ret = nouveau_mm_head(&priv->tags, 1, n, n, 1, pnode); ret = nouveau_mm_head(&priv->tags, 0, 1, n, n, 1, pnode);
if (ret) if (ret)
*pnode = NULL; *pnode = NULL;

View File

@ -62,16 +62,38 @@ gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth)
nv_wr32(priv, 0x17ea58, depth); nv_wr32(priv, 0x17ea58, depth);
} }
static const struct nouveau_bitfield
gf100_ltc_lts_intr_name[] = {
{ 0x00000001, "IDLE_ERROR_IQ" },
{ 0x00000002, "IDLE_ERROR_CBC" },
{ 0x00000004, "IDLE_ERROR_TSTG" },
{ 0x00000008, "IDLE_ERROR_DSTG" },
{ 0x00000010, "EVICTED_CB" },
{ 0x00000020, "ILLEGAL_COMPSTAT" },
{ 0x00000040, "BLOCKLINEAR_CB" },
{ 0x00000100, "ECC_SEC_ERROR" },
{ 0x00000200, "ECC_DED_ERROR" },
{ 0x00000400, "DEBUG" },
{ 0x00000800, "ATOMIC_TO_Z" },
{ 0x00001000, "ILLEGAL_ATOMIC" },
{ 0x00002000, "BLKACTIVITY_ERR" },
{}
};
static void static void
gf100_ltc_lts_isr(struct nvkm_ltc_priv *priv, int ltc, int lts) gf100_ltc_lts_intr(struct nvkm_ltc_priv *priv, int ltc, int lts)
{ {
u32 base = 0x141000 + (ltc * 0x2000) + (lts * 0x400); u32 base = 0x141000 + (ltc * 0x2000) + (lts * 0x400);
u32 stat = nv_rd32(priv, base + 0x020); u32 intr = nv_rd32(priv, base + 0x020);
u32 stat = intr & 0x0000ffff;
if (stat) { if (stat) {
nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", ltc, lts, stat); nv_info(priv, "LTC%d_LTS%d:", ltc, lts);
nv_wr32(priv, base + 0x020, stat); nouveau_bitfield_print(gf100_ltc_lts_intr_name, stat);
pr_cont("\n");
} }
nv_wr32(priv, base + 0x020, intr);
} }
void void
@ -84,14 +106,9 @@ gf100_ltc_intr(struct nouveau_subdev *subdev)
while (mask) { while (mask) {
u32 lts, ltc = __ffs(mask); u32 lts, ltc = __ffs(mask);
for (lts = 0; lts < priv->lts_nr; lts++) for (lts = 0; lts < priv->lts_nr; lts++)
gf100_ltc_lts_isr(priv, ltc, lts); gf100_ltc_lts_intr(priv, ltc, lts);
mask &= ~(1 << ltc); mask &= ~(1 << ltc);
} }
/* we do something horribly wrong and upset PMFB a lot, so mask off
* interrupts from it after the first one until it's fixed
*/
nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
} }
static int static int
@ -151,7 +168,7 @@ gf100_ltc_init_tag_ram(struct nouveau_fb *pfb, struct nvkm_ltc_priv *priv)
tag_size += tag_align; tag_size += tag_align;
tag_size = (tag_size + 0xfff) >> 12; /* round up */ tag_size = (tag_size + 0xfff) >> 12; /* round up */
ret = nouveau_mm_tail(&pfb->vram, 1, tag_size, tag_size, 1, ret = nouveau_mm_tail(&pfb->vram, 1, 1, tag_size, tag_size, 1,
&priv->tag_ram); &priv->tag_ram);
if (ret) { if (ret) {
priv->num_tags = 0; priv->num_tags = 0;

View File

@ -87,11 +87,6 @@ gm107_ltc_intr(struct nouveau_subdev *subdev)
gm107_ltc_lts_isr(priv, ltc, lts); gm107_ltc_lts_isr(priv, ltc, lts);
mask &= ~(1 << ltc); mask &= ~(1 << ltc);
} }
/* we do something horribly wrong and upset PMFB a lot, so mask off
* interrupts from it after the first one until it's fixed
*/
nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
} }
static int static int

View File

@ -4,6 +4,8 @@
#include <subdev/ltc.h> #include <subdev/ltc.h>
#include <subdev/fb.h> #include <subdev/fb.h>
#include <core/enum.h>
struct nvkm_ltc_priv { struct nvkm_ltc_priv {
struct nouveau_ltc base; struct nouveau_ltc base;
u32 ltc_nr; u32 ltc_nr;

View File

@ -203,6 +203,8 @@ _nouveau_pwr_init(struct nouveau_object *object)
nv_wait(ppwr, 0x10a04c, 0xffffffff, 0x00000000); nv_wait(ppwr, 0x10a04c, 0xffffffff, 0x00000000);
nv_mask(ppwr, 0x000200, 0x00002000, 0x00000000); nv_mask(ppwr, 0x000200, 0x00002000, 0x00000000);
nv_mask(ppwr, 0x000200, 0x00002000, 0x00002000); nv_mask(ppwr, 0x000200, 0x00002000, 0x00002000);
nv_rd32(ppwr, 0x000200);
nv_wait(ppwr, 0x10a10c, 0x00000006, 0x00000000);
/* upload data segment */ /* upload data segment */
nv_wr32(ppwr, 0x10a1c0, 0x01000000); nv_wr32(ppwr, 0x10a1c0, 0x01000000);

View File

@ -0,0 +1,94 @@
/*
* Copyright 2014 Martin Peres <martin.peres@free.fr>
*
* 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 folloing 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: Martin Peres
*/
/******************************************************************************
* arith data segment
*****************************************************************************/
#ifdef INCLUDE_PROC
#endif
#ifdef INCLUDE_DATA
#endif
/******************************************************************************
* arith code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// does a 32x32 -> 64 multiplication
//
// A * B = A_lo * B_lo
// + ( A_hi * B_lo ) << 16
// + ( A_lo * B_hi ) << 16
// + ( A_hi * B_hi ) << 32
//
// $r15 - current
// $r14 - A
// $r13 - B
// $r12 - mul_lo (return)
// $r11 - mul_hi (return)
// $r0 - zero
mulu32_32_64:
push $r1 // A_hi
push $r2 // B_hi
push $r3 // tmp0
push $r4 // tmp1
shr b32 $r1 $r14 16
shr b32 $r2 $r13 16
clear b32 $r12
clear b32 $r11
// A_lo * B_lo
mulu $r12 $r14 $r13
// ( A_hi * B_lo ) << 16
mulu $r3 $r1 $r13 // tmp0 = A_hi * B_lo
mov b32 $r4 $r3
and $r3 0xffff // tmp0 = tmp0_lo
shl b32 $r3 16
shr b32 $r4 16 // tmp1 = tmp0_hi
add b32 $r12 $r3
adc b32 $r11 $r4
// ( A_lo * B_hi ) << 16
mulu $r3 $r14 $r2 // tmp0 = A_lo * B_hi
mov b32 $r4 $r3
and $r3 0xffff // tmp0 = tmp0_lo
shl b32 $r3 16
shr b32 $r4 16 // tmp1 = tmp0_hi
add b32 $r12 $r3
adc b32 $r11 $r4
// ( A_hi * B_hi ) << 32
mulu $r3 $r1 $r2 // tmp0 = A_hi * B_hi
add b32 $r11 $r3
pop $r4
pop $r3
pop $r2
pop $r1
ret
#endif

View File

@ -98,12 +98,16 @@ wr32:
// $r14 - ns // $r14 - ns
// $r0 - zero // $r0 - zero
nsec: nsec:
push $r9
push $r8
nv_iord($r8, NV_PPWR_TIMER_LOW) nv_iord($r8, NV_PPWR_TIMER_LOW)
nsec_loop: nsec_loop:
nv_iord($r9, NV_PPWR_TIMER_LOW) nv_iord($r9, NV_PPWR_TIMER_LOW)
sub b32 $r9 $r8 sub b32 $r9 $r8
cmp b32 $r9 $r14 cmp b32 $r9 $r14
bra l #nsec_loop bra l #nsec_loop
pop $r8
pop $r9
ret ret
// busy-wait for a period of time // busy-wait for a period of time
@ -115,6 +119,8 @@ nsec:
// $r11 - timeout (ns) // $r11 - timeout (ns)
// $r0 - zero // $r0 - zero
wait: wait:
push $r9
push $r8
nv_iord($r8, NV_PPWR_TIMER_LOW) nv_iord($r8, NV_PPWR_TIMER_LOW)
wait_loop: wait_loop:
nv_rd32($r10, $r14) nv_rd32($r10, $r14)
@ -126,6 +132,8 @@ wait:
cmp b32 $r9 $r11 cmp b32 $r9 $r11
bra l #wait_loop bra l #wait_loop
wait_done: wait_done:
pop $r8
pop $r9
ret ret
// $r15 - current (kern) // $r15 - current (kern)
@ -242,12 +250,89 @@ intr:
bclr $flags $p0 bclr $flags $p0
iret iret
// request the current process be sent a message after a timeout expires // calculate the number of ticks in the specified nanoseconds delay
//
// $r15 - current
// $r14 - ns
// $r14 - ticks (return)
// $r0 - zero
ticks_from_ns:
push $r12
push $r11
/* try not losing precision (multiply then divide) */
imm32($r13, HW_TICKS_PER_US)
call #mulu32_32_64
/* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */
div $r12 $r12 1000
/* check if there wasn't any overflow */
cmpu b32 $r11 0
bra e #ticks_from_ns_quit
/* let's divide then multiply, too bad for the precision! */
div $r14 $r14 1000
imm32($r13, HW_TICKS_PER_US)
call #mulu32_32_64
/* this cannot overflow as long as HW_TICKS_PER_US < 1000 */
ticks_from_ns_quit:
mov b32 $r14 $r12
pop $r11
pop $r12
ret
// calculate the number of ticks in the specified microsecond delay
//
// $r15 - current
// $r14 - us
// $r14 - ticks (return)
// $r0 - zero
ticks_from_us:
push $r12
push $r11
/* simply multiply $us by HW_TICKS_PER_US */
imm32($r13, HW_TICKS_PER_US)
call #mulu32_32_64
mov b32 $r14 $r12
/* check if there wasn't any overflow */
cmpu b32 $r11 0
bra e #ticks_from_us_quit
/* Overflow! */
clear b32 $r14
ticks_from_us_quit:
pop $r11
pop $r12
ret
// calculate the number of ticks in the specified microsecond delay
// //
// $r15 - current // $r15 - current
// $r14 - ticks // $r14 - ticks
// $r14 - us (return)
// $r0 - zero
ticks_to_us:
/* simply divide $ticks by HW_TICKS_PER_US */
imm32($r13, HW_TICKS_PER_US)
div $r14 $r14 $r13
ret
// request the current process be sent a message after a timeout expires
//
// $r15 - current
// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)
// $r0 - zero // $r0 - zero
timer: timer:
push $r9
push $r8
// interrupts off to prevent racing with timer isr // interrupts off to prevent racing with timer isr
bclr $flags ie0 bclr $flags ie0
@ -255,13 +340,22 @@ timer:
ld b32 $r8 D[$r15 + #proc_time] ld b32 $r8 D[$r15 + #proc_time]
cmp b32 $r8 0 cmp b32 $r8 0
bra g #timer_done bra g #timer_done
// halt watchdog timer temporarily
clear b32 $r8
nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
// find out how much time elapsed since the last update
// of the watchdog and add this time to the wanted ticks
nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
ld b32 $r9 D[$r0 + #time_prev]
sub b32 $r9 $r8
add b32 $r14 $r9
st b32 D[$r15 + #proc_time] $r14 st b32 D[$r15 + #proc_time] $r14
// halt watchdog timer temporarily and check for a pending // check for a pending interrupt. if there's one already
// interrupt. if there's one already pending, we can just // pending, we can just bail since the timer isr will
// bail since the timer isr will queue the next soonest // queue the next soonest right after it's done
// right after it's done
nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
nv_iord($r8, NV_PPWR_INTR) nv_iord($r8, NV_PPWR_INTR)
and $r8 NV_PPWR_INTR_WATCHDOG and $r8 NV_PPWR_INTR_WATCHDOG
bra nz #timer_enable bra nz #timer_enable
@ -272,10 +366,10 @@ timer:
cmp b32 $r14 $r0 cmp b32 $r14 $r0
bra e #timer_reset bra e #timer_reset
cmp b32 $r14 $r8 cmp b32 $r14 $r8
bra l #timer_done bra g #timer_enable
timer_reset: timer_reset:
nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14) nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
st b32 D[$r0 + #time_prev] $r14 st b32 D[$r0 + #time_prev] $r14
// re-enable the watchdog timer // re-enable the watchdog timer
timer_enable: timer_enable:
@ -285,6 +379,9 @@ timer:
// interrupts back on // interrupts back on
timer_done: timer_done:
bset $flags ie0 bset $flags ie0
pop $r8
pop $r9
ret ret
// send message to another process // send message to another process
@ -371,6 +468,9 @@ send:
// $r14 - process // $r14 - process
// $r0 - zero // $r0 - zero
recv: recv:
push $r9
push $r8
ld b32 $r8 D[$r14 + #proc_qget] ld b32 $r8 D[$r14 + #proc_qget]
ld b32 $r9 D[$r14 + #proc_qput] ld b32 $r9 D[$r14 + #proc_qput]
bclr $flags $p1 bclr $flags $p1
@ -403,6 +503,8 @@ recv:
bset $flags $p1 bset $flags $p1
pop $r15 pop $r15
recv_done: recv_done:
pop $r8
pop $r9
ret ret
init: init:

View File

@ -250,3 +250,23 @@
*/ st b32 D[$r0] reg /* */ st b32 D[$r0] reg /*
*/ clear b32 $r0 */ clear b32 $r0
#endif #endif
#define st(size, addr, reg) /*
*/ movw $r0 addr /*
*/ st size D[$r0] reg /*
*/ clear b32 $r0
#define ld(size, reg, addr) /*
*/ movw $r0 addr /*
*/ ld size reg D[$r0] /*
*/ clear b32 $r0
// does a 64+64 -> 64 unsigned addition (C = A + B)
#define addu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
*/ add b32 reg_a_c_lo b_lo /*
*/ adc b32 reg_a_c_hi b_hi
// does a 64+64 -> 64 substraction (C = A - B)
#define subu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
*/ sub b32 reg_a_c_lo b_lo /*
*/ sbb b32 reg_a_c_hi b_hi

View File

@ -43,17 +43,23 @@ process(PROC_MEMX, #memx_init, #memx_recv)
*/ .b32 func */ .b32 func
memx_func_head: memx_func_head:
handler(ENTER , 0x0001, 0x0000, #memx_func_enter) handler(ENTER , 0x0000, 0x0000, #memx_func_enter)
memx_func_next: memx_func_next:
handler(LEAVE , 0x0000, 0x0000, #memx_func_leave) handler(LEAVE , 0x0000, 0x0000, #memx_func_leave)
handler(WR32 , 0x0000, 0x0002, #memx_func_wr32) handler(WR32 , 0x0000, 0x0002, #memx_func_wr32)
handler(WAIT , 0x0004, 0x0000, #memx_func_wait) handler(WAIT , 0x0004, 0x0000, #memx_func_wait)
handler(DELAY , 0x0001, 0x0000, #memx_func_delay) handler(DELAY , 0x0001, 0x0000, #memx_func_delay)
handler(VBLANK, 0x0001, 0x0000, #memx_func_wait_vblank)
memx_func_tail: memx_func_tail:
.equ #memx_func_size #memx_func_next - #memx_func_head .equ #memx_func_size #memx_func_next - #memx_func_head
.equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size .equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size
memx_ts_start:
.b32 0
memx_ts_end:
.b32 0
memx_data_head: memx_data_head:
.skip 0x0800 .skip 0x0800
memx_data_tail: memx_data_tail:
@ -67,19 +73,44 @@ memx_data_tail:
// //
// $r15 - current (memx) // $r15 - current (memx)
// $r4 - packet length // $r4 - packet length
// +00: bitmask of heads to wait for vblank on
// $r3 - opcode desciption // $r3 - opcode desciption
// $r0 - zero // $r0 - zero
memx_func_enter: memx_func_enter:
#if NVKM_PPWR_CHIPSET == GT215
movw $r8 0x1610
nv_rd32($r7, $r8)
imm32($r6, 0xfffffffc)
and $r7 $r6
movw $r6 0x2
or $r7 $r6
nv_wr32($r8, $r7)
#else
movw $r6 0x001620
imm32($r7, ~0x00000aa2);
nv_rd32($r8, $r6)
and $r8 $r7
nv_wr32($r6, $r8)
imm32($r7, ~0x00000001)
nv_rd32($r8, $r6)
and $r8 $r7
nv_wr32($r6, $r8)
movw $r6 0x0026f0
nv_rd32($r8, $r6)
and $r8 $r7
nv_wr32($r6, $r8)
#endif
mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE
nv_iowr(NV_PPWR_OUTPUT_SET, $r6) nv_iowr(NV_PPWR_OUTPUT_SET, $r6)
memx_func_enter_wait: memx_func_enter_wait:
nv_iord($r6, NV_PPWR_OUTPUT) nv_iord($r6, NV_PPWR_OUTPUT)
and $r6 NV_PPWR_OUTPUT_FB_PAUSE and $r6 NV_PPWR_OUTPUT_FB_PAUSE
bra z #memx_func_enter_wait bra z #memx_func_enter_wait
//XXX: TODO
ld b32 $r6 D[$r1 + 0x00] nv_iord($r6, NV_PPWR_TIMER_LOW)
add b32 $r1 0x04 st b32 D[$r0 + #memx_ts_start] $r6
ret ret
// description // description
@ -89,14 +120,93 @@ memx_func_enter:
// $r3 - opcode desciption // $r3 - opcode desciption
// $r0 - zero // $r0 - zero
memx_func_leave: memx_func_leave:
nv_iord($r6, NV_PPWR_TIMER_LOW)
st b32 D[$r0 + #memx_ts_end] $r6
mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE
nv_iowr(NV_PPWR_OUTPUT_CLR, $r6) nv_iowr(NV_PPWR_OUTPUT_CLR, $r6)
memx_func_leave_wait: memx_func_leave_wait:
nv_iord($r6, NV_PPWR_OUTPUT) nv_iord($r6, NV_PPWR_OUTPUT)
and $r6 NV_PPWR_OUTPUT_FB_PAUSE and $r6 NV_PPWR_OUTPUT_FB_PAUSE
bra nz #memx_func_leave_wait bra nz #memx_func_leave_wait
#if NVKM_PPWR_CHIPSET == GT215
movw $r8 0x1610
nv_rd32($r7, $r8)
imm32($r6, 0xffffffcc)
and $r7 $r6
nv_wr32($r8, $r7)
#else
movw $r6 0x0026f0
imm32($r7, 0x00000001)
nv_rd32($r8, $r6)
or $r8 $r7
nv_wr32($r6, $r8)
movw $r6 0x001620
nv_rd32($r8, $r6)
or $r8 $r7
nv_wr32($r6, $r8)
imm32($r7, 0x00000aa2);
nv_rd32($r8, $r6)
or $r8 $r7
nv_wr32($r6, $r8)
#endif
ret ret
#if NVKM_PPWR_CHIPSET < GF119
// description
//
// $r15 - current (memx)
// $r4 - packet length
// +00: head to wait for vblank on
// $r3 - opcode desciption
// $r0 - zero
memx_func_wait_vblank:
ld b32 $r6 D[$r1 + 0x00]
cmp b32 $r6 0x0
bra z #memx_func_wait_vblank_head0
cmp b32 $r6 0x1
bra z #memx_func_wait_vblank_head1
bra #memx_func_wait_vblank_fini
memx_func_wait_vblank_head1:
movw $r7 0x20
bra #memx_func_wait_vblank_0
memx_func_wait_vblank_head0:
movw $r7 0x8
memx_func_wait_vblank_0:
nv_iord($r6, NV_PPWR_INPUT)
and $r6 $r7
bra nz #memx_func_wait_vblank_0
memx_func_wait_vblank_1:
nv_iord($r6, NV_PPWR_INPUT)
and $r6 $r7
bra z #memx_func_wait_vblank_1
memx_func_wait_vblank_fini:
add b32 $r1 0x4
ret
#else
// XXX: currently no-op
//
// $r15 - current (memx)
// $r4 - packet length
// +00: head to wait for vblank on
// $r3 - opcode desciption
// $r0 - zero
memx_func_wait_vblank:
add b32 $r1 0x4
ret
#endif
// description // description
// //
// $r15 - current (memx) // $r15 - current (memx)
@ -160,14 +270,17 @@ memx_exec:
push $r13 push $r13
mov b32 $r1 $r12 mov b32 $r1 $r12
mov b32 $r2 $r11 mov b32 $r2 $r11
memx_exec_next: memx_exec_next:
// fetch the packet header, and locate opcode info // fetch the packet header
ld b32 $r3 D[$r1] ld b32 $r3 D[$r1]
add b32 $r1 4 add b32 $r1 4
shr b32 $r4 $r3 16 extr $r4 $r3 16:31
mulu $r3 #memx_func_size extr $r3 $r3 0:15
// execute the opcode handler // execute the opcode handler
sub b32 $r3 1
mulu $r3 #memx_func_size
ld b32 $r5 D[$r3 + #memx_func_head + #memx_func] ld b32 $r5 D[$r3 + #memx_func_head + #memx_func]
call $r5 call $r5
@ -176,6 +289,10 @@ memx_exec:
bra l #memx_exec_next bra l #memx_exec_next
// send completion reply // send completion reply
ld b32 $r11 D[$r0 + #memx_ts_start]
ld b32 $r12 D[$r0 + #memx_ts_end]
sub b32 $r12 $r11
nv_iord($r11, NV_PPWR_INPUT)
pop $r13 pop $r13
pop $r14 pop $r14
call(send) call(send)

Some files were not shown because too many files have changed in this diff Show More