]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/gpu/drm/nouveau/nouveau_display.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / gpu / drm / nouveau / nouveau_display.c
index 2e11fd65b4dd2898335aba8926412a6164496550..505c6bfb4d75cb4ed1aa23fc24373a01c1929659 100644 (file)
@@ -29,6 +29,9 @@
 #include "nouveau_drv.h"
 #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)
@@ -104,3 +107,207 @@ const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
        .output_poll_changed = nouveau_fbcon_output_poll_changed,
 };
 
+int
+nouveau_vblank_enable(struct drm_device *dev, int crtc)
+{
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->card_type >= NV_50)
+               nv_mask(dev, NV50_PDISPLAY_INTR_EN_1, 0,
+                       NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc));
+       else
+               NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0,
+                           NV_PCRTC_INTR_0_VBLANK);
+
+       return 0;
+}
+
+void
+nouveau_vblank_disable(struct drm_device *dev, int crtc)
+{
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->card_type >= NV_50)
+               nv_mask(dev, NV50_PDISPLAY_INTR_EN_1,
+                       NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc), 0);
+       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;
+}