Skip to content

Commit

Permalink
drm/nouveau/kms: Support NVIDIA format modifiers
Browse files Browse the repository at this point in the history
Allow setting the block layout of a nouveau FB
object using DRM format modifiers.  When
specified, the format modifier block layout and
kind overrides the GEM buffer's implicit layout
and kind.  The specified format modifier is
validated against the list of modifiers supported
by the target display hardware.

v2: Used Tesla family instead of NV50 chipset compare
v4: Do not cache kind, tile_mode in nouveau_framebuffer
v5: Resolved against nouveau_framebuffer cleanup

Signed-off-by: James Jones <[email protected]>
Signed-off-by: Ben Skeggs <[email protected]>
  • Loading branch information
cubanismo authored and Ben Skeggs committed May 22, 2020
1 parent 4f5746c commit fa4f4c2
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 9 deletions.
20 changes: 13 additions & 7 deletions drivers/gpu/drm/nouveau/dispnv50/wndw.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ nv50_wndw_ctxdma_new(struct nv50_wndw *wndw, struct drm_framebuffer *fb)
{
struct nouveau_drm *drm = nouveau_drm(fb->dev);
struct nv50_wndw_ctxdma *ctxdma;
struct nouveau_bo *nvbo = nouveau_gem_object(fb->obj[0]);
const u8 kind = nvbo->kind;
const u32 handle = 0xfb000000 | kind;
u32 handle;
u32 unused;
u8 kind;
struct {
struct nv_dma_v0 base;
union {
Expand All @@ -58,6 +58,9 @@ nv50_wndw_ctxdma_new(struct nv50_wndw *wndw, struct drm_framebuffer *fb)
u32 argc = sizeof(args.base);
int ret;

nouveau_framebuffer_get_layout(fb, &unused, &kind);
handle = 0xfb000000 | kind;

list_for_each_entry(ctxdma, &wndw->ctxdma.list, head) {
if (ctxdma->object.handle == handle)
return ctxdma;
Expand Down Expand Up @@ -238,15 +241,18 @@ nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, bool modeset,
{
struct drm_framebuffer *fb = asyw->state.fb;
struct nouveau_drm *drm = nouveau_drm(wndw->plane.dev);
struct nouveau_bo *nvbo = nouveau_gem_object(fb->obj[0]);
uint8_t kind;
uint32_t tile_mode;
int ret;

NV_ATOMIC(drm, "%s acquire\n", wndw->plane.name);

if (fb != armw->state.fb || !armw->visible || modeset) {
nouveau_framebuffer_get_layout(fb, &tile_mode, &kind);

asyw->image.w = fb->width;
asyw->image.h = fb->height;
asyw->image.kind = nvbo->kind;
asyw->image.kind = kind;

ret = nv50_wndw_atomic_check_acquire_rgb(asyw);
if (ret) {
Expand All @@ -258,9 +264,9 @@ nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, bool modeset,
if (asyw->image.kind) {
asyw->image.layout = 0;
if (drm->client.device.info.chipset >= 0xc0)
asyw->image.blockh = nvbo->mode >> 4;
asyw->image.blockh = tile_mode >> 4;
else
asyw->image.blockh = nvbo->mode;
asyw->image.blockh = tile_mode;
asyw->image.blocks[0] = fb->pitches[0] / 64;
asyw->image.pitch[0] = 0;
} else {
Expand Down
89 changes: 87 additions & 2 deletions drivers/gpu/drm/nouveau/nouveau_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,76 @@ static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
.create_handle = drm_gem_fb_create_handle,
};

static void
nouveau_decode_mod(struct nouveau_drm *drm,
uint64_t modifier,
uint32_t *tile_mode,
uint8_t *kind)
{
BUG_ON(!tile_mode || !kind);

if (modifier == DRM_FORMAT_MOD_LINEAR) {
/* tile_mode will not be used in this case */
*tile_mode = 0;
*kind = 0;
} else {
/*
* Extract the block height and kind from the corresponding
* modifier fields. See drm_fourcc.h for details.
*/
*tile_mode = (uint32_t)(modifier & 0xF);
*kind = (uint8_t)((modifier >> 12) & 0xFF);

if (drm->client.device.info.chipset >= 0xc0)
*tile_mode <<= 4;
}
}

void
nouveau_framebuffer_get_layout(struct drm_framebuffer *fb,
uint32_t *tile_mode,
uint8_t *kind)
{
if (fb->flags & DRM_MODE_FB_MODIFIERS) {
struct nouveau_drm *drm = nouveau_drm(fb->dev);

nouveau_decode_mod(drm, fb->modifier, tile_mode, kind);
} else {
const struct nouveau_bo *nvbo = nouveau_gem_object(fb->obj[0]);

*tile_mode = nvbo->mode;
*kind = nvbo->kind;
}
}

static int
nouveau_validate_decode_mod(struct nouveau_drm *drm,
uint64_t modifier,
uint32_t *tile_mode,
uint8_t *kind)
{
struct nouveau_display *disp = nouveau_display(drm->dev);
int mod;

if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) {
return -EINVAL;
}

BUG_ON(!disp->format_modifiers);

for (mod = 0;
(disp->format_modifiers[mod] != DRM_FORMAT_MOD_INVALID) &&
(disp->format_modifiers[mod] != modifier);
mod++);

if (disp->format_modifiers[mod] == DRM_FORMAT_MOD_INVALID)
return -EINVAL;

nouveau_decode_mod(drm, modifier, tile_mode, kind);

return 0;
}

static inline uint32_t
nouveau_get_width_in_blocks(uint32_t stride)
{
Expand Down Expand Up @@ -266,6 +336,8 @@ nouveau_framebuffer_new(struct drm_device *dev,
struct drm_framebuffer *fb;
const struct drm_format_info *info;
unsigned int width, height, i;
uint32_t tile_mode;
uint8_t kind;
int ret;

/* YUV overlays have special requirements pre-NV50 */
Expand All @@ -288,6 +360,18 @@ nouveau_framebuffer_new(struct drm_device *dev,
return -EINVAL;
}

if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) {
if (nouveau_validate_decode_mod(drm, mode_cmd->modifier[0],
&tile_mode, &kind)) {
DRM_DEBUG_KMS("Unsupported modifier: 0x%llx\n",
mode_cmd->modifier[0]);
return -EINVAL;
}
} else {
tile_mode = nvbo->mode;
kind = nvbo->kind;
}

info = drm_get_format_info(dev, mode_cmd);

for (i = 0; i < info->num_planes; i++) {
Expand All @@ -298,11 +382,11 @@ nouveau_framebuffer_new(struct drm_device *dev,
mode_cmd->height,
i);

if (nvbo->kind) {
if (kind) {
ret = nouveau_check_bl_size(drm, nvbo,
mode_cmd->offsets[i],
mode_cmd->pitches[i],
height, nvbo->mode);
height, tile_mode);
if (ret)
return ret;
} else {
Expand Down Expand Up @@ -592,6 +676,7 @@ nouveau_display_create(struct drm_device *dev)

dev->mode_config.preferred_depth = 24;
dev->mode_config.prefer_shadow = 1;
dev->mode_config.allow_fb_modifiers = true;

if (drm->client.device.info.chipset < 0x11)
dev->mode_config.async_page_flip = false;
Expand Down
4 changes: 4 additions & 0 deletions drivers/gpu/drm/nouveau/nouveau_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,

void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);

void
nouveau_framebuffer_get_layout(struct drm_framebuffer *fb, uint32_t *tile_mode,
uint8_t *kind);

struct drm_framebuffer *
nouveau_user_framebuffer_create(struct drm_device *, struct drm_file *,
const struct drm_mode_fb_cmd2 *);
Expand Down

0 comments on commit fa4f4c2

Please sign in to comment.