Path: blob/master/drivers/gpu/drm/nouveau/nouveau_dma.c
15112 views
/*1* Copyright (C) 2007 Ben Skeggs.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.h"28#include "nouveau_drv.h"29#include "nouveau_dma.h"30#include "nouveau_ramht.h"3132void33nouveau_dma_pre_init(struct nouveau_channel *chan)34{35struct drm_nouveau_private *dev_priv = chan->dev->dev_private;36struct nouveau_bo *pushbuf = chan->pushbuf_bo;3738if (dev_priv->card_type >= NV_50) {39const int ib_size = pushbuf->bo.mem.size / 2;4041chan->dma.ib_base = (pushbuf->bo.mem.size - ib_size) >> 2;42chan->dma.ib_max = (ib_size / 8) - 1;43chan->dma.ib_put = 0;44chan->dma.ib_free = chan->dma.ib_max - chan->dma.ib_put;4546chan->dma.max = (pushbuf->bo.mem.size - ib_size) >> 2;47} else {48chan->dma.max = (pushbuf->bo.mem.size >> 2) - 2;49}5051chan->dma.put = 0;52chan->dma.cur = chan->dma.put;53chan->dma.free = chan->dma.max - chan->dma.cur;54}5556int57nouveau_dma_init(struct nouveau_channel *chan)58{59struct drm_device *dev = chan->dev;60struct drm_nouveau_private *dev_priv = dev->dev_private;61int ret, i;6263if (dev_priv->card_type >= NV_C0) {64ret = nouveau_gpuobj_gr_new(chan, 0x9039, 0x9039);65if (ret)66return ret;6768ret = RING_SPACE(chan, 2);69if (ret)70return ret;7172BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0000, 1);73OUT_RING (chan, 0x00009039);74FIRE_RING (chan);75return 0;76}7778/* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */79ret = nouveau_gpuobj_gr_new(chan, NvM2MF, dev_priv->card_type < NV_50 ?800x0039 : 0x5039);81if (ret)82return ret;8384/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */85ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000,86&chan->m2mf_ntfy);87if (ret)88return ret;8990/* Insert NOPS for NOUVEAU_DMA_SKIPS */91ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);92if (ret)93return ret;9495for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)96OUT_RING(chan, 0);9798/* Initialise NV_MEMORY_TO_MEMORY_FORMAT */99ret = RING_SPACE(chan, 6);100if (ret)101return ret;102BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);103OUT_RING (chan, NvM2MF);104BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 3);105OUT_RING (chan, NvNotify0);106OUT_RING (chan, chan->vram_handle);107OUT_RING (chan, chan->gart_handle);108109/* Sit back and pray the channel works.. */110FIRE_RING(chan);111112return 0;113}114115void116OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)117{118bool is_iomem;119u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem);120mem = &mem[chan->dma.cur];121if (is_iomem)122memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4);123else124memcpy(mem, data, nr_dwords * 4);125chan->dma.cur += nr_dwords;126}127128/* Fetch and adjust GPU GET pointer129*130* Returns:131* value >= 0, the adjusted GET pointer132* -EINVAL if GET pointer currently outside main push buffer133* -EBUSY if timeout exceeded134*/135static inline int136READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout)137{138uint32_t val;139140val = nvchan_rd32(chan, chan->user_get);141142/* reset counter as long as GET is still advancing, this is143* to avoid misdetecting a GPU lockup if the GPU happens to144* just be processing an operation that takes a long time145*/146if (val != *prev_get) {147*prev_get = val;148*timeout = 0;149}150151if ((++*timeout & 0xff) == 0) {152DRM_UDELAY(1);153if (*timeout > 100000)154return -EBUSY;155}156157if (val < chan->pushbuf_base ||158val > chan->pushbuf_base + (chan->dma.max << 2))159return -EINVAL;160161return (val - chan->pushbuf_base) >> 2;162}163164void165nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo,166int delta, int length)167{168struct nouveau_bo *pb = chan->pushbuf_bo;169uint64_t offset = bo->bo.offset + delta;170int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base;171172BUG_ON(chan->dma.ib_free < 1);173nouveau_bo_wr32(pb, ip++, lower_32_bits(offset));174nouveau_bo_wr32(pb, ip++, upper_32_bits(offset) | length << 8);175176chan->dma.ib_put = (chan->dma.ib_put + 1) & chan->dma.ib_max;177178DRM_MEMORYBARRIER();179/* Flush writes. */180nouveau_bo_rd32(pb, 0);181182nvchan_wr32(chan, 0x8c, chan->dma.ib_put);183chan->dma.ib_free--;184}185186static int187nv50_dma_push_wait(struct nouveau_channel *chan, int count)188{189uint32_t cnt = 0, prev_get = 0;190191while (chan->dma.ib_free < count) {192uint32_t get = nvchan_rd32(chan, 0x88);193if (get != prev_get) {194prev_get = get;195cnt = 0;196}197198if ((++cnt & 0xff) == 0) {199DRM_UDELAY(1);200if (cnt > 100000)201return -EBUSY;202}203204chan->dma.ib_free = get - chan->dma.ib_put;205if (chan->dma.ib_free <= 0)206chan->dma.ib_free += chan->dma.ib_max;207}208209return 0;210}211212static int213nv50_dma_wait(struct nouveau_channel *chan, int slots, int count)214{215uint32_t cnt = 0, prev_get = 0;216int ret;217218ret = nv50_dma_push_wait(chan, slots + 1);219if (unlikely(ret))220return ret;221222while (chan->dma.free < count) {223int get = READ_GET(chan, &prev_get, &cnt);224if (unlikely(get < 0)) {225if (get == -EINVAL)226continue;227228return get;229}230231if (get <= chan->dma.cur) {232chan->dma.free = chan->dma.max - chan->dma.cur;233if (chan->dma.free >= count)234break;235236FIRE_RING(chan);237do {238get = READ_GET(chan, &prev_get, &cnt);239if (unlikely(get < 0)) {240if (get == -EINVAL)241continue;242return get;243}244} while (get == 0);245chan->dma.cur = 0;246chan->dma.put = 0;247}248249chan->dma.free = get - chan->dma.cur - 1;250}251252return 0;253}254255int256nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size)257{258uint32_t prev_get = 0, cnt = 0;259int get;260261if (chan->dma.ib_max)262return nv50_dma_wait(chan, slots, size);263264while (chan->dma.free < size) {265get = READ_GET(chan, &prev_get, &cnt);266if (unlikely(get == -EBUSY))267return -EBUSY;268269/* loop until we have a usable GET pointer. the value270* we read from the GPU may be outside the main ring if271* PFIFO is processing a buffer called from the main ring,272* discard these values until something sensible is seen.273*274* the other case we discard GET is while the GPU is fetching275* from the SKIPS area, so the code below doesn't have to deal276* with some fun corner cases.277*/278if (unlikely(get == -EINVAL) || get < NOUVEAU_DMA_SKIPS)279continue;280281if (get <= chan->dma.cur) {282/* engine is fetching behind us, or is completely283* idle (GET == PUT) so we have free space up until284* the end of the push buffer285*286* we can only hit that path once per call due to287* looping back to the beginning of the push buffer,288* we'll hit the fetching-ahead-of-us path from that289* point on.290*291* the *one* exception to that rule is if we read292* GET==PUT, in which case the below conditional will293* always succeed and break us out of the wait loop.294*/295chan->dma.free = chan->dma.max - chan->dma.cur;296if (chan->dma.free >= size)297break;298299/* not enough space left at the end of the push buffer,300* instruct the GPU to jump back to the start right301* after processing the currently pending commands.302*/303OUT_RING(chan, chan->pushbuf_base | 0x20000000);304305/* wait for GET to depart from the skips area.306* prevents writing GET==PUT and causing a race307* condition that causes us to think the GPU is308* idle when it's not.309*/310do {311get = READ_GET(chan, &prev_get, &cnt);312if (unlikely(get == -EBUSY))313return -EBUSY;314if (unlikely(get == -EINVAL))315continue;316} while (get <= NOUVEAU_DMA_SKIPS);317WRITE_PUT(NOUVEAU_DMA_SKIPS);318319/* we're now submitting commands at the start of320* the push buffer.321*/322chan->dma.cur =323chan->dma.put = NOUVEAU_DMA_SKIPS;324}325326/* engine fetching ahead of us, we have space up until the327* current GET pointer. the "- 1" is to ensure there's328* space left to emit a jump back to the beginning of the329* push buffer if we require it. we can never get GET == PUT330* here, so this is safe.331*/332chan->dma.free = get - chan->dma.cur - 1;333}334335return 0;336}337338339340