mirror of https://gitee.com/openkylin/linux.git
Merge branch 'linux-5.6' of git://github.com/skeggsb/linux into drm-next
A couple of OOPS fixes, fixes for TU1xx if firmware isn't available, better behaviour in the face of GPU faults, and a patch to make HD audio work again after runpm changes. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Ben Skeggs <skeggsb@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/ <CACAvsv4xcLF6Ahh7UYEesn-wBEksd2da+ghusBAdODMrH7Sz2A@mail.gmail.com
This commit is contained in:
commit
d47c7f0626
|
@ -17,6 +17,7 @@ config DRM_NOUVEAU
|
|||
select INPUT if ACPI && X86
|
||||
select THERMAL if ACPI && X86
|
||||
select ACPI_VIDEO if ACPI && X86
|
||||
select SND_HDA_COMPONENT if SND_HDA_CORE
|
||||
help
|
||||
Choose this option for open-source NVIDIA support.
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/component.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
@ -476,12 +477,113 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* audio component binding for ELD notification
|
||||
*/
|
||||
static void
|
||||
nv50_audio_component_eld_notify(struct drm_audio_component *acomp, int port)
|
||||
{
|
||||
if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify)
|
||||
acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
|
||||
port, -1);
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_audio_component_get_eld(struct device *kdev, int port, int pipe,
|
||||
bool *enabled, unsigned char *buf, int max_bytes)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(kdev);
|
||||
struct nouveau_drm *drm = nouveau_drm(drm_dev);
|
||||
struct drm_encoder *encoder;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct nouveau_connector *nv_connector;
|
||||
struct nouveau_crtc *nv_crtc;
|
||||
int ret = 0;
|
||||
|
||||
*enabled = false;
|
||||
drm_for_each_encoder(encoder, drm->dev) {
|
||||
nv_encoder = nouveau_encoder(encoder);
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
nv_crtc = nouveau_crtc(encoder->crtc);
|
||||
if (!nv_connector || !nv_crtc || nv_crtc->index != port)
|
||||
continue;
|
||||
*enabled = drm_detect_monitor_audio(nv_connector->edid);
|
||||
if (*enabled) {
|
||||
ret = drm_eld_size(nv_connector->base.eld);
|
||||
memcpy(buf, nv_connector->base.eld,
|
||||
min(max_bytes, ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_audio_component_ops nv50_audio_component_ops = {
|
||||
.get_eld = nv50_audio_component_get_eld,
|
||||
};
|
||||
|
||||
static int
|
||||
nv50_audio_component_bind(struct device *kdev, struct device *hda_kdev,
|
||||
void *data)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(kdev);
|
||||
struct nouveau_drm *drm = nouveau_drm(drm_dev);
|
||||
struct drm_audio_component *acomp = data;
|
||||
|
||||
if (WARN_ON(!device_link_add(hda_kdev, kdev, DL_FLAG_STATELESS)))
|
||||
return -ENOMEM;
|
||||
|
||||
drm_modeset_lock_all(drm_dev);
|
||||
acomp->ops = &nv50_audio_component_ops;
|
||||
acomp->dev = kdev;
|
||||
drm->audio.component = acomp;
|
||||
drm_modeset_unlock_all(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_audio_component_unbind(struct device *kdev, struct device *hda_kdev,
|
||||
void *data)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(kdev);
|
||||
struct nouveau_drm *drm = nouveau_drm(drm_dev);
|
||||
struct drm_audio_component *acomp = data;
|
||||
|
||||
drm_modeset_lock_all(drm_dev);
|
||||
drm->audio.component = NULL;
|
||||
acomp->ops = NULL;
|
||||
acomp->dev = NULL;
|
||||
drm_modeset_unlock_all(drm_dev);
|
||||
}
|
||||
|
||||
static const struct component_ops nv50_audio_component_bind_ops = {
|
||||
.bind = nv50_audio_component_bind,
|
||||
.unbind = nv50_audio_component_unbind,
|
||||
};
|
||||
|
||||
static void
|
||||
nv50_audio_component_init(struct nouveau_drm *drm)
|
||||
{
|
||||
if (!component_add(drm->dev->dev, &nv50_audio_component_bind_ops))
|
||||
drm->audio.component_registered = true;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_audio_component_fini(struct nouveau_drm *drm)
|
||||
{
|
||||
if (drm->audio.component_registered) {
|
||||
component_del(drm->dev->dev, &nv50_audio_component_bind_ops);
|
||||
drm->audio.component_registered = false;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Audio
|
||||
*****************************************************************************/
|
||||
static void
|
||||
nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nv50_disp *disp = nv50_disp(encoder->dev);
|
||||
struct {
|
||||
|
@ -496,11 +598,14 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
|
|||
};
|
||||
|
||||
nvif_mthd(&disp->disp->object, 0, &args, sizeof(args));
|
||||
|
||||
nv50_audio_component_eld_notify(drm->audio.component, nv_crtc->index);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
||||
struct nouveau_connector *nv_connector;
|
||||
|
@ -527,6 +632,8 @@ nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||
|
||||
nvif_mthd(&disp->disp->object, 0, &args,
|
||||
sizeof(args.base) + drm_eld_size(args.data));
|
||||
|
||||
nv50_audio_component_eld_notify(drm->audio.component, nv_crtc->index);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -2296,6 +2403,8 @@ nv50_display_destroy(struct drm_device *dev)
|
|||
{
|
||||
struct nv50_disp *disp = nv50_disp(dev);
|
||||
|
||||
nv50_audio_component_fini(nouveau_drm(dev));
|
||||
|
||||
nv50_core_del(&disp->core);
|
||||
|
||||
nouveau_bo_unmap(disp->sync);
|
||||
|
@ -2444,6 +2553,8 @@ nv50_display_create(struct drm_device *dev)
|
|||
/* Disable vblank irqs aggressively for power-saving, safe on nv50+ */
|
||||
dev->vblank_disable_immediate = true;
|
||||
|
||||
nv50_audio_component_init(drm);
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
nv50_display_destroy(dev);
|
||||
|
|
|
@ -55,6 +55,8 @@ nouveau_channel_killed(struct nvif_notify *ntfy)
|
|||
struct nouveau_cli *cli = (void *)chan->user.client;
|
||||
NV_PRINTK(warn, cli, "channel %d killed!\n", chan->chid);
|
||||
atomic_set(&chan->killed, 1);
|
||||
if (chan->fence)
|
||||
nouveau_fence_context_kill(chan->fence, -ENODEV);
|
||||
return NVIF_NOTIFY_DROP;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
#include <drm/ttm/ttm_module.h>
|
||||
#include <drm/ttm/ttm_page_alloc.h>
|
||||
|
||||
#include <drm/drm_audio_component.h>
|
||||
|
||||
#include "uapi/drm/nouveau_drm.h"
|
||||
|
||||
struct nouveau_channel;
|
||||
|
@ -211,6 +213,11 @@ struct nouveau_drm {
|
|||
struct nouveau_svm *svm;
|
||||
|
||||
struct nouveau_dmem *dmem;
|
||||
|
||||
struct {
|
||||
struct drm_audio_component *component;
|
||||
bool component_registered;
|
||||
} audio;
|
||||
};
|
||||
|
||||
static inline struct nouveau_drm *
|
||||
|
|
|
@ -87,7 +87,7 @@ nouveau_local_fence(struct dma_fence *fence, struct nouveau_drm *drm)
|
|||
}
|
||||
|
||||
void
|
||||
nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
|
||||
nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error)
|
||||
{
|
||||
struct nouveau_fence *fence;
|
||||
|
||||
|
@ -95,11 +95,19 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
|
|||
while (!list_empty(&fctx->pending)) {
|
||||
fence = list_entry(fctx->pending.next, typeof(*fence), head);
|
||||
|
||||
if (error)
|
||||
dma_fence_set_error(&fence->base, error);
|
||||
|
||||
if (nouveau_fence_signal(fence))
|
||||
nvif_notify_put(&fctx->notify);
|
||||
}
|
||||
spin_unlock_irq(&fctx->lock);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
|
||||
{
|
||||
nouveau_fence_context_kill(fctx, 0);
|
||||
nvif_notify_fini(&fctx->notify);
|
||||
fctx->dead = 1;
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ struct nouveau_fence_priv {
|
|||
void nouveau_fence_context_new(struct nouveau_channel *, struct nouveau_fence_chan *);
|
||||
void nouveau_fence_context_del(struct nouveau_fence_chan *);
|
||||
void nouveau_fence_context_free(struct nouveau_fence_chan *);
|
||||
void nouveau_fence_context_kill(struct nouveau_fence_chan *, int error);
|
||||
|
||||
int nv04_fence_create(struct nouveau_drm *);
|
||||
int nv04_fence_mthd(struct nouveau_channel *, u32, u32, u32);
|
||||
|
|
|
@ -688,7 +688,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
|
|||
struct validate_op op;
|
||||
struct nouveau_fence *fence = NULL;
|
||||
int i, j, ret = 0;
|
||||
bool do_reloc = false;
|
||||
bool do_reloc = false, sync = false;
|
||||
|
||||
if (unlikely(!abi16))
|
||||
return -ENOMEM;
|
||||
|
@ -702,6 +702,10 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
|
|||
|
||||
if (!chan)
|
||||
return nouveau_abi16_put(abi16, -ENOENT);
|
||||
if (unlikely(atomic_read(&chan->killed)))
|
||||
return nouveau_abi16_put(abi16, -ENODEV);
|
||||
|
||||
sync = req->vram_available & NOUVEAU_GEM_PUSHBUF_SYNC;
|
||||
|
||||
req->vram_available = drm->gem.vram_available;
|
||||
req->gart_available = drm->gem.gart_available;
|
||||
|
@ -850,6 +854,13 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
if (!(ret = nouveau_fence_wait(fence, false, false))) {
|
||||
if ((ret = dma_fence_get_status(&fence->base)) == 1)
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
validate_fini(&op, chan, fence, bo);
|
||||
nouveau_fence_unref(&fence);
|
||||
|
|
|
@ -69,8 +69,8 @@ nouveau_vma_del(struct nouveau_vma **pvma)
|
|||
}
|
||||
list_del(&vma->head);
|
||||
kfree(*pvma);
|
||||
*pvma = NULL;
|
||||
}
|
||||
*pvma = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -74,6 +74,8 @@ nv50_disp_chan_mthd(struct nv50_disp_chan *chan, int debug)
|
|||
|
||||
if (debug > subdev->debug)
|
||||
return;
|
||||
if (!mthd)
|
||||
return;
|
||||
|
||||
for (i = 0; (list = mthd->data[i].mthd) != NULL; i++) {
|
||||
u32 base = chan->head * mthd->addr;
|
||||
|
|
|
@ -101,15 +101,26 @@ gv100_disp_exception(struct nv50_disp *disp, int chid)
|
|||
u32 stat = nvkm_rd32(device, 0x611020 + (chid * 12));
|
||||
u32 type = (stat & 0x00007000) >> 12;
|
||||
u32 mthd = (stat & 0x00000fff) << 2;
|
||||
u32 data = nvkm_rd32(device, 0x611024 + (chid * 12));
|
||||
u32 code = nvkm_rd32(device, 0x611028 + (chid * 12));
|
||||
const struct nvkm_enum *reason =
|
||||
nvkm_enum_find(nv50_disp_intr_error_type, type);
|
||||
|
||||
nvkm_error(subdev, "chid %d stat %08x reason %d [%s] mthd %04x "
|
||||
"data %08x code %08x\n",
|
||||
chid, stat, type, reason ? reason->name : "",
|
||||
mthd, data, code);
|
||||
/*TODO: Suspect 33->41 are for WRBK channel exceptions, but we
|
||||
* don't support those currently.
|
||||
*
|
||||
* CORE+WIN CHIDs map directly to the FE_EXCEPT() slots.
|
||||
*/
|
||||
if (chid <= 32) {
|
||||
u32 data = nvkm_rd32(device, 0x611024 + (chid * 12));
|
||||
u32 code = nvkm_rd32(device, 0x611028 + (chid * 12));
|
||||
nvkm_error(subdev, "chid %d stat %08x reason %d [%s] "
|
||||
"mthd %04x data %08x code %08x\n",
|
||||
chid, stat, type, reason ? reason->name : "",
|
||||
mthd, data, code);
|
||||
} else {
|
||||
nvkm_error(subdev, "chid %d stat %08x reason %d [%s] "
|
||||
"mthd %04x\n",
|
||||
chid, stat, type, reason ? reason->name : "", mthd);
|
||||
}
|
||||
|
||||
if (chid < ARRAY_SIZE(disp->chan) && disp->chan[chid]) {
|
||||
switch (mthd) {
|
||||
|
|
|
@ -58,8 +58,12 @@ struct nvkm_acr_lsfw *
|
|||
nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *func, struct nvkm_acr *acr,
|
||||
struct nvkm_falcon *falcon, enum nvkm_acr_lsf_id id)
|
||||
{
|
||||
struct nvkm_acr_lsfw *lsfw = nvkm_acr_lsfw_get(acr, id);
|
||||
struct nvkm_acr_lsfw *lsfw;
|
||||
|
||||
if (!acr)
|
||||
return ERR_PTR(-ENOSYS);
|
||||
|
||||
lsfw = nvkm_acr_lsfw_get(acr, id);
|
||||
if (lsfw && lsfw->func) {
|
||||
nvkm_error(&acr->subdev, "LSFW %d redefined\n", id);
|
||||
return ERR_PTR(-EEXIST);
|
||||
|
|
|
@ -125,6 +125,34 @@ nvkm_fb_oneinit(struct nvkm_subdev *subdev)
|
|||
return nvkm_mm_init(&fb->tags, 0, 0, tags, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_fb_init_scrub_vpr(struct nvkm_fb *fb)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &fb->subdev;
|
||||
int ret;
|
||||
|
||||
nvkm_debug(subdev, "VPR locked, running scrubber binary\n");
|
||||
|
||||
if (!fb->vpr_scrubber.size) {
|
||||
nvkm_warn(subdev, "VPR locked, but no scrubber binary!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fb->func->vpr.scrub(fb);
|
||||
if (ret) {
|
||||
nvkm_error(subdev, "VPR scrubber binary failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fb->func->vpr.scrub_required(fb)) {
|
||||
nvkm_error(subdev, "VPR still locked after scrub!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
nvkm_debug(subdev, "VPR scrubber binary successful\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_fb_init(struct nvkm_subdev *subdev)
|
||||
{
|
||||
|
@ -157,18 +185,9 @@ nvkm_fb_init(struct nvkm_subdev *subdev)
|
|||
|
||||
if (fb->func->vpr.scrub_required &&
|
||||
fb->func->vpr.scrub_required(fb)) {
|
||||
nvkm_debug(subdev, "VPR locked, running scrubber binary\n");
|
||||
|
||||
ret = fb->func->vpr.scrub(fb);
|
||||
ret = nvkm_fb_init_scrub_vpr(fb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (fb->func->vpr.scrub_required(fb)) {
|
||||
nvkm_error(subdev, "VPR still locked after scrub!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
nvkm_debug(subdev, "VPR scrubber binary successful\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -120,8 +120,9 @@ gp102_fb_new_(const struct nvkm_fb_func *func, struct nvkm_device *device,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nvkm_firmware_load_blob(&(*pfb)->subdev, "nvdec/scrubber", "", 0,
|
||||
&(*pfb)->vpr_scrubber);
|
||||
nvkm_firmware_load_blob(&(*pfb)->subdev, "nvdec/scrubber", "", 0,
|
||||
&(*pfb)->vpr_scrubber);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -110,6 +110,7 @@ struct drm_nouveau_gem_pushbuf {
|
|||
__u64 push;
|
||||
__u32 suffix0;
|
||||
__u32 suffix1;
|
||||
#define NOUVEAU_GEM_PUSHBUF_SYNC (1ULL << 0)
|
||||
__u64 vram_available;
|
||||
__u64 gart_available;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue