/* * Copyright 2007 Nouveau Project * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include "nouveau_drmif.h" #include "nouveau_dma.h" static inline uint32_t READ_GET(struct nouveau_channel_priv *nvchan) { return *nvchan->get; } static inline void WRITE_PUT(struct nouveau_channel_priv *nvchan, uint32_t val) { uint32_t put = ((val << 2) + nvchan->dma->base); volatile int dum; NOUVEAU_DMA_BARRIER; dum = nvchan->pushbuf[0]; dum = READ_GET(nvchan); *nvchan->put = put; nvchan->dma->put = val; #ifdef NOUVEAU_DMA_TRACE printf("WRITE_PUT %d/0x%08x\n", nvchan->drm.channel, put); #endif NOUVEAU_DMA_BARRIER; } static inline int LOCAL_GET(struct nouveau_dma_priv *dma, uint32_t *val) { uint32_t get = *val; if (get >= dma->base && get <= (dma->base + (dma->max << 2))) { *val = (get - dma->base) >> 2; return 1; } return 0; } void nouveau_dma_channel_init(struct nouveau_channel *chan) { struct nouveau_channel_priv *nvchan = nouveau_channel(chan); int i; nvchan->dma = &nvchan->struct_dma; nvchan->dma->base = nvchan->drm.put_base; nvchan->dma->cur = nvchan->dma->put = 0; nvchan->dma->max = (nvchan->drm.cmdbuf_size >> 2) - 2; nvchan->dma->free = nvchan->dma->max - nvchan->dma->cur; RING_SPACE_CH(chan, RING_SKIPS); for (i = 0; i < RING_SKIPS; i++) OUT_RING_CH(chan, 0); } #define CHECK_TIMEOUT() do { \ if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT) \ return - EBUSY; \ } while(0) int nouveau_dma_wait(struct nouveau_channel *chan, unsigned size) { struct nouveau_channel_priv *nvchan = nouveau_channel(chan); struct nouveau_dma_priv *dma = nvchan->dma; uint32_t get, t_start; FIRE_RING_CH(chan); t_start = NOUVEAU_TIME_MSEC(); while (dma->free < size) { CHECK_TIMEOUT(); get = READ_GET(nvchan); if (!LOCAL_GET(dma, &get)) continue; if (dma->put >= get) { dma->free = dma->max - dma->cur; if (dma->free < size) { #ifdef NOUVEAU_DMA_DEBUG dma->push_free = 1; #endif OUT_RING_CH(chan, 0x20000000 | dma->base); if (get <= RING_SKIPS) { /*corner case - will be idle*/ if (dma->put <= RING_SKIPS) WRITE_PUT(nvchan, RING_SKIPS + 1); do { CHECK_TIMEOUT(); get = READ_GET(nvchan); if (!LOCAL_GET(dma, &get)) get = 0; } while (get <= RING_SKIPS); } WRITE_PUT(nvchan, RING_SKIPS); dma->cur = dma->put = RING_SKIPS; dma->free = get - (RING_SKIPS + 1); } } else { dma->free = get - dma->cur - 1; } } return 0; } #ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF static void nouveau_dma_parse_pushbuf(struct nouveau_channel *chan, int get, int put) { struct nouveau_channel_priv *nvchan = nouveau_channel(chan); unsigned mthd_count = 0; while (get != put) { uint32_t gpuget = (get << 2) + nvchan->drm.put_base; uint32_t data; if (get < 0 || get >= nvchan->drm.cmdbuf_size) assert(0); data = nvchan->pushbuf[get++]; if (mthd_count) { printf("0x%08x 0x%08x\n", gpuget, data); mthd_count--; continue; } switch (data & 0x60000000) { case 0x00000000: mthd_count = (data >> 18) & 0x7ff; printf("0x%08x 0x%08x MTHD " "Sc %d Mthd 0x%04x Size %d\n", gpuget, data, (data>>13) & 7, data & 0x1ffc, mthd_count); break; case 0x20000000: get = (data & 0x1ffffffc) >> 2; printf("0x%08x 0x%08x JUMP 0x%08x\n", gpuget, data, data & 0x1ffffffc); continue; case 0x40000000: mthd_count = (data >> 18) & 0x7ff; printf("0x%08x 0x%08x NINC " "Sc %d Mthd 0x%04x Size %d\n", gpuget, data, (data>>13) & 7, data & 0x1ffc, mthd_count); break; case 0x60000000: /* DMA_OPCODE_CALL apparently, doesn't seem to work on * my NV40 at least.. */ /* fall-through */ default: printf("DMA_PUSHER 0x%08x 0x%08x\n", gpuget, data); assert(0); } } } #endif void nouveau_dma_kickoff(struct nouveau_channel *chan) { struct nouveau_channel_priv *nvchan = nouveau_channel(chan); struct nouveau_dma_priv *dma = nvchan->dma; if (dma->cur == dma->put) return; #ifdef NOUVEAU_DMA_DEBUG if (dma->push_free) { printf("Packet incomplete: %d left\n", dma->push_free); return; } #endif #ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF nouveau_dma_parse_pushbuf(chan, dma->put, dma->cur); #endif WRITE_PUT(nvchan, dma->cur); }