LUT updates
[platform/upstream/libdrm.git] / shared-core / nouveau_dma.c
1 /*
2  * Copyright (C) 2007 Ben Skeggs.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial
15  * portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  */
26
27 #include "drmP.h"
28 #include "drm.h"
29 #include "nouveau_drv.h"
30 #include "nouveau_dma.h"
31
32 int
33 nouveau_dma_channel_init(struct drm_device *dev)
34 {
35         struct drm_nouveau_private *dev_priv = dev->dev_private;
36         struct nouveau_drm_channel *dchan = &dev_priv->channel;
37         struct nouveau_gpuobj *gpuobj = NULL;
38         struct mem_block *pushbuf;
39         int grclass, ret, i;
40
41         DRM_DEBUG("\n");
42
43         pushbuf = nouveau_mem_alloc(dev, 0, 0x8000,
44                                     NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED,
45                                     (struct drm_file *)-2);
46         if (!pushbuf) {
47                 DRM_ERROR("Failed to allocate DMA push buffer\n");
48                 return -ENOMEM;
49         }
50
51         /* Allocate channel */
52         ret = nouveau_fifo_alloc(dev, &dchan->chan, (struct drm_file *)-2,
53                                  pushbuf, NvDmaFB, NvDmaTT);
54         if (ret) {
55                 DRM_ERROR("Error allocating GPU channel: %d\n", ret);
56                 return ret;
57         }
58         DRM_DEBUG("Using FIFO channel %d\n", dchan->chan->id);
59
60         /* Map push buffer */
61         drm_core_ioremap(dchan->chan->pushbuf_mem->map, dev);
62         if (!dchan->chan->pushbuf_mem->map->handle) {
63                 DRM_ERROR("Failed to ioremap push buffer\n");
64                 return -EINVAL;
65         }
66         dchan->pushbuf = (void*)dchan->chan->pushbuf_mem->map->handle;
67
68         /* Initialise DMA vars */
69         dchan->max  = (dchan->chan->pushbuf_mem->size >> 2) - 2;
70         dchan->put  = dchan->chan->pushbuf_base >> 2;
71         dchan->cur  = dchan->put;
72         dchan->free = dchan->max - dchan->cur;
73
74         /* Insert NOPS for NOUVEAU_DMA_SKIPS */
75         dchan->free -= NOUVEAU_DMA_SKIPS;
76         dchan->push_free = NOUVEAU_DMA_SKIPS;
77         for (i=0; i < NOUVEAU_DMA_SKIPS; i++)
78                 OUT_RING(0);
79
80         /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier */
81         if ((ret = nouveau_notifier_alloc(dchan->chan, NvNotify0, 1,
82                                           &dchan->notify0_offset))) {
83                 DRM_ERROR("Error allocating NvNotify0: %d\n", ret);
84                 return ret;
85         }
86
87         /* We use NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
88         if (dev_priv->card_type < NV_50) grclass = NV_MEMORY_TO_MEMORY_FORMAT;
89         else                             grclass = NV50_MEMORY_TO_MEMORY_FORMAT;
90         if ((ret = nouveau_gpuobj_gr_new(dchan->chan, grclass, &gpuobj))) {
91                 DRM_ERROR("Error creating NvM2MF: %d\n", ret);
92                 return ret;
93         }
94
95         if ((ret = nouveau_gpuobj_ref_add(dev, dchan->chan, NvM2MF,
96                                           gpuobj, NULL))) {
97                 DRM_ERROR("Error referencing NvM2MF: %d\n", ret);
98                 return ret;
99         }
100         dchan->m2mf_dma_source = NvDmaFB;
101         dchan->m2mf_dma_destin = NvDmaFB;
102
103         BEGIN_RING(NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
104         OUT_RING  (NvM2MF);
105         BEGIN_RING(NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_SET_DMA_NOTIFY, 1);
106         OUT_RING  (NvNotify0);
107         BEGIN_RING(NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_SET_DMA_SOURCE, 2);
108         OUT_RING  (dchan->m2mf_dma_source);
109         OUT_RING  (dchan->m2mf_dma_destin);
110         FIRE_RING();
111
112         return 0;
113 }
114
115 void
116 nouveau_dma_channel_takedown(struct drm_device *dev)
117 {
118         struct drm_nouveau_private *dev_priv = dev->dev_private;
119         struct nouveau_drm_channel *dchan = &dev_priv->channel;
120
121         DRM_DEBUG("\n");
122
123         if (dchan->chan) {
124                 nouveau_fifo_free(dchan->chan);
125                 dchan->chan = NULL;
126         }
127 }
128
129 #define READ_GET() ((NV_READ(dchan->chan->get) -                               \
130                     dchan->chan->pushbuf_base) >> 2)
131 #define WRITE_PUT(val) do {                                                    \
132         NV_WRITE(dchan->chan->put,                                             \
133                  ((val) << 2) + dchan->chan->pushbuf_base);                    \
134 } while(0)
135
136 int
137 nouveau_dma_wait(struct drm_device *dev, int size)
138 {
139         struct drm_nouveau_private *dev_priv = dev->dev_private;
140         struct nouveau_drm_channel *dchan = &dev_priv->channel;
141         uint32_t get;
142
143         while (dchan->free < size) {
144                 get = READ_GET();
145
146                 if (dchan->put >= get) {
147                         dchan->free = dchan->max - dchan->cur;
148
149                         if (dchan->free < size) {
150                                 dchan->push_free = 1;
151                                 OUT_RING(0x20000000|dchan->chan->pushbuf_base);
152                                 if (get <= NOUVEAU_DMA_SKIPS) {
153                                         /*corner case - will be idle*/
154                                         if (dchan->put <= NOUVEAU_DMA_SKIPS)
155                                                 WRITE_PUT(NOUVEAU_DMA_SKIPS + 1);
156
157                                         do {
158                                                 get = READ_GET();
159                                         } while (get <= NOUVEAU_DMA_SKIPS);
160                                 }
161
162                                 WRITE_PUT(NOUVEAU_DMA_SKIPS);
163                                 dchan->cur  = dchan->put = NOUVEAU_DMA_SKIPS;
164                                 dchan->free = get - (NOUVEAU_DMA_SKIPS + 1);
165                         }
166                 } else {
167                         dchan->free = get - dchan->cur - 1;
168                 }
169         }
170
171         return 0;
172 }