mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
drm/nouveau: Implement the pageflip ioctl.
nv0x-nv4x should be mostly fine, nv50 doesn't work yet. Signed-off-by: Francisco Jerez <currojerez@riseup.net> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
042206c0cd
commit
332b242f47
14 changed files with 264 additions and 8 deletions
|
@ -942,6 +942,18 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
|
|||
return ttm_bo_validate(bo, &nvbo->placement, false, true, false);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence)
|
||||
{
|
||||
spin_lock(&nvbo->bo.bdev->fence_lock);
|
||||
__nouveau_fence_unref(&nvbo->bo.sync_obj);
|
||||
|
||||
if (likely(fence))
|
||||
nvbo->bo.sync_obj = nouveau_fence_ref(fence);
|
||||
|
||||
spin_unlock(&nvbo->bo.bdev->fence_lock);
|
||||
}
|
||||
|
||||
struct ttm_bo_driver nouveau_bo_driver = {
|
||||
.create_ttm_backend_entry = nouveau_bo_create_ttm_backend_entry,
|
||||
.invalidate_caches = nouveau_bo_invalidate_caches,
|
||||
|
|
|
@ -148,6 +148,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
|
|||
|
||||
NV_DEBUG(dev, "initialising channel %d\n", chan->id);
|
||||
INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
|
||||
INIT_LIST_HEAD(&chan->nvsw.flip);
|
||||
INIT_LIST_HEAD(&chan->fence.pending);
|
||||
|
||||
/* Allocate DMA push buffer */
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "nouveau_fb.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include "nouveau_hw.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nouveau_dma.h"
|
||||
|
||||
static void
|
||||
nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
|
||||
|
@ -131,3 +133,181 @@ nouveau_vblank_disable(struct drm_device *dev, int crtc)
|
|||
else
|
||||
NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_page_flip_reserve(struct nouveau_bo *old_bo,
|
||||
struct nouveau_bo *new_bo)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = ttm_bo_reserve(&old_bo->bo, false, false, false, 0);
|
||||
if (ret)
|
||||
goto fail_unreserve;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_unreserve:
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
fail:
|
||||
nouveau_bo_unpin(new_bo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_page_flip_unreserve(struct nouveau_bo *old_bo,
|
||||
struct nouveau_bo *new_bo,
|
||||
struct nouveau_fence *fence)
|
||||
{
|
||||
nouveau_bo_fence(new_bo, fence);
|
||||
ttm_bo_unreserve(&new_bo->bo);
|
||||
|
||||
nouveau_bo_fence(old_bo, fence);
|
||||
ttm_bo_unreserve(&old_bo->bo);
|
||||
|
||||
nouveau_bo_unpin(old_bo);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_page_flip_emit(struct nouveau_channel *chan,
|
||||
struct nouveau_bo *old_bo,
|
||||
struct nouveau_bo *new_bo,
|
||||
struct nouveau_page_flip_state *s,
|
||||
struct nouveau_fence **pfence)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* Queue it to the pending list */
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
list_add_tail(&s->head, &chan->nvsw.flip);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
/* Synchronize with the old framebuffer */
|
||||
ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* Emit the pageflip */
|
||||
ret = RING_SPACE(chan, 2);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);
|
||||
OUT_RING(chan, 0);
|
||||
FIRE_RING(chan);
|
||||
|
||||
ret = nouveau_fence_new(chan, pfence, true);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
list_del(&s->head);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo;
|
||||
struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo;
|
||||
struct nouveau_page_flip_state *s;
|
||||
struct nouveau_channel *chan;
|
||||
struct nouveau_fence *fence;
|
||||
int ret;
|
||||
|
||||
if (dev_priv->engine.graph.accel_blocked)
|
||||
return -ENODEV;
|
||||
|
||||
s = kzalloc(sizeof(*s), GFP_KERNEL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Don't let the buffers go away while we flip */
|
||||
ret = nouveau_page_flip_reserve(old_bo, new_bo);
|
||||
if (ret)
|
||||
goto fail_free;
|
||||
|
||||
/* Initialize a page flip struct */
|
||||
*s = (struct nouveau_page_flip_state)
|
||||
{ { }, s->event, nouveau_crtc(crtc)->index,
|
||||
fb->bits_per_pixel, fb->pitch, crtc->x, crtc->y,
|
||||
new_bo->bo.offset };
|
||||
|
||||
/* Choose the channel the flip will be handled in */
|
||||
chan = nouveau_fence_channel(new_bo->bo.sync_obj);
|
||||
if (!chan)
|
||||
chan = nouveau_channel_get_unlocked(dev_priv->channel);
|
||||
mutex_lock(&chan->mutex);
|
||||
|
||||
/* Emit a page flip */
|
||||
ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);
|
||||
nouveau_channel_put(&chan);
|
||||
if (ret)
|
||||
goto fail_unreserve;
|
||||
|
||||
/* Update the crtc struct and cleanup */
|
||||
crtc->fb = fb;
|
||||
|
||||
nouveau_page_flip_unreserve(old_bo, new_bo, fence);
|
||||
nouveau_fence_unref(&fence);
|
||||
return 0;
|
||||
|
||||
fail_unreserve:
|
||||
nouveau_page_flip_unreserve(old_bo, new_bo, NULL);
|
||||
fail_free:
|
||||
kfree(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_finish_page_flip(struct nouveau_channel *chan,
|
||||
struct nouveau_page_flip_state *ps)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct nouveau_page_flip_state *s;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
if (list_empty(&chan->nvsw.flip)) {
|
||||
NV_ERROR(dev, "Unexpected pageflip in channel %d.\n", chan->id);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s = list_first_entry(&chan->nvsw.flip,
|
||||
struct nouveau_page_flip_state, head);
|
||||
if (s->event) {
|
||||
struct drm_pending_vblank_event *e = s->event;
|
||||
struct timeval now;
|
||||
|
||||
do_gettimeofday(&now);
|
||||
e->event.sequence = 0;
|
||||
e->event.tv_sec = now.tv_sec;
|
||||
e->event.tv_usec = now.tv_usec;
|
||||
list_add_tail(&e->base.link, &e->base.file_priv->event_list);
|
||||
wake_up_interruptible(&e->base.file_priv->event_wait);
|
||||
}
|
||||
|
||||
list_del(&s->head);
|
||||
*ps = *s;
|
||||
kfree(s);
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -166,6 +166,13 @@ struct nouveau_gpuobj {
|
|||
void *priv;
|
||||
};
|
||||
|
||||
struct nouveau_page_flip_state {
|
||||
struct list_head head;
|
||||
struct drm_pending_vblank_event *event;
|
||||
int crtc, bpp, pitch, x, y;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
struct nouveau_channel {
|
||||
struct drm_device *dev;
|
||||
int id;
|
||||
|
@ -253,6 +260,7 @@ struct nouveau_channel {
|
|||
uint32_t vblsem_offset;
|
||||
uint32_t vblsem_rval;
|
||||
struct list_head vbl_wait;
|
||||
struct list_head flip;
|
||||
} nvsw;
|
||||
|
||||
struct {
|
||||
|
@ -1076,6 +1084,8 @@ extern void nv04_graph_destroy_context(struct nouveau_channel *);
|
|||
extern int nv04_graph_load_context(struct nouveau_channel *);
|
||||
extern int nv04_graph_unload_context(struct drm_device *);
|
||||
extern void nv04_graph_context_switch(struct drm_device *);
|
||||
extern int nv04_graph_mthd_page_flip(struct nouveau_channel *chan,
|
||||
u32 class, u32 mthd, u32 data);
|
||||
|
||||
/* nv10_graph.c */
|
||||
extern int nv10_graph_init(struct drm_device *);
|
||||
|
@ -1249,6 +1259,7 @@ extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index);
|
|||
extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val);
|
||||
extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index);
|
||||
extern void nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val);
|
||||
extern void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *);
|
||||
|
||||
/* nouveau_fence.c */
|
||||
struct nouveau_fence;
|
||||
|
@ -1315,6 +1326,10 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
|
|||
/* nouveau_display.c */
|
||||
int nouveau_vblank_enable(struct drm_device *dev, int crtc);
|
||||
void nouveau_vblank_disable(struct drm_device *dev, int crtc);
|
||||
int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event);
|
||||
int nouveau_finish_page_flip(struct nouveau_channel *,
|
||||
struct nouveau_page_flip_state *);
|
||||
|
||||
/* nv10_gpio.c */
|
||||
int nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
|
||||
|
@ -1514,5 +1529,6 @@ nv_match_device(struct drm_device *dev, unsigned device,
|
|||
#define NV_SW_VBLSEM_OFFSET 0x00000400
|
||||
#define NV_SW_VBLSEM_RELEASE_VALUE 0x00000404
|
||||
#define NV_SW_VBLSEM_RELEASE 0x00000408
|
||||
#define NV_SW_PAGE_FLIP 0x00000500
|
||||
|
||||
#endif /* __NOUVEAU_DRV_H__ */
|
||||
|
|
|
@ -231,15 +231,8 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
|
|||
|
||||
list_for_each_safe(entry, tmp, list) {
|
||||
nvbo = list_entry(entry, struct nouveau_bo, entry);
|
||||
if (likely(fence)) {
|
||||
struct nouveau_fence *prev_fence;
|
||||
|
||||
spin_lock(&nvbo->bo.bdev->fence_lock);
|
||||
prev_fence = nvbo->bo.sync_obj;
|
||||
nvbo->bo.sync_obj = nouveau_fence_ref(fence);
|
||||
spin_unlock(&nvbo->bo.bdev->fence_lock);
|
||||
nouveau_fence_unref(&prev_fence);
|
||||
}
|
||||
nouveau_bo_fence(nvbo, fence);
|
||||
|
||||
if (unlikely(nvbo->validate_mapped)) {
|
||||
ttm_bo_kunmap(&nvbo->kmap);
|
||||
|
|
|
@ -1090,6 +1090,9 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
|
|||
case NOUVEAU_GETPARAM_HAS_BO_USAGE:
|
||||
getparam->value = 1;
|
||||
break;
|
||||
case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
|
||||
getparam->value = (dev_priv->card_type < NV_50);
|
||||
break;
|
||||
case NOUVEAU_GETPARAM_GRAPH_UNITS:
|
||||
/* NV40 and NV50 versions are quite different, but register
|
||||
* address is the same. User is supposed to know the card
|
||||
|
|
|
@ -989,6 +989,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = {
|
|||
.cursor_move = nv04_crtc_cursor_move,
|
||||
.gamma_set = nv_crtc_gamma_set,
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.page_flip = nouveau_crtc_page_flip,
|
||||
.destroy = nv_crtc_destroy,
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "drm.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_hw.h"
|
||||
|
||||
static int nv04_graph_register(struct drm_device *dev);
|
||||
|
||||
|
@ -553,6 +554,20 @@ nv04_graph_mthd_set_ref(struct nouveau_channel *chan,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv04_graph_mthd_page_flip(struct nouveau_channel *chan,
|
||||
u32 class, u32 mthd, u32 data)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct nouveau_page_flip_state s;
|
||||
|
||||
if (!nouveau_finish_page_flip(chan, &s))
|
||||
nv_set_crtc_base(dev, s.crtc,
|
||||
s.offset + s.y * s.pitch + s.x * s.bpp / 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Software methods, why they are needed, and how they all work:
|
||||
*
|
||||
|
@ -1204,6 +1219,7 @@ nv04_graph_register(struct drm_device *dev)
|
|||
/* nvsw */
|
||||
NVOBJ_CLASS(dev, 0x506e, SW);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0150, nv04_graph_mthd_set_ref);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
|
||||
|
||||
dev_priv->engine.graph.registered = true;
|
||||
return 0;
|
||||
|
|
|
@ -1113,6 +1113,10 @@ nv10_graph_register(struct drm_device *dev)
|
|||
NVOBJ_MTHD (dev, 0x0099, 0x1658, nv17_graph_mthd_lma_enable);
|
||||
}
|
||||
|
||||
/* nvsw */
|
||||
NVOBJ_CLASS(dev, 0x506e, SW);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
|
||||
|
||||
dev_priv->engine.graph.registered = true;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -801,6 +801,10 @@ nv20_graph_register(struct drm_device *dev)
|
|||
else
|
||||
NVOBJ_CLASS(dev, 0x0597, GR);
|
||||
|
||||
/* nvsw */
|
||||
NVOBJ_CLASS(dev, 0x506e, SW);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
|
||||
|
||||
dev_priv->engine.graph.registered = true;
|
||||
return 0;
|
||||
}
|
||||
|
@ -841,6 +845,10 @@ nv30_graph_register(struct drm_device *dev)
|
|||
if (0x000001e0 & (1 << (dev_priv->chipset & 0x0f)))
|
||||
NVOBJ_CLASS(dev, 0x0497, GR);
|
||||
|
||||
/* nvsw */
|
||||
NVOBJ_CLASS(dev, 0x506e, SW);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
|
||||
|
||||
dev_priv->engine.graph.registered = true;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -446,6 +446,10 @@ nv40_graph_register(struct drm_device *dev)
|
|||
else
|
||||
NVOBJ_CLASS(dev, 0x4097, GR);
|
||||
|
||||
/* nvsw */
|
||||
NVOBJ_CLASS(dev, 0x506e, SW);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
|
||||
|
||||
dev_priv->engine.graph.registered = true;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@ static const struct drm_crtc_funcs nv50_crtc_funcs = {
|
|||
.cursor_move = nv50_crtc_cursor_move,
|
||||
.gamma_set = nv50_crtc_gamma_set,
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.page_flip = nouveau_crtc_page_flip,
|
||||
.destroy = nv50_crtc_destroy,
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "nouveau_drv.h"
|
||||
#include "nouveau_ramht.h"
|
||||
#include "nouveau_grctx.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nv50_evo.h"
|
||||
|
||||
static int nv50_graph_register(struct drm_device *);
|
||||
|
||||
|
@ -389,6 +391,19 @@ nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_graph_nvsw_mthd_page_flip(struct nouveau_channel *chan,
|
||||
u32 class, u32 mthd, u32 data)
|
||||
{
|
||||
struct nouveau_page_flip_state s;
|
||||
|
||||
if (!nouveau_finish_page_flip(chan, &s)) {
|
||||
/* XXX - Do something here */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_graph_register(struct drm_device *dev)
|
||||
{
|
||||
|
@ -402,6 +417,7 @@ nv50_graph_register(struct drm_device *dev)
|
|||
NVOBJ_MTHD (dev, 0x506e, 0x0400, nv50_graph_nvsw_vblsem_offset);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0404, nv50_graph_nvsw_vblsem_release_val);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0408, nv50_graph_nvsw_vblsem_release);
|
||||
NVOBJ_MTHD (dev, 0x506e, 0x0500, nv50_graph_nvsw_mthd_page_flip);
|
||||
|
||||
NVOBJ_CLASS(dev, 0x0030, GR); /* null */
|
||||
NVOBJ_CLASS(dev, 0x5039, GR); /* m2mf */
|
||||
|
|
|
@ -81,6 +81,7 @@ struct drm_nouveau_gpuobj_free {
|
|||
#define NOUVEAU_GETPARAM_GRAPH_UNITS 13
|
||||
#define NOUVEAU_GETPARAM_PTIMER_TIME 14
|
||||
#define NOUVEAU_GETPARAM_HAS_BO_USAGE 15
|
||||
#define NOUVEAU_GETPARAM_HAS_PAGEFLIP 16
|
||||
struct drm_nouveau_getparam {
|
||||
uint64_t param;
|
||||
uint64_t value;
|
||||
|
|
Loading…
Reference in a new issue