Commit a7ece18c authored by Dave Airlie's avatar Dave Airlie

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: default avatarDave Airlie <airlied@redhat.com>
From: Ben Skeggs <skeggsb@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CACAvsv6MOjtgzKchpis1XrZYmu7-6CaxnHVzJKOXPH62_em7tw@mail.gmail.com
parents 671d2799 d7787cc0
...@@ -44,6 +44,7 @@ int core507d_new_(const struct nv50_core_func *, struct nouveau_drm *, s32, ...@@ -44,6 +44,7 @@ int core507d_new_(const struct nv50_core_func *, struct nouveau_drm *, s32,
struct nv50_core **); struct nv50_core **);
int core507d_init(struct nv50_core *); int core507d_init(struct nv50_core *);
void core507d_ntfy_init(struct nouveau_bo *, u32); 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_caps_init(struct nouveau_drm *, struct nv50_disp *);
int core507d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *); int core507d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *);
int core507d_update(struct nv50_core *, u32 *, bool); int core507d_update(struct nv50_core *, u32 *, bool);
...@@ -55,6 +56,7 @@ extern const struct nv50_outp_func pior507d; ...@@ -55,6 +56,7 @@ extern const struct nv50_outp_func pior507d;
int core827d_new(struct nouveau_drm *, s32, struct nv50_core **); int core827d_new(struct nouveau_drm *, s32, struct nv50_core **);
int core907d_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 dac907d;
extern const struct nv50_outp_func sor907d; extern const struct nv50_outp_func sor907d;
......
...@@ -78,18 +78,55 @@ core507d_ntfy_init(struct nouveau_bo *bo, u32 offset) ...@@ -78,18 +78,55 @@ core507d_ntfy_init(struct nouveau_bo *bo, u32 offset)
} }
int 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; struct nvif_push *push = disp->core->chan.push;
int ret; int ret;
if ((ret = PUSH_WAIT(push, 2))) ret = PUSH_WAIT(push, 6);
if (ret)
return 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, GET_CAPABILITIES, 0x00000000);
PUSH_MTHD(push, NV507D, SET_NOTIFIER_CONTROL,
NVDEF(NV507D, SET_NOTIFIER_CONTROL, NOTIFY, DISABLE));
return PUSH_KICK(push); 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 int
core507d_init(struct nv50_core *core) core507d_init(struct nv50_core *core)
{ {
......
...@@ -22,11 +22,45 @@ ...@@ -22,11 +22,45 @@
#include "core.h" #include "core.h"
#include "head.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 static const struct nv50_core_func
core907d = { core907d = {
.init = core507d_init, .init = core507d_init,
.ntfy_init = core507d_ntfy_init, .ntfy_init = core507d_ntfy_init,
.caps_init = core507d_caps_init, .caps_init = core907d_caps_init,
.ntfy_wait_done = core507d_ntfy_wait_done, .ntfy_wait_done = core507d_ntfy_wait_done,
.update = core507d_update, .update = core507d_update,
.head = &head907d, .head = &head907d,
......
...@@ -26,7 +26,7 @@ static const struct nv50_core_func ...@@ -26,7 +26,7 @@ static const struct nv50_core_func
core917d = { core917d = {
.init = core507d_init, .init = core507d_init,
.ntfy_init = core507d_ntfy_init, .ntfy_init = core507d_ntfy_init,
.caps_init = core507d_caps_init, .caps_init = core907d_caps_init,
.ntfy_wait_done = core507d_ntfy_wait_done, .ntfy_wait_done = core507d_ntfy_wait_done,
.update = core507d_update, .update = core507d_update,
.head = &head917d, .head = &head917d,
......
...@@ -32,7 +32,10 @@ ...@@ -32,7 +32,10 @@
#define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_DONE_TRUE 0x00000001 #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_R0 15:1
#define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_TIMESTAMP 29:16 #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 // class methods
#define NV507D_UPDATE (0x00000080) #define NV507D_UPDATE (0x00000080)
......
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
#ifndef _cl907d_h_ #ifndef _cl907d_h_
#define _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 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 0:0
#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20_SINGLE_LVDS18_FALSE 0x00000000 #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) ...@@ -1023,29 +1023,6 @@ get_tmds_link_bandwidth(struct drm_connector *connector)
return 112000 * duallink_scale; 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 static enum drm_mode_status
nouveau_connector_mode_valid(struct drm_connector *connector, nouveau_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode) struct drm_display_mode *mode)
...@@ -1053,7 +1030,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, ...@@ -1053,7 +1030,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
struct drm_encoder *encoder = to_drm_encoder(nv_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) { switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_LVDS: case DCB_OUTPUT_LVDS:
...@@ -1082,8 +1059,15 @@ nouveau_connector_mode_valid(struct drm_connector *connector, ...@@ -1082,8 +1059,15 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
return MODE_BAD; return MODE_BAD;
} }
return nouveau_conn_mode_clock_valid(mode, min_clock, max_clock, if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
NULL); 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 * static struct drm_encoder *
......
...@@ -231,23 +231,30 @@ nv50_dp_mode_valid(struct drm_connector *connector, ...@@ -231,23 +231,30 @@ nv50_dp_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
unsigned *out_clock) unsigned *out_clock)
{ {
const unsigned min_clock = 25000; const unsigned int min_clock = 25000;
unsigned max_clock, ds_clock, clock; unsigned int max_rate, mode_rate, ds_max_dotclock, clock = mode->clock;
enum drm_mode_status ret; const u8 bpp = connector->display_info.bpc * 3;
if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace) if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace)
return MODE_NO_INTERLACE; return MODE_NO_INTERLACE;
max_clock = outp->dp.link_nr * outp->dp.link_bw; if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
ds_clock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, clock *= 2;
outp->dp.downstream_ports);
if (ds_clock) max_rate = outp->dp.link_nr * outp->dp.link_bw;
max_clock = min(max_clock, ds_clock); 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) if (out_clock)
*out_clock = 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, ...@@ -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. */ * to the caller, instead of a normal nouveau_bo ttm reference. */
ret = drm_gem_object_init(drm->dev, &nvbo->bo.base, size); ret = drm_gem_object_init(drm->dev, &nvbo->bo.base, size);
if (ret) { if (ret) {
nouveau_bo_ref(NULL, &nvbo); drm_gem_object_release(&nvbo->bo.base);
kfree(nvbo);
return ret; return ret;
} }
......
...@@ -105,11 +105,11 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, ...@@ -105,11 +105,11 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
struct nouveau_cli *cli = nouveau_cli(file_priv); struct nouveau_cli *cli = nouveau_cli(file_priv);
struct drm_nouveau_svm_bind *args = data; struct drm_nouveau_svm_bind *args = data;
unsigned target, cmd, priority; unsigned target, cmd, priority;
unsigned long addr, end, size; unsigned long addr, end;
struct mm_struct *mm; struct mm_struct *mm;
args->va_start &= PAGE_MASK; args->va_start &= PAGE_MASK;
args->va_end &= PAGE_MASK; args->va_end = ALIGN(args->va_end, PAGE_SIZE);
/* Sanity check arguments */ /* Sanity check arguments */
if (args->reserved0 || args->reserved1) if (args->reserved0 || args->reserved1)
...@@ -118,8 +118,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, ...@@ -118,8 +118,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
return -EINVAL; return -EINVAL;
if (args->va_start >= args->va_end) if (args->va_start >= args->va_end)
return -EINVAL; return -EINVAL;
if (!args->npages)
return -EINVAL;
cmd = args->header >> NOUVEAU_SVM_BIND_COMMAND_SHIFT; cmd = args->header >> NOUVEAU_SVM_BIND_COMMAND_SHIFT;
cmd &= NOUVEAU_SVM_BIND_COMMAND_MASK; cmd &= NOUVEAU_SVM_BIND_COMMAND_MASK;
...@@ -151,12 +149,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, ...@@ -151,12 +149,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
if (args->stride) if (args->stride)
return -EINVAL; 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 * 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 * 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, ...@@ -171,7 +163,7 @@ nouveau_svmm_bind(struct drm_device *dev, void *data,
return -EINVAL; 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; struct vm_area_struct *vma;
unsigned long next; unsigned long next;
......
...@@ -2924,17 +2924,34 @@ nvkm_device_del(struct nvkm_device **pdevice) ...@@ -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 static inline bool
nvkm_device_endianness(struct nvkm_device *device) nvkm_device_endianness(struct nvkm_device *device)
{ {
u32 boot1 = nvkm_rd32(device, 0x000004) & 0x01000001;
#ifdef __BIG_ENDIAN #ifdef __BIG_ENDIAN
if (!boot1) const bool big_endian = true;
return false;
#else #else
if (boot1) const bool big_endian = false;
return false;
#endif #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; return true;
} }
...@@ -2986,16 +3003,12 @@ nvkm_device_ctor(const struct nvkm_device_func *func, ...@@ -2986,16 +3003,12 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
/* identify the chipset, and determine classes of subdev/engines */ /* identify the chipset, and determine classes of subdev/engines */
if (detect) { if (detect) {
/* switch mmio to cpu's native endianness */ /* 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)) { if (!nvkm_device_endianness(device)) {
nvdev_error(device, nvdev_error(device,
"GPU not supported on big-endian\n"); "Couldn't switch GPU to CPUs endianess\n");
ret = -ENOSYS; ret = -ENOSYS;
goto done; goto done;
} }
}
boot0 = nvkm_rd32(device, 0x000000); boot0 = nvkm_rd32(device, 0x000000);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment