mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-25 11:29:47 +07:00
12885ecbfe
This introduces support for CRC readback on gf119+, using the documentation generously provided to us by Nvidia: https://github.com/NVIDIA/open-gpu-doc/blob/master/Display-CRC/display-crc.txt We expose all available CRC sources. SF, SOR, PIOR, and DAC are exposed through a single set of "outp" sources: outp-active/auto for a CRC of the scanout region, outp-complete for a CRC of both the scanout and blanking/sync region combined, and outp-inactive for a CRC of only the blanking/sync region. For each source, nouveau selects the appropriate tap point based on the output path in use. We also expose an "rg" source, which allows for capturing CRCs of the scanout raster before it's encoded into a video signal in the output path. This tap point is referred to as the raster generator. Note that while there's some other neat features that can be used with CRC capture on nvidia hardware, like capturing from two CRC sources simultaneously, I couldn't see any usecase for them and did not implement them. Nvidia only allows for accessing CRCs through a shared DMA region that we program through the core EVO/NvDisplay channel which is referred to as the notifier context. The notifier context is limited to either 255 (for Fermi-Pascal) or 2047 (Volta+) entries to store CRCs in, and unfortunately the hardware simply drops CRCs and reports an overflow once all available entries in the notifier context are filled. Since the DRM CRC API and igt-gpu-tools don't expect there to be a limit on how many CRCs can be captured, we work around this in nouveau by allocating two separate notifier contexts for each head instead of one. We schedule a vblank worker ahead of time so that once we start getting close to filling up all of the available entries in the notifier context, we can swap the currently used notifier context out with another pre-prepared notifier context in a manner similar to page flipping. Unfortunately, the hardware only allows us to this by flushing two separate updates on the core channel: one to release the current notifier context handle, and one to program the next notifier context's handle. When the hardware processes the first update, the CRC for the current frame is lost. However, the second update can be flushed immediately without waiting for the first to complete so that CRC generation resumes on the next frame. According to Nvidia's hardware engineers, there isn't any cleaner way of flipping notifier contexts that would avoid this. Since using vblank workers to swap out the notifier context will ensure we can usually flush both updates to hardware within the timespan of a single frame, we can also ensure that there will only be exactly one frame lost between the first and second update being executed by the hardware. This gives us the guarantee that we're always correctly matching each CRC entry with it's respective frame even after a context flip. And since IGT will retrieve the CRC entry for a frame by waiting until it receives a CRC for any subsequent frames, this doesn't cause an issue with any tests and is much simpler than trying to change the current DRM API to accommodate. In order to facilitate testing of correct handling of this limitation, we also expose a debugfs interface to manually control the threshold for when we start trying to flip the notifier context. We will use this in igt to trigger a context flip for testing purposes without needing to wait for the notifier to completely fill up. This threshold is reset to the default value set by nouveau after each capture, and is exposed in a separate folder within each CRTC's debugfs directory labelled "nv_crc". Changes since v1: * Forgot to finish saving crc.h before saving, whoops. This just adds some corrections to the empty function declarations that we use if CONFIG_DEBUG_FS isn't enabled. Changes since v2: * Don't check return code from debugfs_create_dir() or debugfs_create_file() - Greg K-H Changes since v3: (no functional changes) * Fix SPDX license identifiers (checkpatch) * s/uint32_t/u32/ (checkpatch) * Fix indenting in switch cases (checkpatch) Changes since v4: * Remove unneeded param changes with nv50_head_flush_clr/set * Rebase Changes since v5: * Remove set but unused variable (outp) in nv50_crc_atomic_check() - Kbuild bot Signed-off-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Ben Skeggs <bskeggs@redhat.com> Acked-by: Dave Airlie <airlied@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-10-lyude@redhat.com
268 lines
3.8 KiB
C
268 lines
3.8 KiB
C
#ifndef __NV50_KMS_ATOM_H__
|
|
#define __NV50_KMS_ATOM_H__
|
|
#define nv50_atom(p) container_of((p), struct nv50_atom, state)
|
|
#include <drm/drm_atomic.h>
|
|
#include "crc.h"
|
|
|
|
struct nouveau_encoder;
|
|
|
|
struct nv50_atom {
|
|
struct drm_atomic_state state;
|
|
|
|
struct list_head outp;
|
|
bool lock_core;
|
|
bool flush_disable;
|
|
};
|
|
|
|
#define nv50_head_atom(p) container_of((p), struct nv50_head_atom, state)
|
|
|
|
struct nv50_head_atom {
|
|
struct drm_crtc_state state;
|
|
|
|
struct {
|
|
u32 mask;
|
|
u32 owned;
|
|
u32 olut;
|
|
} wndw;
|
|
|
|
struct {
|
|
u16 iW;
|
|
u16 iH;
|
|
u16 oW;
|
|
u16 oH;
|
|
} view;
|
|
|
|
struct nv50_head_mode {
|
|
bool interlace;
|
|
u32 clock;
|
|
struct {
|
|
u16 active;
|
|
u16 synce;
|
|
u16 blanke;
|
|
u16 blanks;
|
|
} h;
|
|
struct {
|
|
u32 active;
|
|
u16 synce;
|
|
u16 blanke;
|
|
u16 blanks;
|
|
u16 blank2s;
|
|
u16 blank2e;
|
|
u16 blankus;
|
|
} v;
|
|
} mode;
|
|
|
|
struct {
|
|
bool visible;
|
|
u32 handle;
|
|
u64 offset:40;
|
|
u8 buffer:1;
|
|
u8 mode:4;
|
|
u16 size:11;
|
|
u8 range:2;
|
|
u8 output_mode:2;
|
|
void (*load)(struct drm_color_lut *, int size, void __iomem *);
|
|
} olut;
|
|
|
|
struct {
|
|
bool visible;
|
|
u32 handle;
|
|
u64 offset:40;
|
|
u8 format;
|
|
u8 kind:7;
|
|
u8 layout:1;
|
|
u8 blockh:4;
|
|
u16 blocks:12;
|
|
u32 pitch:20;
|
|
u16 x;
|
|
u16 y;
|
|
u16 w;
|
|
u16 h;
|
|
} core;
|
|
|
|
struct {
|
|
bool visible;
|
|
u32 handle;
|
|
u64 offset:40;
|
|
u8 layout:2;
|
|
u8 format:8;
|
|
} curs;
|
|
|
|
struct {
|
|
u8 depth;
|
|
u8 cpp;
|
|
u16 x;
|
|
u16 y;
|
|
u16 w;
|
|
u16 h;
|
|
} base;
|
|
|
|
struct {
|
|
u8 cpp;
|
|
} ovly;
|
|
|
|
struct {
|
|
bool enable:1;
|
|
u8 bits:2;
|
|
u8 mode:4;
|
|
} dither;
|
|
|
|
struct {
|
|
struct {
|
|
u16 cos:12;
|
|
u16 sin:12;
|
|
} sat;
|
|
} procamp;
|
|
|
|
struct {
|
|
u8 nhsync:1;
|
|
u8 nvsync:1;
|
|
u8 depth:4;
|
|
u8 crc_raster:2;
|
|
u8 bpc;
|
|
} or;
|
|
|
|
struct nv50_crc_atom crc;
|
|
|
|
/* Currently only used for MST */
|
|
struct {
|
|
int pbn;
|
|
u8 tu:6;
|
|
} dp;
|
|
|
|
union nv50_head_atom_mask {
|
|
struct {
|
|
bool olut:1;
|
|
bool core:1;
|
|
bool curs:1;
|
|
bool view:1;
|
|
bool mode:1;
|
|
bool base:1;
|
|
bool ovly:1;
|
|
bool dither:1;
|
|
bool procamp:1;
|
|
bool crc:1;
|
|
bool or:1;
|
|
};
|
|
u16 mask;
|
|
} set, clr;
|
|
};
|
|
|
|
static inline struct nv50_head_atom *
|
|
nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc)
|
|
{
|
|
struct drm_crtc_state *statec = drm_atomic_get_crtc_state(state, crtc);
|
|
if (IS_ERR(statec))
|
|
return (void *)statec;
|
|
return nv50_head_atom(statec);
|
|
}
|
|
|
|
static inline struct drm_encoder *
|
|
nv50_head_atom_get_encoder(struct nv50_head_atom *atom)
|
|
{
|
|
struct drm_encoder *encoder = NULL;
|
|
|
|
/* We only ever have a single encoder */
|
|
drm_for_each_encoder_mask(encoder, atom->state.crtc->dev,
|
|
atom->state.encoder_mask)
|
|
break;
|
|
|
|
return encoder;
|
|
}
|
|
|
|
#define nv50_wndw_atom(p) container_of((p), struct nv50_wndw_atom, state)
|
|
|
|
struct nv50_wndw_atom {
|
|
struct drm_plane_state state;
|
|
|
|
struct drm_property_blob *ilut;
|
|
bool visible;
|
|
|
|
struct {
|
|
u32 handle;
|
|
u16 offset:12;
|
|
bool awaken:1;
|
|
} ntfy;
|
|
|
|
struct {
|
|
u32 handle;
|
|
u16 offset:12;
|
|
u32 acquire;
|
|
u32 release;
|
|
} sema;
|
|
|
|
struct {
|
|
u32 handle;
|
|
struct {
|
|
u64 offset:40;
|
|
u8 buffer:1;
|
|
u8 enable:2;
|
|
u8 mode:4;
|
|
u16 size:11;
|
|
u8 range:2;
|
|
u8 output_mode:2;
|
|
void (*load)(struct drm_color_lut *, int size,
|
|
void __iomem *);
|
|
} i;
|
|
} xlut;
|
|
|
|
struct {
|
|
u32 matrix[12];
|
|
bool valid;
|
|
} csc;
|
|
|
|
struct {
|
|
u8 mode:2;
|
|
u8 interval:4;
|
|
|
|
u8 colorspace:2;
|
|
u8 format;
|
|
u8 kind:7;
|
|
u8 layout:1;
|
|
u8 blockh:4;
|
|
u16 blocks[3];
|
|
u32 pitch[3];
|
|
u16 w;
|
|
u16 h;
|
|
|
|
u32 handle[6];
|
|
u64 offset[6];
|
|
} image;
|
|
|
|
struct {
|
|
u16 sx;
|
|
u16 sy;
|
|
u16 sw;
|
|
u16 sh;
|
|
u16 dw;
|
|
u16 dh;
|
|
} scale;
|
|
|
|
struct {
|
|
u16 x;
|
|
u16 y;
|
|
} point;
|
|
|
|
struct {
|
|
u8 depth;
|
|
u8 k1;
|
|
u8 src_color:4;
|
|
u8 dst_color:4;
|
|
} blend;
|
|
|
|
union nv50_wndw_atom_mask {
|
|
struct {
|
|
bool ntfy:1;
|
|
bool sema:1;
|
|
bool xlut:1;
|
|
bool csc:1;
|
|
bool image:1;
|
|
bool scale:1;
|
|
bool point:1;
|
|
bool blend:1;
|
|
};
|
|
u8 mask;
|
|
} set, clr;
|
|
};
|
|
#endif
|