diff options
author | James Jones <jajones@nvidia.com> | 2020-02-10 15:15:55 -0800 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2020-05-22 11:11:16 +1000 |
commit | fa4f4c213f5f7807360c41f2501a3031a9940f3a (patch) | |
tree | 30aa71544b9ab12c78fc38b3ab79963035e508f8 /drivers/gpu/drm/nouveau/nouveau_display.c | |
parent | 4f5746c863db1a98535964009da19c6f7a842c55 (diff) |
drm/nouveau/kms: Support NVIDIA format modifiers
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 <jajones@nvidia.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 89 |
1 files changed, 87 insertions, 2 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 7d6b344047ab..496c4621cc78 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -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) { @@ -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 */ @@ -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++) { @@ -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 { @@ -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; |