Path: blob/master/drivers/gpu/drm/nouveau/nouveau_display.c
15112 views
/*1* Copyright (C) 2008 Maarten Maathuis.2* All Rights Reserved.3*4* Permission is hereby granted, free of charge, to any person obtaining5* a copy of this software and associated documentation files (the6* "Software"), to deal in the Software without restriction, including7* without limitation the rights to use, copy, modify, merge, publish,8* distribute, sublicense, and/or sell copies of the Software, and to9* permit persons to whom the Software is furnished to do so, subject to10* the following conditions:11*12* The above copyright notice and this permission notice (including the13* next paragraph) shall be included in all copies or substantial14* portions of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.19* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE20* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION21* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION22* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.23*24*/2526#include "drmP.h"27#include "drm_crtc_helper.h"28#include "nouveau_drv.h"29#include "nouveau_fb.h"30#include "nouveau_fbcon.h"31#include "nouveau_hw.h"32#include "nouveau_crtc.h"33#include "nouveau_dma.h"34#include "nv50_display.h"3536static void37nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)38{39struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);4041if (fb->nvbo)42drm_gem_object_unreference_unlocked(fb->nvbo->gem);4344drm_framebuffer_cleanup(drm_fb);45kfree(fb);46}4748static int49nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb,50struct drm_file *file_priv,51unsigned int *handle)52{53struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);5455return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle);56}5758static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {59.destroy = nouveau_user_framebuffer_destroy,60.create_handle = nouveau_user_framebuffer_create_handle,61};6263int64nouveau_framebuffer_init(struct drm_device *dev,65struct nouveau_framebuffer *nv_fb,66struct drm_mode_fb_cmd *mode_cmd,67struct nouveau_bo *nvbo)68{69struct drm_nouveau_private *dev_priv = dev->dev_private;70struct drm_framebuffer *fb = &nv_fb->base;71int ret;7273ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs);74if (ret) {75return ret;76}7778drm_helper_mode_fill_fb_struct(fb, mode_cmd);79nv_fb->nvbo = nvbo;8081if (dev_priv->card_type >= NV_50) {82u32 tile_flags = nouveau_bo_tile_layout(nvbo);83if (tile_flags == 0x7a00 ||84tile_flags == 0xfe00)85nv_fb->r_dma = NvEvoFB32;86else87if (tile_flags == 0x7000)88nv_fb->r_dma = NvEvoFB16;89else90nv_fb->r_dma = NvEvoVRAM_LP;9192switch (fb->depth) {93case 8: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_8; break;94case 15: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_15; break;95case 16: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_16; break;96case 24:97case 32: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_24; break;98case 30: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_30; break;99default:100NV_ERROR(dev, "unknown depth %d\n", fb->depth);101return -EINVAL;102}103104if (dev_priv->chipset == 0x50)105nv_fb->r_format |= (tile_flags << 8);106107if (!tile_flags)108nv_fb->r_pitch = 0x00100000 | fb->pitch;109else {110u32 mode = nvbo->tile_mode;111if (dev_priv->card_type >= NV_C0)112mode >>= 4;113nv_fb->r_pitch = ((fb->pitch / 4) << 4) | mode;114}115}116117return 0;118}119120static struct drm_framebuffer *121nouveau_user_framebuffer_create(struct drm_device *dev,122struct drm_file *file_priv,123struct drm_mode_fb_cmd *mode_cmd)124{125struct nouveau_framebuffer *nouveau_fb;126struct drm_gem_object *gem;127int ret;128129gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);130if (!gem)131return ERR_PTR(-ENOENT);132133nouveau_fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);134if (!nouveau_fb)135return ERR_PTR(-ENOMEM);136137ret = nouveau_framebuffer_init(dev, nouveau_fb, mode_cmd, nouveau_gem_object(gem));138if (ret) {139drm_gem_object_unreference(gem);140return ERR_PTR(ret);141}142143return &nouveau_fb->base;144}145146const struct drm_mode_config_funcs nouveau_mode_config_funcs = {147.fb_create = nouveau_user_framebuffer_create,148.output_poll_changed = nouveau_fbcon_output_poll_changed,149};150151int152nouveau_vblank_enable(struct drm_device *dev, int crtc)153{154struct drm_nouveau_private *dev_priv = dev->dev_private;155156if (dev_priv->card_type >= NV_50)157nv_mask(dev, NV50_PDISPLAY_INTR_EN_1, 0,158NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc));159else160NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0,161NV_PCRTC_INTR_0_VBLANK);162163return 0;164}165166void167nouveau_vblank_disable(struct drm_device *dev, int crtc)168{169struct drm_nouveau_private *dev_priv = dev->dev_private;170171if (dev_priv->card_type >= NV_50)172nv_mask(dev, NV50_PDISPLAY_INTR_EN_1,173NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc), 0);174else175NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0);176}177178static int179nouveau_page_flip_reserve(struct nouveau_bo *old_bo,180struct nouveau_bo *new_bo)181{182int ret;183184ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM);185if (ret)186return ret;187188ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0);189if (ret)190goto fail;191192ret = ttm_bo_reserve(&old_bo->bo, false, false, false, 0);193if (ret)194goto fail_unreserve;195196return 0;197198fail_unreserve:199ttm_bo_unreserve(&new_bo->bo);200fail:201nouveau_bo_unpin(new_bo);202return ret;203}204205static void206nouveau_page_flip_unreserve(struct nouveau_bo *old_bo,207struct nouveau_bo *new_bo,208struct nouveau_fence *fence)209{210nouveau_bo_fence(new_bo, fence);211ttm_bo_unreserve(&new_bo->bo);212213nouveau_bo_fence(old_bo, fence);214ttm_bo_unreserve(&old_bo->bo);215216nouveau_bo_unpin(old_bo);217}218219static int220nouveau_page_flip_emit(struct nouveau_channel *chan,221struct nouveau_bo *old_bo,222struct nouveau_bo *new_bo,223struct nouveau_page_flip_state *s,224struct nouveau_fence **pfence)225{226struct drm_nouveau_private *dev_priv = chan->dev->dev_private;227struct drm_device *dev = chan->dev;228unsigned long flags;229int ret;230231/* Queue it to the pending list */232spin_lock_irqsave(&dev->event_lock, flags);233list_add_tail(&s->head, &chan->nvsw.flip);234spin_unlock_irqrestore(&dev->event_lock, flags);235236/* Synchronize with the old framebuffer */237ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan);238if (ret)239goto fail;240241/* Emit the pageflip */242ret = RING_SPACE(chan, 2);243if (ret)244goto fail;245246if (dev_priv->card_type < NV_C0)247BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);248else249BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0500, 1);250OUT_RING (chan, 0);251FIRE_RING (chan);252253ret = nouveau_fence_new(chan, pfence, true);254if (ret)255goto fail;256257return 0;258fail:259spin_lock_irqsave(&dev->event_lock, flags);260list_del(&s->head);261spin_unlock_irqrestore(&dev->event_lock, flags);262return ret;263}264265int266nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,267struct drm_pending_vblank_event *event)268{269struct drm_device *dev = crtc->dev;270struct drm_nouveau_private *dev_priv = dev->dev_private;271struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo;272struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo;273struct nouveau_page_flip_state *s;274struct nouveau_channel *chan;275struct nouveau_fence *fence;276int ret;277278if (!dev_priv->channel)279return -ENODEV;280281s = kzalloc(sizeof(*s), GFP_KERNEL);282if (!s)283return -ENOMEM;284285/* Don't let the buffers go away while we flip */286ret = nouveau_page_flip_reserve(old_bo, new_bo);287if (ret)288goto fail_free;289290/* Initialize a page flip struct */291*s = (struct nouveau_page_flip_state)292{ { }, event, nouveau_crtc(crtc)->index,293fb->bits_per_pixel, fb->pitch, crtc->x, crtc->y,294new_bo->bo.offset };295296/* Choose the channel the flip will be handled in */297chan = nouveau_fence_channel(new_bo->bo.sync_obj);298if (!chan)299chan = nouveau_channel_get_unlocked(dev_priv->channel);300mutex_lock(&chan->mutex);301302/* Emit a page flip */303if (dev_priv->card_type >= NV_50) {304ret = nv50_display_flip_next(crtc, fb, chan);305if (ret) {306nouveau_channel_put(&chan);307goto fail_unreserve;308}309}310311ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);312nouveau_channel_put(&chan);313if (ret)314goto fail_unreserve;315316/* Update the crtc struct and cleanup */317crtc->fb = fb;318319nouveau_page_flip_unreserve(old_bo, new_bo, fence);320nouveau_fence_unref(&fence);321return 0;322323fail_unreserve:324nouveau_page_flip_unreserve(old_bo, new_bo, NULL);325fail_free:326kfree(s);327return ret;328}329330int331nouveau_finish_page_flip(struct nouveau_channel *chan,332struct nouveau_page_flip_state *ps)333{334struct drm_device *dev = chan->dev;335struct nouveau_page_flip_state *s;336unsigned long flags;337338spin_lock_irqsave(&dev->event_lock, flags);339340if (list_empty(&chan->nvsw.flip)) {341NV_ERROR(dev, "Unexpected pageflip in channel %d.\n", chan->id);342spin_unlock_irqrestore(&dev->event_lock, flags);343return -EINVAL;344}345346s = list_first_entry(&chan->nvsw.flip,347struct nouveau_page_flip_state, head);348if (s->event) {349struct drm_pending_vblank_event *e = s->event;350struct timeval now;351352do_gettimeofday(&now);353e->event.sequence = 0;354e->event.tv_sec = now.tv_sec;355e->event.tv_usec = now.tv_usec;356list_add_tail(&e->base.link, &e->base.file_priv->event_list);357wake_up_interruptible(&e->base.file_priv->event_wait);358}359360list_del(&s->head);361if (ps)362*ps = *s;363kfree(s);364365spin_unlock_irqrestore(&dev->event_lock, flags);366return 0;367}368369370