Skip to content

Commit

Permalink
Merge branch 'linux-5.7' of git://github.com/skeggsb/linux into drm-next
Browse files Browse the repository at this point in the history
A couple of misc fixes/workarounds for some issues that are causing a
lot of pain for people.

Of most interest are the PCI power management and GR init WARs, which
effect a rather significant number of laptop systems that are in use
today.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Ben Skeggs <skeggsb@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/ <CACAvsv5Ef5YKS9EPBH3YUubzvVr++_rzjgSqV_B5nC0L2kB6-Q@mail.gmail.com
  • Loading branch information
Dave Airlie committed Apr 7, 2020
2 parents 3208a24 + 374b558 commit 9c34696
Show file tree
Hide file tree
Showing 23 changed files with 263 additions and 50 deletions.
3 changes: 2 additions & 1 deletion drivers/gpu/drm/nouveau/dispnv04/dac.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@

#include <subdev/bios/gpio.h>
#include <subdev/gpio.h>
#include <subdev/timer.h>

#include <nvif/timer.h>

int nv04_dac_output_offset(struct drm_encoder *encoder)
{
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/dispnv04/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "hw.h"

#include <subdev/bios/pll.h>
#include <nvif/timer.h>

#define CHIPSET_NFORCE 0x01a0
#define CHIPSET_NFORCE2 0x01f0
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/dispnv50/base507c.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include <nvif/cl507c.h>
#include <nvif/event.h>
#include <nvif/timer.h>

#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/dispnv50/core507d.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "head.h"

#include <nvif/cl507d.h>
#include <nvif/timer.h>

#include "nouveau_bo.h"

Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/nouveau/dispnv50/corec37d.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

#include <nouveau_bo.h>

#include <nvif/timer.h>

void
corec37d_wndw_owner(struct nv50_core *core)
{
Expand Down
21 changes: 18 additions & 3 deletions drivers/gpu/drm/nouveau/dispnv50/curs507a.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,36 @@
#include "head.h"

#include <nvif/cl507a.h>
#include <nvif/timer.h>

#include <drm/drm_atomic_helper.h>
#include <drm/drm_plane_helper.h>

bool
curs507a_space(struct nv50_wndw *wndw)
{
nvif_msec(&nouveau_drm(wndw->plane.dev)->client.device, 2,
if (nvif_rd32(&wndw->wimm.base.user, 0x0008) >= 4)
return true;
);
WARN_ON(1);
return false;
}

static void
curs507a_update(struct nv50_wndw *wndw, u32 *interlock)
{
nvif_wr32(&wndw->wimm.base.user, 0x0080, 0x00000000);
if (curs507a_space(wndw))
nvif_wr32(&wndw->wimm.base.user, 0x0080, 0x00000000);
}

static void
curs507a_point(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
nvif_wr32(&wndw->wimm.base.user, 0x0084, asyw->point.y << 16 |
asyw->point.x);
if (curs507a_space(wndw)) {
nvif_wr32(&wndw->wimm.base.user, 0x0084, asyw->point.y << 16 |
asyw->point.x);
}
}

const struct nv50_wimm_func
Expand Down
9 changes: 6 additions & 3 deletions drivers/gpu/drm/nouveau/dispnv50/cursc37a.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
static void
cursc37a_update(struct nv50_wndw *wndw, u32 *interlock)
{
nvif_wr32(&wndw->wimm.base.user, 0x0200, 0x00000001);
if (curs507a_space(wndw))
nvif_wr32(&wndw->wimm.base.user, 0x0200, 0x00000001);
}

static void
cursc37a_point(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
nvif_wr32(&wndw->wimm.base.user, 0x0208, asyw->point.y << 16 |
asyw->point.x);
if (curs507a_space(wndw)) {
nvif_wr32(&wndw->wimm.base.user, 0x0208, asyw->point.y << 16 |
asyw->point.x);
}
}

static const struct nv50_wimm_func
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/dispnv50/disp.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <nvif/cl5070.h>
#include <nvif/cl507d.h>
#include <nvif/event.h>
#include <nvif/timer.h>

#include "nouveau_drv.h"
#include "nouveau_dma.h"
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/nouveau/dispnv50/ovly827e.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

#include <nouveau_bo.h>

#include <nvif/timer.h>

static void
ovly827e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/dispnv50/wndw.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ struct nv50_wimm_func {
};

extern const struct nv50_wimm_func curs507a;
bool curs507a_space(struct nv50_wndw *);

int wndwc37e_new(struct nouveau_drm *, enum drm_plane_type, int, s32,
struct nv50_wndw **);
Expand Down
21 changes: 0 additions & 21 deletions drivers/gpu/drm/nouveau/include/nvif/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,6 @@ int nvif_device_init(struct nvif_object *, u32 handle, s32 oclass, void *, u32,
void nvif_device_fini(struct nvif_device *);
u64 nvif_device_time(struct nvif_device *);

/* Delay based on GPU time (ie. PTIMER).
*
* Will return -ETIMEDOUT unless the loop was terminated with 'break',
* where it will return the number of nanoseconds taken instead.
*/
#define nvif_nsec(d,n,cond...) ({ \
struct nvif_device *_device = (d); \
u64 _nsecs = (n), _time0 = nvif_device_time(_device); \
s64 _taken = 0; \
\
do { \
cond \
} while (_taken = nvif_device_time(_device) - _time0, _taken < _nsecs);\
\
if (_taken >= _nsecs) \
_taken = -ETIMEDOUT; \
_taken; \
})
#define nvif_usec(d,u,cond...) nvif_nsec((d), (u) * 1000, ##cond)
#define nvif_msec(d,m,cond...) nvif_usec((d), (m) * 1000, ##cond)

/*XXX*/
#include <subdev/bios.h>
#include <subdev/fb.h>
Expand Down
35 changes: 35 additions & 0 deletions drivers/gpu/drm/nouveau/include/nvif/timer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef __NVIF_TIMER_H__
#define __NVIF_TIMER_H__
#include <nvif/os.h>

struct nvif_timer_wait {
struct nvif_device *device;
u64 limit;
u64 time0;
u64 time1;
int reads;
};

void nvif_timer_wait_init(struct nvif_device *, u64 nsec,
struct nvif_timer_wait *);
s64 nvif_timer_wait_test(struct nvif_timer_wait *);

/* Delay based on GPU time (ie. PTIMER).
*
* Will return -ETIMEDOUT unless the loop was terminated with 'break',
* where it will return the number of nanoseconds taken instead.
*/
#define nvif_nsec(d,n,cond...) ({ \
struct nvif_timer_wait _wait; \
s64 _taken = 0; \
\
nvif_timer_wait_init((d), (n), &_wait); \
do { \
cond \
} while ((_taken = nvif_timer_wait_test(&_wait)) >= 0); \
\
_taken; \
})
#define nvif_usec(d,u,cond...) nvif_nsec((d), (u) * 1000, ##cond)
#define nvif_msec(d,m,cond...) nvif_usec((d), (m) * 1000, ##cond)
#endif
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/include/nvif/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ struct nvif_user {

struct nvif_user_func {
void (*doorbell)(struct nvif_user *, u32 token);
u64 (*time)(struct nvif_user *);
};

int nvif_user_init(struct nvif_device *);
Expand Down
9 changes: 7 additions & 2 deletions drivers/gpu/drm/nouveau/nouveau_bo.c
Original file line number Diff line number Diff line change
Expand Up @@ -1494,8 +1494,13 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg)
ret = nvif_object_map_handle(&mem->mem.object,
&args, argc,
&handle, &length);
if (ret != 1)
return ret ? ret : -EINVAL;
if (ret != 1) {
if (WARN_ON(ret == 0))
return -EINVAL;
if (ret == -ENOSPC)
return -EAGAIN;
return ret;
}

reg->bus.base = 0;
reg->bus.offset = handle;
Expand Down
20 changes: 8 additions & 12 deletions drivers/gpu/drm/nouveau/nouveau_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,22 +222,18 @@ nouveau_drm_debugfs_init(struct drm_minor *minor)
{
struct nouveau_drm *drm = nouveau_drm(minor->dev);
struct dentry *dentry;
int i, ret;
int i;

for (i = 0; i < ARRAY_SIZE(nouveau_debugfs_files); i++) {
dentry = debugfs_create_file(nouveau_debugfs_files[i].name,
S_IRUGO | S_IWUSR,
minor->debugfs_root, minor->dev,
nouveau_debugfs_files[i].fops);
if (!dentry)
return -ENOMEM;
debugfs_create_file(nouveau_debugfs_files[i].name,
S_IRUGO | S_IWUSR,
minor->debugfs_root, minor->dev,
nouveau_debugfs_files[i].fops);
}

ret = drm_debugfs_create_files(nouveau_debugfs_list,
NOUVEAU_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
if (ret)
return ret;
drm_debugfs_create_files(nouveau_debugfs_list,
NOUVEAU_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);

/* Set the size of the vbios since we know it, and it's confusing to
* userspace if it wants to seek() but the file has a length of 0
Expand Down
63 changes: 63 additions & 0 deletions drivers/gpu/drm/nouveau/nouveau_drm.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,64 @@ nouveau_drm_device_fini(struct drm_device *dev)
kfree(drm);
}

/*
* On some Intel PCIe bridge controllers doing a
* D0 -> D3hot -> D3cold -> D0 sequence causes Nvidia GPUs to not reappear.
* Skipping the intermediate D3hot step seems to make it work again. This is
* probably caused by not meeting the expectation the involved AML code has
* when the GPU is put into D3hot state before invoking it.
*
* This leads to various manifestations of this issue:
* - AML code execution to power on the GPU hits an infinite loop (as the
* code waits on device memory to change).
* - kernel crashes, as all PCI reads return -1, which most code isn't able
* to handle well enough.
*
* In all cases dmesg will contain at least one line like this:
* 'nouveau 0000:01:00.0: Refused to change power state, currently in D3'
* followed by a lot of nouveau timeouts.
*
* In the \_SB.PCI0.PEG0.PG00._OFF code deeper down writes bit 0x80 to the not
* documented PCI config space register 0x248 of the Intel PCIe bridge
* controller (0x1901) in order to change the state of the PCIe link between
* the PCIe port and the GPU. There are alternative code paths using other
* registers, which seem to work fine (executed pre Windows 8):
* - 0xbc bit 0x20 (publicly available documentation claims 'reserved')
* - 0xb0 bit 0x10 (link disable)
* Changing the conditions inside the firmware by poking into the relevant
* addresses does resolve the issue, but it seemed to be ACPI private memory
* and not any device accessible memory at all, so there is no portable way of
* changing the conditions.
* On a XPS 9560 that means bits [0,3] on \CPEX need to be cleared.
*
* The only systems where this behavior can be seen are hybrid graphics laptops
* with a secondary Nvidia Maxwell, Pascal or Turing GPU. It's unclear whether
* this issue only occurs in combination with listed Intel PCIe bridge
* controllers and the mentioned GPUs or other devices as well.
*
* documentation on the PCIe bridge controller can be found in the
* "7th Generation Intel® Processor Families for H Platforms Datasheet Volume 2"
* Section "12 PCI Express* Controller (x16) Registers"
*/

static void quirk_broken_nv_runpm(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct nouveau_drm *drm = nouveau_drm(dev);
struct pci_dev *bridge = pci_upstream_bridge(pdev);

if (!bridge || bridge->vendor != PCI_VENDOR_ID_INTEL)
return;

switch (bridge->device) {
case 0x1901:
drm->old_pm_cap = pdev->pm_cap;
pdev->pm_cap = 0;
NV_INFO(drm, "Disabling PCI power management to avoid bug\n");
break;
}
}

static int nouveau_drm_probe(struct pci_dev *pdev,
const struct pci_device_id *pent)
{
Expand Down Expand Up @@ -699,6 +757,7 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
if (ret)
goto fail_drm_dev_init;

quirk_broken_nv_runpm(pdev);
return 0;

fail_drm_dev_init:
Expand Down Expand Up @@ -734,7 +793,11 @@ static void
nouveau_drm_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct nouveau_drm *drm = nouveau_drm(dev);

/* revert our workaround */
if (drm->old_pm_cap)
pdev->pm_cap = drm->old_pm_cap;
nouveau_drm_device_remove(dev);
pci_disable_device(pdev);
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/nouveau/nouveau_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ struct nouveau_drm {

struct list_head clients;

u8 old_pm_cap;

struct {
struct agp_bridge_data *bridge;
u32 base;
Expand Down
9 changes: 6 additions & 3 deletions drivers/gpu/drm/nouveau/nouveau_svm.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
mm = get_task_mm(current);
down_read(&mm->mmap_sem);

if (!cli->svm.svmm) {
up_read(&mm->mmap_sem);
return -EINVAL;
}

for (addr = args->va_start, end = args->va_start + size; addr < end;) {
struct vm_area_struct *vma;
unsigned long next;
Expand All @@ -179,6 +184,7 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
if (!vma)
break;

addr = max(addr, vma->vm_start);
next = min(vma->vm_end, end);
/* This is a best effort so we ignore errors */
nouveau_dmem_migrate_vma(cli->drm, vma, addr, next);
Expand Down Expand Up @@ -657,9 +663,6 @@ nouveau_svm_fault(struct nvif_notify *notify)
limit = start + (ARRAY_SIZE(args.phys) << PAGE_SHIFT);
if (start < svmm->unmanaged.limit)
limit = min_t(u64, limit, svmm->unmanaged.start);
else
if (limit > svmm->unmanaged.start)
start = max_t(u64, start, svmm->unmanaged.limit);
SVMM_DBG(svmm, "wndw %016llx-%016llx", start, limit);

mm = svmm->notifier.mm;
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/nouveau/nvif/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ nvif-y += nvif/fifo.o
nvif-y += nvif/mem.o
nvif-y += nvif/mmu.o
nvif-y += nvif/notify.o
nvif-y += nvif/timer.o
nvif-y += nvif/vmm.o

# Usermode classes
Expand Down
Loading

0 comments on commit 9c34696

Please sign in to comment.