Merge branch 'linux-5.10' of git://github.com/skeggsb/linux into drm-fixes
Fixes an endian regression on older GPUs, a refcount overflow, a migration fix and 3 display fixes. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Ben Skeggs <skeggsb@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/CACAvsv6MOjtgzKchpis1XrZYmu7-6CaxnHVzJKOXPH62_em7tw@mail.gmail.com
This commit is contained in:
commit
a7ece18c5d
@ -44,6 +44,7 @@ int core507d_new_(const struct nv50_core_func *, struct nouveau_drm *, s32,
|
||||
struct nv50_core **);
|
||||
int core507d_init(struct nv50_core *);
|
||||
void core507d_ntfy_init(struct nouveau_bo *, u32);
|
||||
int core507d_read_caps(struct nv50_disp *disp);
|
||||
int core507d_caps_init(struct nouveau_drm *, struct nv50_disp *);
|
||||
int core507d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *);
|
||||
int core507d_update(struct nv50_core *, u32 *, bool);
|
||||
@ -55,6 +56,7 @@ extern const struct nv50_outp_func pior507d;
|
||||
int core827d_new(struct nouveau_drm *, s32, struct nv50_core **);
|
||||
|
||||
int core907d_new(struct nouveau_drm *, s32, struct nv50_core **);
|
||||
int core907d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp);
|
||||
extern const struct nv50_outp_func dac907d;
|
||||
extern const struct nv50_outp_func sor907d;
|
||||
|
||||
|
@ -78,18 +78,55 @@ core507d_ntfy_init(struct nouveau_bo *bo, u32 offset)
|
||||
}
|
||||
|
||||
int
|
||||
core507d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp)
|
||||
core507d_read_caps(struct nv50_disp *disp)
|
||||
{
|
||||
struct nvif_push *push = disp->core->chan.push;
|
||||
int ret;
|
||||
|
||||
if ((ret = PUSH_WAIT(push, 2)))
|
||||
ret = PUSH_WAIT(push, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PUSH_MTHD(push, NV507D, SET_NOTIFIER_CONTROL,
|
||||
NVDEF(NV507D, SET_NOTIFIER_CONTROL, MODE, WRITE) |
|
||||
NVVAL(NV507D, SET_NOTIFIER_CONTROL, OFFSET, NV50_DISP_CORE_NTFY >> 2) |
|
||||
NVDEF(NV507D, SET_NOTIFIER_CONTROL, NOTIFY, ENABLE));
|
||||
|
||||
PUSH_MTHD(push, NV507D, GET_CAPABILITIES, 0x00000000);
|
||||
|
||||
PUSH_MTHD(push, NV507D, SET_NOTIFIER_CONTROL,
|
||||
NVDEF(NV507D, SET_NOTIFIER_CONTROL, NOTIFY, DISABLE));
|
||||
|
||||
return PUSH_KICK(push);
|
||||
}
|
||||
|
||||
int
|
||||
core507d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp)
|
||||
{
|
||||
struct nv50_core *core = disp->core;
|
||||
struct nouveau_bo *bo = disp->sync;
|
||||
s64 time;
|
||||
int ret;
|
||||
|
||||
NVBO_WR32(bo, NV50_DISP_CORE_NTFY, NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1,
|
||||
NVDEF(NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, DONE, FALSE));
|
||||
|
||||
ret = core507d_read_caps(disp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
time = nvif_msec(core->chan.base.device, 2000ULL,
|
||||
if (NVBO_TD32(bo, NV50_DISP_CORE_NTFY,
|
||||
NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, DONE, ==, TRUE))
|
||||
break;
|
||||
usleep_range(1, 2);
|
||||
);
|
||||
if (time < 0)
|
||||
NV_ERROR(drm, "core caps notifier timeout\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
core507d_init(struct nv50_core *core)
|
||||
{
|
||||
|
@ -22,11 +22,45 @@
|
||||
#include "core.h"
|
||||
#include "head.h"
|
||||
|
||||
#include <nvif/push507c.h>
|
||||
#include <nvif/timer.h>
|
||||
|
||||
#include <nvhw/class/cl907d.h>
|
||||
|
||||
#include "nouveau_bo.h"
|
||||
|
||||
int
|
||||
core907d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp)
|
||||
{
|
||||
struct nv50_core *core = disp->core;
|
||||
struct nouveau_bo *bo = disp->sync;
|
||||
s64 time;
|
||||
int ret;
|
||||
|
||||
NVBO_WR32(bo, NV50_DISP_CORE_NTFY, NV907D_CORE_NOTIFIER_3, CAPABILITIES_4,
|
||||
NVDEF(NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, DONE, FALSE));
|
||||
|
||||
ret = core507d_read_caps(disp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
time = nvif_msec(core->chan.base.device, 2000ULL,
|
||||
if (NVBO_TD32(bo, NV50_DISP_CORE_NTFY,
|
||||
NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, DONE, ==, TRUE))
|
||||
break;
|
||||
usleep_range(1, 2);
|
||||
);
|
||||
if (time < 0)
|
||||
NV_ERROR(drm, "core caps notifier timeout\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nv50_core_func
|
||||
core907d = {
|
||||
.init = core507d_init,
|
||||
.ntfy_init = core507d_ntfy_init,
|
||||
.caps_init = core507d_caps_init,
|
||||
.caps_init = core907d_caps_init,
|
||||
.ntfy_wait_done = core507d_ntfy_wait_done,
|
||||
.update = core507d_update,
|
||||
.head = &head907d,
|
||||
|
@ -26,7 +26,7 @@ static const struct nv50_core_func
|
||||
core917d = {
|
||||
.init = core507d_init,
|
||||
.ntfy_init = core507d_ntfy_init,
|
||||
.caps_init = core507d_caps_init,
|
||||
.caps_init = core907d_caps_init,
|
||||
.ntfy_wait_done = core507d_ntfy_wait_done,
|
||||
.update = core507d_update,
|
||||
.head = &head917d,
|
||||
|
@ -32,7 +32,10 @@
|
||||
#define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_DONE_TRUE 0x00000001
|
||||
#define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_R0 15:1
|
||||
#define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_TIMESTAMP 29:16
|
||||
|
||||
#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1 0x00000001
|
||||
#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE 0:0
|
||||
#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE_FALSE 0x00000000
|
||||
#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE_TRUE 0x00000001
|
||||
|
||||
// class methods
|
||||
#define NV507D_UPDATE (0x00000080)
|
||||
|
@ -24,6 +24,10 @@
|
||||
#ifndef _cl907d_h_
|
||||
#define _cl907d_h_
|
||||
|
||||
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4 0x00000004
|
||||
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE 0:0
|
||||
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE_FALSE 0x00000000
|
||||
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE_TRUE 0x00000001
|
||||
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20 0x00000014
|
||||
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20_SINGLE_LVDS18 0:0
|
||||
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20_SINGLE_LVDS18_FALSE 0x00000000
|
||||
|
@ -1023,29 +1023,6 @@ get_tmds_link_bandwidth(struct drm_connector *connector)
|
||||
return 112000 * duallink_scale;
|
||||
}
|
||||
|
||||
enum drm_mode_status
|
||||
nouveau_conn_mode_clock_valid(const struct drm_display_mode *mode,
|
||||
const unsigned min_clock,
|
||||
const unsigned max_clock,
|
||||
unsigned int *clock_out)
|
||||
{
|
||||
unsigned int clock = mode->clock;
|
||||
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
|
||||
DRM_MODE_FLAG_3D_FRAME_PACKING)
|
||||
clock *= 2;
|
||||
|
||||
if (clock < min_clock)
|
||||
return MODE_CLOCK_LOW;
|
||||
if (clock > max_clock)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
if (clock_out)
|
||||
*clock_out = clock;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
nouveau_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
@ -1053,7 +1030,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
||||
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
||||
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
||||
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
||||
unsigned min_clock = 25000, max_clock = min_clock;
|
||||
unsigned int min_clock = 25000, max_clock = min_clock, clock = mode->clock;
|
||||
|
||||
switch (nv_encoder->dcb->type) {
|
||||
case DCB_OUTPUT_LVDS:
|
||||
@ -1082,8 +1059,15 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
||||
return MODE_BAD;
|
||||
}
|
||||
|
||||
return nouveau_conn_mode_clock_valid(mode, min_clock, max_clock,
|
||||
NULL);
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
|
||||
clock *= 2;
|
||||
|
||||
if (clock < min_clock)
|
||||
return MODE_CLOCK_LOW;
|
||||
if (clock > max_clock)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static struct drm_encoder *
|
||||
|
@ -231,23 +231,30 @@ nv50_dp_mode_valid(struct drm_connector *connector,
|
||||
const struct drm_display_mode *mode,
|
||||
unsigned *out_clock)
|
||||
{
|
||||
const unsigned min_clock = 25000;
|
||||
unsigned max_clock, ds_clock, clock;
|
||||
enum drm_mode_status ret;
|
||||
const unsigned int min_clock = 25000;
|
||||
unsigned int max_rate, mode_rate, ds_max_dotclock, clock = mode->clock;
|
||||
const u8 bpp = connector->display_info.bpc * 3;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace)
|
||||
return MODE_NO_INTERLACE;
|
||||
|
||||
max_clock = outp->dp.link_nr * outp->dp.link_bw;
|
||||
ds_clock = drm_dp_downstream_max_dotclock(outp->dp.dpcd,
|
||||
outp->dp.downstream_ports);
|
||||
if (ds_clock)
|
||||
max_clock = min(max_clock, ds_clock);
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
|
||||
clock *= 2;
|
||||
|
||||
max_rate = outp->dp.link_nr * outp->dp.link_bw;
|
||||
mode_rate = DIV_ROUND_UP(clock * bpp, 8);
|
||||
if (mode_rate > max_rate)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
ds_max_dotclock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, outp->dp.downstream_ports);
|
||||
if (ds_max_dotclock && clock > ds_max_dotclock)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
if (clock < min_clock)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
clock = mode->clock * (connector->display_info.bpc * 3) / 10;
|
||||
ret = nouveau_conn_mode_clock_valid(mode, min_clock, max_clock,
|
||||
&clock);
|
||||
if (out_clock)
|
||||
*out_clock = clock;
|
||||
return ret;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
@ -190,7 +190,8 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain,
|
||||
* to the caller, instead of a normal nouveau_bo ttm reference. */
|
||||
ret = drm_gem_object_init(drm->dev, &nvbo->bo.base, size);
|
||||
if (ret) {
|
||||
nouveau_bo_ref(NULL, &nvbo);
|
||||
drm_gem_object_release(&nvbo->bo.base);
|
||||
kfree(nvbo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -105,11 +105,11 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
|
||||
struct nouveau_cli *cli = nouveau_cli(file_priv);
|
||||
struct drm_nouveau_svm_bind *args = data;
|
||||
unsigned target, cmd, priority;
|
||||
unsigned long addr, end, size;
|
||||
unsigned long addr, end;
|
||||
struct mm_struct *mm;
|
||||
|
||||
args->va_start &= PAGE_MASK;
|
||||
args->va_end &= PAGE_MASK;
|
||||
args->va_end = ALIGN(args->va_end, PAGE_SIZE);
|
||||
|
||||
/* Sanity check arguments */
|
||||
if (args->reserved0 || args->reserved1)
|
||||
@ -118,8 +118,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
|
||||
return -EINVAL;
|
||||
if (args->va_start >= args->va_end)
|
||||
return -EINVAL;
|
||||
if (!args->npages)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = args->header >> NOUVEAU_SVM_BIND_COMMAND_SHIFT;
|
||||
cmd &= NOUVEAU_SVM_BIND_COMMAND_MASK;
|
||||
@ -151,12 +149,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
|
||||
if (args->stride)
|
||||
return -EINVAL;
|
||||
|
||||
size = ((unsigned long)args->npages) << PAGE_SHIFT;
|
||||
if ((args->va_start + size) <= args->va_start)
|
||||
return -EINVAL;
|
||||
if ((args->va_start + size) > args->va_end)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Ok we are ask to do something sane, for now we only support migrate
|
||||
* commands but we will add things like memory policy (what to do on
|
||||
@ -171,7 +163,7 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (addr = args->va_start, end = args->va_start + size; addr < end;) {
|
||||
for (addr = args->va_start, end = args->va_end; addr < end;) {
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long next;
|
||||
|
||||
|
@ -2924,17 +2924,34 @@ nvkm_device_del(struct nvkm_device **pdevice)
|
||||
}
|
||||
}
|
||||
|
||||
/* returns true if the GPU is in the CPU native byte order */
|
||||
static inline bool
|
||||
nvkm_device_endianness(struct nvkm_device *device)
|
||||
{
|
||||
u32 boot1 = nvkm_rd32(device, 0x000004) & 0x01000001;
|
||||
#ifdef __BIG_ENDIAN
|
||||
if (!boot1)
|
||||
return false;
|
||||
const bool big_endian = true;
|
||||
#else
|
||||
if (boot1)
|
||||
return false;
|
||||
const bool big_endian = false;
|
||||
#endif
|
||||
|
||||
/* Read NV_PMC_BOOT_1, and assume non-functional endian switch if it
|
||||
* doesn't contain the expected values.
|
||||
*/
|
||||
u32 pmc_boot_1 = nvkm_rd32(device, 0x000004);
|
||||
if (pmc_boot_1 && pmc_boot_1 != 0x01000001)
|
||||
return !big_endian; /* Assume GPU is LE in this case. */
|
||||
|
||||
/* 0 means LE and 0x01000001 means BE GPU. Condition is true when
|
||||
* GPU/CPU endianness don't match.
|
||||
*/
|
||||
if (big_endian == !pmc_boot_1) {
|
||||
nvkm_wr32(device, 0x000004, 0x01000001);
|
||||
nvkm_rd32(device, 0x000000);
|
||||
if (nvkm_rd32(device, 0x000004) != (big_endian ? 0x01000001 : 0x00000000))
|
||||
return !big_endian; /* Assume GPU is LE on any unexpected read-back. */
|
||||
}
|
||||
|
||||
/* CPU/GPU endianness should (hopefully) match. */
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2987,14 +3004,10 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
|
||||
if (detect) {
|
||||
/* switch mmio to cpu's native endianness */
|
||||
if (!nvkm_device_endianness(device)) {
|
||||
nvkm_wr32(device, 0x000004, 0x01000001);
|
||||
nvkm_rd32(device, 0x000000);
|
||||
if (!nvkm_device_endianness(device)) {
|
||||
nvdev_error(device,
|
||||
"GPU not supported on big-endian\n");
|
||||
ret = -ENOSYS;
|
||||
goto done;
|
||||
}
|
||||
nvdev_error(device,
|
||||
"Couldn't switch GPU to CPUs endianess\n");
|
||||
ret = -ENOSYS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
boot0 = nvkm_rd32(device, 0x000000);
|
||||
|
Loading…
x
Reference in New Issue
Block a user