diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nouveau/dispnv50/wndw.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 89 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.h | 4 |
3 files changed, 104 insertions, 9 deletions
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index 1425a9ca86cf..e25ead56052c 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -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 { @@ -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; @@ -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) { @@ -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 { 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; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 1dd69c50a4ed..6e0d900441d6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -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 *); |