diff options
author | Paul Moore <pmoore@redhat.com> | 2014-01-28 14:44:16 -0500 |
---|---|---|
committer | Paul Moore <pmoore@redhat.com> | 2014-02-05 10:39:48 -0500 |
commit | 825e587af2e90e9b953849f3347a01d8f383d577 (patch) | |
tree | e48942a05882da47544e179c6a0c920e00137a6a /drivers/gpu/drm/nouveau/nouveau_display.c | |
parent | 8ed814602876bec9bad2649ca17f34b499357a1c (diff) | |
parent | d8ec26d7f8287f5788a494f56e8814210f0e64be (diff) |
Merge tag 'v3.13' into stable-3.14
Linux 3.13
Conflicts:
security/selinux/hooks.c
Trivial merge issue in selinux_inet_conn_request() likely due to me
including patches that I sent to the stable folks in my next tree
resulting in the patch hitting twice (I think). Thankfully it was an
easy fix this time, but regardless, lesson learned, I will not do that
again.
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 188 |
1 files changed, 130 insertions, 58 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 7848590f556..25ea82f8def 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -26,7 +26,6 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> -#include <drm/ttm/ttm_execbuf_util.h> #include "nouveau_fbcon.h" #include "dispnv04/hw.h" @@ -38,19 +37,92 @@ #include "nouveau_fence.h" -#include <subdev/bios/gpio.h> -#include <subdev/gpio.h> #include <engine/disp.h> #include <core/class.h> +static int +nouveau_display_vblank_handler(void *data, int head) +{ + struct nouveau_drm *drm = data; + drm_handle_vblank(drm->dev, head); + return NVKM_EVENT_KEEP; +} + +int +nouveau_display_vblank_enable(struct drm_device *dev, int head) +{ + struct nouveau_display *disp = nouveau_display(dev); + if (disp) { + nouveau_event_get(disp->vblank[head]); + return 0; + } + return -EIO; +} + +void +nouveau_display_vblank_disable(struct drm_device *dev, int head) +{ + struct nouveau_display *disp = nouveau_display(dev); + if (disp) + nouveau_event_put(disp->vblank[head]); +} + +static void +nouveau_display_vblank_fini(struct drm_device *dev) +{ + struct nouveau_display *disp = nouveau_display(dev); + int i; + + if (disp->vblank) { + for (i = 0; i < dev->mode_config.num_crtc; i++) + nouveau_event_ref(NULL, &disp->vblank[i]); + kfree(disp->vblank); + disp->vblank = NULL; + } + + drm_vblank_cleanup(dev); +} + +static int +nouveau_display_vblank_init(struct drm_device *dev) +{ + struct nouveau_display *disp = nouveau_display(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_disp *pdisp = nouveau_disp(drm->device); + int ret, i; + + disp->vblank = kzalloc(dev->mode_config.num_crtc * + sizeof(*disp->vblank), GFP_KERNEL); + if (!disp->vblank) + return -ENOMEM; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + ret = nouveau_event_new(pdisp->vblank, i, + nouveau_display_vblank_handler, + drm, &disp->vblank[i]); + if (ret) { + nouveau_display_vblank_fini(dev); + return ret; + } + } + + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) { + nouveau_display_vblank_fini(dev); + return ret; + } + + return 0; +} + static void nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) { struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); if (fb->nvbo) - drm_gem_object_unreference_unlocked(fb->nvbo->gem); + drm_gem_object_unreference_unlocked(&fb->nvbo->gem); drm_framebuffer_cleanup(drm_fb); kfree(fb); @@ -63,7 +135,7 @@ nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb, { struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); - return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle); + return drm_gem_handle_create(file_priv, &fb->nvbo->gem, handle); } static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { @@ -227,9 +299,7 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = { int nouveau_display_init(struct drm_device *dev) { - struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_display *disp = nouveau_display(dev); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct drm_connector *connector; int ret; @@ -243,10 +313,7 @@ nouveau_display_init(struct drm_device *dev) /* enable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) { - nouveau_event_get(gpio->events, conn->hpd.line, - &conn->hpd_func); - } + if (conn->hpd_func) nouveau_event_get(conn->hpd_func); } return ret; @@ -255,18 +322,13 @@ nouveau_display_init(struct drm_device *dev) void nouveau_display_fini(struct drm_device *dev) { - struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_display *disp = nouveau_display(dev); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct drm_connector *connector; /* disable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - if (gpio && conn->hpd.func != DCB_GPIO_UNUSED) { - nouveau_event_put(gpio->events, conn->hpd.line, - &conn->hpd_func); - } + if (conn->hpd_func) nouveau_event_put(conn->hpd_func); } drm_kms_helper_poll_disable(dev); @@ -336,6 +398,11 @@ nouveau_display_create(struct drm_device *dev) dev->mode_config.preferred_depth = 24; dev->mode_config.prefer_shadow = 1; + if (nv_device(drm->device)->chipset < 0x11) + dev->mode_config.async_page_flip = false; + else + dev->mode_config.async_page_flip = true; + drm_kms_helper_poll_init(dev); drm_kms_helper_poll_disable(dev); @@ -352,7 +419,7 @@ nouveau_display_create(struct drm_device *dev) goto disp_create_err; if (dev->mode_config.num_crtc) { - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + ret = nouveau_display_vblank_init(dev); if (ret) goto vblank_err; } @@ -374,7 +441,7 @@ nouveau_display_destroy(struct drm_device *dev) struct nouveau_display *disp = nouveau_display(dev); nouveau_backlight_exit(dev); - drm_vblank_cleanup(dev); + nouveau_display_vblank_fini(dev); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); @@ -394,7 +461,7 @@ nouveau_display_suspend(struct drm_device *dev) nouveau_display_fini(dev); - NV_SUSPEND(drm, "unpinning framebuffer(s)...\n"); + NV_INFO(drm, "unpinning framebuffer(s)...\n"); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_framebuffer *nouveau_fb; @@ -492,19 +559,15 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, goto fail; /* Emit the pageflip */ - ret = RING_SPACE(chan, 3); + ret = RING_SPACE(chan, 2); if (ret) goto fail; - if (nv_device(drm->device)->card_type < NV_C0) { + if (nv_device(drm->device)->card_type < NV_C0) BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); - OUT_RING (chan, 0x00000000); - OUT_RING (chan, 0x00000000); - } else { - BEGIN_NVC0(chan, 0, NV10_SUBCHAN_REF_CNT, 1); - OUT_RING (chan, 0); - BEGIN_IMC0(chan, 0, NVSW_SUBCHAN_PAGE_FLIP, 0x0000); - } + else + BEGIN_NVC0(chan, FermiSw, NV_SW_PAGE_FLIP, 1); + OUT_RING (chan, 0x00000000); FIRE_RING (chan); ret = nouveau_fence_new(chan, false, pfence); @@ -521,22 +584,16 @@ fail: int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags) + struct drm_pending_vblank_event *event, u32 flags) { + const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1; struct drm_device *dev = crtc->dev; struct nouveau_drm *drm = nouveau_drm(dev); 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 = NULL; + struct nouveau_channel *chan = drm->channel; struct nouveau_fence *fence; - struct ttm_validate_buffer resv[2] = { - { .bo = &old_bo->bo }, - { .bo = &new_bo->bo }, - }; - struct ww_acquire_ctx ticket; - LIST_HEAD(res); int ret; if (!drm->channel) @@ -546,26 +603,23 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, if (!s) return -ENOMEM; - /* Choose the channel the flip will be handled in */ - spin_lock(&old_bo->bo.bdev->fence_lock); - fence = new_bo->bo.sync_obj; - if (fence) - chan = fence->channel; - if (!chan) - chan = drm->channel; - spin_unlock(&old_bo->bo.bdev->fence_lock); + /* synchronise rendering channel with the kernel's channel */ + spin_lock(&new_bo->bo.bdev->fence_lock); + fence = nouveau_fence_ref(new_bo->bo.sync_obj); + spin_unlock(&new_bo->bo.bdev->fence_lock); + ret = nouveau_fence_sync(fence, chan); + nouveau_fence_unref(&fence); + if (ret) + goto fail_free; if (new_bo != old_bo) { ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM); if (ret) goto fail_free; - - list_add(&resv[1].head, &res); } - list_add(&resv[0].head, &res); mutex_lock(&chan->cli->mutex); - ret = ttm_eu_reserve_buffers(&ticket, &res); + ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL); if (ret) goto fail_unpin; @@ -577,12 +631,29 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Emit a page flip */ if (nv_device(drm->device)->card_type >= NV_50) { - ret = nv50_display_flip_next(crtc, fb, chan, 0); + ret = nv50_display_flip_next(crtc, fb, chan, swap_interval); if (ret) goto fail_unreserve; } else { struct nv04_display *dispnv04 = nv04_display(dev); - nouveau_bo_ref(new_bo, &dispnv04->image[nouveau_crtc(crtc)->index]); + int head = nouveau_crtc(crtc)->index; + + if (swap_interval) { + ret = RING_SPACE(chan, 8); + if (ret) + goto fail_unreserve; + + BEGIN_NV04(chan, NvSubImageBlit, 0x012c, 1); + OUT_RING (chan, 0); + BEGIN_NV04(chan, NvSubImageBlit, 0x0134, 1); + OUT_RING (chan, head); + BEGIN_NV04(chan, NvSubImageBlit, 0x0100, 1); + OUT_RING (chan, 0); + BEGIN_NV04(chan, NvSubImageBlit, 0x0130, 1); + OUT_RING (chan, 0); + } + + nouveau_bo_ref(new_bo, &dispnv04->image[head]); } ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); @@ -593,14 +664,15 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Update the crtc struct and cleanup */ crtc->fb = fb; - ttm_eu_fence_buffer_objects(&ticket, &res, fence); + nouveau_bo_fence(old_bo, fence); + ttm_bo_unreserve(&old_bo->bo); if (old_bo != new_bo) nouveau_bo_unpin(old_bo); nouveau_fence_unref(&fence); return 0; fail_unreserve: - ttm_eu_backoff_reservation(&ticket, &res); + ttm_bo_unreserve(&old_bo->bo); fail_unpin: mutex_unlock(&chan->cli->mutex); if (old_bo != new_bo) @@ -630,7 +702,7 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head); if (s->event) - drm_send_vblank_event(dev, -1, s->event); + drm_send_vblank_event(dev, s->crtc, s->event); list_del(&s->head); if (ps) @@ -674,8 +746,8 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, if (ret) return ret; - ret = drm_gem_handle_create(file_priv, bo->gem, &args->handle); - drm_gem_object_unreference_unlocked(bo->gem); + ret = drm_gem_handle_create(file_priv, &bo->gem, &args->handle); + drm_gem_object_unreference_unlocked(&bo->gem); return ret; } @@ -688,7 +760,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv, gem = drm_gem_object_lookup(dev, file_priv, handle); if (gem) { - struct nouveau_bo *bo = gem->driver_private; + struct nouveau_bo *bo = nouveau_gem_object(gem); *poffset = drm_vma_node_offset_addr(&bo->bo.vma_node); drm_gem_object_unreference_unlocked(gem); return 0; |