blob: 94baec692b57713fe6f246d3a296edefafe40c7b [file] [log] [blame]
Dave Airlie22f579c2005-06-28 22:48:56 +10001/* via_dma.c -- DMA support for the VIA Unichrome/Pro
Dave Airlieb5e89ed2005-09-25 14:28:13 +10002 *
Dave Airlie22f579c2005-06-28 22:48:56 +10003 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A.
7 * All Rights Reserved.
Dave Airlieb5e89ed2005-09-25 14:28:13 +10008 *
Dave Airlie22f579c2005-06-28 22:48:56 +10009 * Copyright 2004 The Unichrome project.
10 * All Rights Reserved.
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sub license,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice (including the
20 * next paragraph) shall be included in all copies or substantial portions
21 * of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
Dave Airlieb5e89ed2005-09-25 14:28:13 +100026 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
27 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
28 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
Dave Airlie22f579c2005-06-28 22:48:56 +100029 * USE OR OTHER DEALINGS IN THE SOFTWARE.
30 *
Dave Airlieb5e89ed2005-09-25 14:28:13 +100031 * Authors:
32 * Tungsten Graphics,
33 * Erdi Chen,
Dave Airlie22f579c2005-06-28 22:48:56 +100034 * Thomas Hellstrom.
35 */
36
37#include "drmP.h"
38#include "drm.h"
39#include "via_drm.h"
40#include "via_drv.h"
41#include "via_3d_reg.h"
42
43#define CMDBUF_ALIGNMENT_SIZE (0x100)
44#define CMDBUF_ALIGNMENT_MASK (0x0ff)
45
46/* defines for VIA 3D registers */
47#define VIA_REG_STATUS 0x400
48#define VIA_REG_TRANSET 0x43C
49#define VIA_REG_TRANSPACE 0x440
50
51/* VIA_REG_STATUS(0x400): Engine Status */
52#define VIA_CMD_RGTR_BUSY 0x00000080 /* Command Regulator is busy */
53#define VIA_2D_ENG_BUSY 0x00000001 /* 2D Engine is busy */
54#define VIA_3D_ENG_BUSY 0x00000002 /* 3D Engine is busy */
55#define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */
56
57#define SetReg2DAGP(nReg, nData) { \
58 *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \
59 *((uint32_t *)(vb) + 1) = (nData); \
60 vb = ((uint32_t *)vb) + 2; \
61 dev_priv->dma_low +=8; \
62}
63
Dave Airlieb5e89ed2005-09-25 14:28:13 +100064#define via_flush_write_combine() DRM_MEMORYBARRIER()
Dave Airlie22f579c2005-06-28 22:48:56 +100065
66#define VIA_OUT_RING_QW(w1,w2) \
67 *vb++ = (w1); \
68 *vb++ = (w2); \
Dave Airlieb5e89ed2005-09-25 14:28:13 +100069 dev_priv->dma_low += 8;
Dave Airlie22f579c2005-06-28 22:48:56 +100070
71static void via_cmdbuf_start(drm_via_private_t * dev_priv);
72static void via_cmdbuf_pause(drm_via_private_t * dev_priv);
73static void via_cmdbuf_reset(drm_via_private_t * dev_priv);
74static void via_cmdbuf_rewind(drm_via_private_t * dev_priv);
75static int via_wait_idle(drm_via_private_t * dev_priv);
Dave Airlieb5e89ed2005-09-25 14:28:13 +100076static void via_pad_cache(drm_via_private_t * dev_priv, int qwords);
Dave Airlie22f579c2005-06-28 22:48:56 +100077
78/*
79 * Free space in command buffer.
80 */
81
Dave Airlieb5e89ed2005-09-25 14:28:13 +100082static uint32_t via_cmdbuf_space(drm_via_private_t * dev_priv)
Dave Airlie22f579c2005-06-28 22:48:56 +100083{
Dave Airlieb5e89ed2005-09-25 14:28:13 +100084 uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
Dave Airlie22f579c2005-06-28 22:48:56 +100085 uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base;
Dave Airlieb5e89ed2005-09-25 14:28:13 +100086
87 return ((hw_addr <= dev_priv->dma_low) ?
88 (dev_priv->dma_high + hw_addr - dev_priv->dma_low) :
Dave Airlie22f579c2005-06-28 22:48:56 +100089 (hw_addr - dev_priv->dma_low));
90}
91
92/*
93 * How much does the command regulator lag behind?
94 */
95
Dave Airlieb5e89ed2005-09-25 14:28:13 +100096static uint32_t via_cmdbuf_lag(drm_via_private_t * dev_priv)
Dave Airlie22f579c2005-06-28 22:48:56 +100097{
Dave Airlieb5e89ed2005-09-25 14:28:13 +100098 uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
Dave Airlie22f579c2005-06-28 22:48:56 +100099 uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000100
101 return ((hw_addr <= dev_priv->dma_low) ?
102 (dev_priv->dma_low - hw_addr) :
Dave Airlie22f579c2005-06-28 22:48:56 +1000103 (dev_priv->dma_wrap + dev_priv->dma_low - hw_addr));
104}
105
106/*
107 * Check that the given size fits in the buffer, otherwise wait.
108 */
109
110static inline int
111via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size)
112{
113 uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
114 uint32_t cur_addr, hw_addr, next_addr;
115 volatile uint32_t *hw_addr_ptr;
116 uint32_t count;
117 hw_addr_ptr = dev_priv->hw_addr_ptr;
118 cur_addr = dev_priv->dma_low;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000119 next_addr = cur_addr + size + 512 * 1024;
Dave Airlie22f579c2005-06-28 22:48:56 +1000120 count = 1000000;
121 do {
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000122 hw_addr = *hw_addr_ptr - agp_base;
Dave Airlie22f579c2005-06-28 22:48:56 +1000123 if (count-- == 0) {
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000124 DRM_ERROR
125 ("via_cmdbuf_wait timed out hw %x cur_addr %x next_addr %x\n",
126 hw_addr, cur_addr, next_addr);
Dave Airlie22f579c2005-06-28 22:48:56 +1000127 return -1;
128 }
129 } while ((cur_addr < hw_addr) && (next_addr >= hw_addr));
130 return 0;
131}
132
Dave Airlie22f579c2005-06-28 22:48:56 +1000133/*
134 * Checks whether buffer head has reach the end. Rewind the ring buffer
135 * when necessary.
136 *
137 * Returns virtual pointer to ring buffer.
138 */
139
140static inline uint32_t *via_check_dma(drm_via_private_t * dev_priv,
141 unsigned int size)
142{
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000143 if ((dev_priv->dma_low + size + 4 * CMDBUF_ALIGNMENT_SIZE) >
144 dev_priv->dma_high) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000145 via_cmdbuf_rewind(dev_priv);
146 }
147 if (via_cmdbuf_wait(dev_priv, size) != 0) {
148 return NULL;
149 }
150
151 return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low);
152}
153
Dave Airlie84b1fd12007-07-11 15:53:27 +1000154int via_dma_cleanup(struct drm_device * dev)
Dave Airlie22f579c2005-06-28 22:48:56 +1000155{
156 if (dev->dev_private) {
157 drm_via_private_t *dev_priv =
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000158 (drm_via_private_t *) dev->dev_private;
Dave Airlie22f579c2005-06-28 22:48:56 +1000159
160 if (dev_priv->ring.virtual_start) {
161 via_cmdbuf_reset(dev_priv);
162
163 drm_core_ioremapfree(&dev_priv->ring.map, dev);
164 dev_priv->ring.virtual_start = NULL;
165 }
166
167 }
168
169 return 0;
170}
171
Dave Airlie84b1fd12007-07-11 15:53:27 +1000172static int via_initialize(struct drm_device * dev,
Dave Airlie22f579c2005-06-28 22:48:56 +1000173 drm_via_private_t * dev_priv,
174 drm_via_dma_init_t * init)
175{
176 if (!dev_priv || !dev_priv->mmio) {
177 DRM_ERROR("via_dma_init called before via_map_init\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000178 return -EFAULT;
Dave Airlie22f579c2005-06-28 22:48:56 +1000179 }
180
181 if (dev_priv->ring.virtual_start != NULL) {
Márton Németh3e684ea2008-01-24 15:58:57 +1000182 DRM_ERROR("called again without calling cleanup\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000183 return -EFAULT;
Dave Airlie22f579c2005-06-28 22:48:56 +1000184 }
185
186 if (!dev->agp || !dev->agp->base) {
Márton Németh3e684ea2008-01-24 15:58:57 +1000187 DRM_ERROR("called with no agp memory available\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000188 return -EFAULT;
Dave Airlie22f579c2005-06-28 22:48:56 +1000189 }
190
Thomas Hellstrom756db732007-02-08 12:57:40 +1100191 if (dev_priv->chipset == VIA_DX9_0) {
192 DRM_ERROR("AGP DMA is not supported on this chip\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000193 return -EINVAL;
Thomas Hellstrom756db732007-02-08 12:57:40 +1100194 }
195
Dave Airlie22f579c2005-06-28 22:48:56 +1000196 dev_priv->ring.map.offset = dev->agp->base + init->offset;
197 dev_priv->ring.map.size = init->size;
198 dev_priv->ring.map.type = 0;
199 dev_priv->ring.map.flags = 0;
200 dev_priv->ring.map.mtrr = 0;
201
202 drm_core_ioremap(&dev_priv->ring.map, dev);
203
204 if (dev_priv->ring.map.handle == NULL) {
205 via_dma_cleanup(dev);
206 DRM_ERROR("can not ioremap virtual address for"
207 " ring buffer\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000208 return -ENOMEM;
Dave Airlie22f579c2005-06-28 22:48:56 +1000209 }
210
211 dev_priv->ring.virtual_start = dev_priv->ring.map.handle;
212
213 dev_priv->dma_ptr = dev_priv->ring.virtual_start;
214 dev_priv->dma_low = 0;
215 dev_priv->dma_high = init->size;
216 dev_priv->dma_wrap = init->size;
217 dev_priv->dma_offset = init->offset;
218 dev_priv->last_pause_ptr = NULL;
Dave Airlie92514242005-11-12 21:52:46 +1100219 dev_priv->hw_addr_ptr =
220 (volatile uint32_t *)((char *)dev_priv->mmio->handle +
221 init->reg_pause_addr);
Dave Airlie22f579c2005-06-28 22:48:56 +1000222
223 via_cmdbuf_start(dev_priv);
224
225 return 0;
226}
227
Eric Anholtc153f452007-09-03 12:06:45 +1000228static int via_dma_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
Dave Airlie22f579c2005-06-28 22:48:56 +1000229{
Dave Airlie22f579c2005-06-28 22:48:56 +1000230 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
Eric Anholtc153f452007-09-03 12:06:45 +1000231 drm_via_dma_init_t *init = data;
Dave Airlie22f579c2005-06-28 22:48:56 +1000232 int retcode = 0;
233
Eric Anholtc153f452007-09-03 12:06:45 +1000234 switch (init->func) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000235 case VIA_INIT_DMA:
Dave Airlie92514242005-11-12 21:52:46 +1100236 if (!DRM_SUSER(DRM_CURPROC))
Eric Anholt20caafa2007-08-25 19:22:43 +1000237 retcode = -EPERM;
Dave Airlie22f579c2005-06-28 22:48:56 +1000238 else
Eric Anholtc153f452007-09-03 12:06:45 +1000239 retcode = via_initialize(dev, dev_priv, init);
Dave Airlie22f579c2005-06-28 22:48:56 +1000240 break;
241 case VIA_CLEANUP_DMA:
Dave Airlie92514242005-11-12 21:52:46 +1100242 if (!DRM_SUSER(DRM_CURPROC))
Eric Anholt20caafa2007-08-25 19:22:43 +1000243 retcode = -EPERM;
Dave Airlie22f579c2005-06-28 22:48:56 +1000244 else
245 retcode = via_dma_cleanup(dev);
246 break;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000247 case VIA_DMA_INITIALIZED:
248 retcode = (dev_priv->ring.virtual_start != NULL) ?
Eric Anholt20caafa2007-08-25 19:22:43 +1000249 0 : -EFAULT;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000250 break;
Dave Airlie22f579c2005-06-28 22:48:56 +1000251 default:
Eric Anholt20caafa2007-08-25 19:22:43 +1000252 retcode = -EINVAL;
Dave Airlie22f579c2005-06-28 22:48:56 +1000253 break;
254 }
255
256 return retcode;
257}
258
Dave Airlie84b1fd12007-07-11 15:53:27 +1000259static int via_dispatch_cmdbuffer(struct drm_device * dev, drm_via_cmdbuffer_t * cmd)
Dave Airlie22f579c2005-06-28 22:48:56 +1000260{
261 drm_via_private_t *dev_priv;
262 uint32_t *vb;
263 int ret;
264
265 dev_priv = (drm_via_private_t *) dev->dev_private;
266
267 if (dev_priv->ring.virtual_start == NULL) {
Márton Németh3e684ea2008-01-24 15:58:57 +1000268 DRM_ERROR("called without initializing AGP ring buffer.\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000269 return -EFAULT;
Dave Airlie22f579c2005-06-28 22:48:56 +1000270 }
271
272 if (cmd->size > VIA_PCI_BUF_SIZE) {
Eric Anholt20caafa2007-08-25 19:22:43 +1000273 return -ENOMEM;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000274 }
Dave Airlie22f579c2005-06-28 22:48:56 +1000275
276 if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size))
Eric Anholt20caafa2007-08-25 19:22:43 +1000277 return -EFAULT;
Dave Airlie22f579c2005-06-28 22:48:56 +1000278
279 /*
280 * Running this function on AGP memory is dead slow. Therefore
281 * we run it on a temporary cacheable system memory buffer and
282 * copy it to AGP memory when ready.
283 */
284
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000285 if ((ret =
286 via_verify_command_stream((uint32_t *) dev_priv->pci_buf,
287 cmd->size, dev, 1))) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000288 return ret;
289 }
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000290
Dave Airlie22f579c2005-06-28 22:48:56 +1000291 vb = via_check_dma(dev_priv, (cmd->size < 0x100) ? 0x102 : cmd->size);
292 if (vb == NULL) {
Eric Anholt20caafa2007-08-25 19:22:43 +1000293 return -EAGAIN;
Dave Airlie22f579c2005-06-28 22:48:56 +1000294 }
295
296 memcpy(vb, dev_priv->pci_buf, cmd->size);
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000297
Dave Airlie22f579c2005-06-28 22:48:56 +1000298 dev_priv->dma_low += cmd->size;
299
300 /*
301 * Small submissions somehow stalls the CPU. (AGP cache effects?)
302 * pad to greater size.
303 */
304
305 if (cmd->size < 0x100)
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000306 via_pad_cache(dev_priv, (0x100 - cmd->size) >> 3);
Dave Airlie22f579c2005-06-28 22:48:56 +1000307 via_cmdbuf_pause(dev_priv);
308
309 return 0;
310}
311
Dave Airlie84b1fd12007-07-11 15:53:27 +1000312int via_driver_dma_quiescent(struct drm_device * dev)
Dave Airlie22f579c2005-06-28 22:48:56 +1000313{
314 drm_via_private_t *dev_priv = dev->dev_private;
315
316 if (!via_wait_idle(dev_priv)) {
Eric Anholt20caafa2007-08-25 19:22:43 +1000317 return -EBUSY;
Dave Airlie22f579c2005-06-28 22:48:56 +1000318 }
319 return 0;
320}
321
Eric Anholtc153f452007-09-03 12:06:45 +1000322static int via_flush_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
Dave Airlie22f579c2005-06-28 22:48:56 +1000323{
Dave Airlie22f579c2005-06-28 22:48:56 +1000324
Eric Anholt6c340ea2007-08-25 20:23:09 +1000325 LOCK_TEST_WITH_RETURN(dev, file_priv);
Dave Airlie22f579c2005-06-28 22:48:56 +1000326
327 return via_driver_dma_quiescent(dev);
328}
329
Eric Anholtc153f452007-09-03 12:06:45 +1000330static int via_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv)
Dave Airlie22f579c2005-06-28 22:48:56 +1000331{
Eric Anholtc153f452007-09-03 12:06:45 +1000332 drm_via_cmdbuffer_t *cmdbuf = data;
Dave Airlie22f579c2005-06-28 22:48:56 +1000333 int ret;
334
Eric Anholt6c340ea2007-08-25 20:23:09 +1000335 LOCK_TEST_WITH_RETURN(dev, file_priv);
Dave Airlie22f579c2005-06-28 22:48:56 +1000336
Márton Németh3e684ea2008-01-24 15:58:57 +1000337 DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size);
Dave Airlie22f579c2005-06-28 22:48:56 +1000338
Eric Anholtc153f452007-09-03 12:06:45 +1000339 ret = via_dispatch_cmdbuffer(dev, cmdbuf);
Dave Airlie22f579c2005-06-28 22:48:56 +1000340 if (ret) {
341 return ret;
342 }
343
344 return 0;
345}
346
Dave Airlie84b1fd12007-07-11 15:53:27 +1000347static int via_dispatch_pci_cmdbuffer(struct drm_device * dev,
Dave Airlie22f579c2005-06-28 22:48:56 +1000348 drm_via_cmdbuffer_t * cmd)
349{
350 drm_via_private_t *dev_priv = dev->dev_private;
351 int ret;
352
353 if (cmd->size > VIA_PCI_BUF_SIZE) {
Eric Anholt20caafa2007-08-25 19:22:43 +1000354 return -ENOMEM;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000355 }
Dave Airlie22f579c2005-06-28 22:48:56 +1000356 if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size))
Eric Anholt20caafa2007-08-25 19:22:43 +1000357 return -EFAULT;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000358
359 if ((ret =
360 via_verify_command_stream((uint32_t *) dev_priv->pci_buf,
361 cmd->size, dev, 0))) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000362 return ret;
363 }
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000364
365 ret =
366 via_parse_command_stream(dev, (const uint32_t *)dev_priv->pci_buf,
367 cmd->size);
Dave Airlie22f579c2005-06-28 22:48:56 +1000368 return ret;
369}
370
Eric Anholtc153f452007-09-03 12:06:45 +1000371static int via_pci_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv)
Dave Airlie22f579c2005-06-28 22:48:56 +1000372{
Eric Anholtc153f452007-09-03 12:06:45 +1000373 drm_via_cmdbuffer_t *cmdbuf = data;
Dave Airlie22f579c2005-06-28 22:48:56 +1000374 int ret;
375
Eric Anholt6c340ea2007-08-25 20:23:09 +1000376 LOCK_TEST_WITH_RETURN(dev, file_priv);
Dave Airlie22f579c2005-06-28 22:48:56 +1000377
Márton Németh3e684ea2008-01-24 15:58:57 +1000378 DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size);
Dave Airlie22f579c2005-06-28 22:48:56 +1000379
Eric Anholtc153f452007-09-03 12:06:45 +1000380 ret = via_dispatch_pci_cmdbuffer(dev, cmdbuf);
Dave Airlie22f579c2005-06-28 22:48:56 +1000381 if (ret) {
382 return ret;
383 }
384
385 return 0;
386}
387
Dave Airlie22f579c2005-06-28 22:48:56 +1000388static inline uint32_t *via_align_buffer(drm_via_private_t * dev_priv,
389 uint32_t * vb, int qw_count)
390{
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000391 for (; qw_count > 0; --qw_count) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000392 VIA_OUT_RING_QW(HC_DUMMY, HC_DUMMY);
393 }
394 return vb;
395}
396
Dave Airlie22f579c2005-06-28 22:48:56 +1000397/*
Joe Perches8dfba4d2008-02-03 17:11:42 +0200398 * This function is used internally by ring buffer management code.
Dave Airlie22f579c2005-06-28 22:48:56 +1000399 *
400 * Returns virtual pointer to ring buffer.
401 */
402static inline uint32_t *via_get_dma(drm_via_private_t * dev_priv)
403{
404 return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low);
405}
406
407/*
408 * Hooks a segment of data into the tail of the ring-buffer by
409 * modifying the pause address stored in the buffer itself. If
410 * the regulator has already paused, restart it.
411 */
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000412static int via_hook_segment(drm_via_private_t * dev_priv,
Dave Airlie22f579c2005-06-28 22:48:56 +1000413 uint32_t pause_addr_hi, uint32_t pause_addr_lo,
414 int no_pci_fire)
415{
416 int paused, count;
417 volatile uint32_t *paused_at = dev_priv->last_pause_ptr;
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000418 uint32_t reader,ptr;
Dave Airlie22f579c2005-06-28 22:48:56 +1000419
420 paused = 0;
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000421 via_flush_write_combine();
Thomas Hellstromef68d292007-05-08 15:48:39 +1000422 (void) *(volatile uint32_t *)(via_get_dma(dev_priv) -1);
423 *paused_at = pause_addr_lo;
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000424 via_flush_write_combine();
Thomas Hellstromef68d292007-05-08 15:48:39 +1000425 (void) *paused_at;
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000426 reader = *(dev_priv->hw_addr_ptr);
427 ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) +
428 dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4;
429 dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1;
Dave Airlie22f579c2005-06-28 22:48:56 +1000430
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000431 if ((ptr - reader) <= dev_priv->dma_diff ) {
432 count = 10000000;
433 while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--);
Dave Airlie22f579c2005-06-28 22:48:56 +1000434 }
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000435
Dave Airlie22f579c2005-06-28 22:48:56 +1000436 if (paused && !no_pci_fire) {
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000437 reader = *(dev_priv->hw_addr_ptr);
438 if ((ptr - reader) == dev_priv->dma_diff) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000439
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000440 /*
441 * There is a concern that these writes may stall the PCI bus
442 * if the GPU is not idle. However, idling the GPU first
443 * doesn't make a difference.
444 */
Dave Airlie22f579c2005-06-28 22:48:56 +1000445
Dave Airlie22f579c2005-06-28 22:48:56 +1000446 VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16));
447 VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi);
448 VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo);
Thomas Hellstrom76f62552007-01-08 21:03:23 +1100449 VIA_READ(VIA_REG_TRANSPACE);
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000450 }
Dave Airlie22f579c2005-06-28 22:48:56 +1000451 }
452 return paused;
453}
454
Dave Airlie22f579c2005-06-28 22:48:56 +1000455static int via_wait_idle(drm_via_private_t * dev_priv)
456{
457 int count = 10000000;
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000458
459 while (!(VIA_READ(VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && count--);
460
Dave Airlie22f579c2005-06-28 22:48:56 +1000461 while (count-- && (VIA_READ(VIA_REG_STATUS) &
462 (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY |
463 VIA_3D_ENG_BUSY))) ;
464 return count;
465}
466
467static uint32_t *via_align_cmd(drm_via_private_t * dev_priv, uint32_t cmd_type,
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000468 uint32_t addr, uint32_t * cmd_addr_hi,
469 uint32_t * cmd_addr_lo, int skip_wait)
Dave Airlie22f579c2005-06-28 22:48:56 +1000470{
471 uint32_t agp_base;
472 uint32_t cmd_addr, addr_lo, addr_hi;
473 uint32_t *vb;
474 uint32_t qw_pad_count;
475
476 if (!skip_wait)
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000477 via_cmdbuf_wait(dev_priv, 2 * CMDBUF_ALIGNMENT_SIZE);
Dave Airlie22f579c2005-06-28 22:48:56 +1000478
479 vb = via_get_dma(dev_priv);
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000480 VIA_OUT_RING_QW(HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) |
481 (VIA_REG_TRANSPACE >> 2), HC_ParaType_PreCR << 16);
Dave Airlie22f579c2005-06-28 22:48:56 +1000482 agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
483 qw_pad_count = (CMDBUF_ALIGNMENT_SIZE >> 3) -
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000484 ((dev_priv->dma_low & CMDBUF_ALIGNMENT_MASK) >> 3);
Dave Airlie22f579c2005-06-28 22:48:56 +1000485
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000486 cmd_addr = (addr) ? addr :
487 agp_base + dev_priv->dma_low - 8 + (qw_pad_count << 3);
Dave Airlie22f579c2005-06-28 22:48:56 +1000488 addr_lo = ((HC_SubA_HAGPBpL << 24) | (cmd_type & HC_HAGPBpID_MASK) |
489 (cmd_addr & HC_HAGPBpL_MASK));
490 addr_hi = ((HC_SubA_HAGPBpH << 24) | (cmd_addr >> 24));
491
492 vb = via_align_buffer(dev_priv, vb, qw_pad_count - 1);
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000493 VIA_OUT_RING_QW(*cmd_addr_hi = addr_hi, *cmd_addr_lo = addr_lo);
Dave Airlie22f579c2005-06-28 22:48:56 +1000494 return vb;
495}
496
Dave Airlie22f579c2005-06-28 22:48:56 +1000497static void via_cmdbuf_start(drm_via_private_t * dev_priv)
498{
499 uint32_t pause_addr_lo, pause_addr_hi;
500 uint32_t start_addr, start_addr_lo;
501 uint32_t end_addr, end_addr_lo;
502 uint32_t command;
503 uint32_t agp_base;
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000504 uint32_t ptr;
505 uint32_t reader;
506 int count;
Dave Airlie22f579c2005-06-28 22:48:56 +1000507
Dave Airlie22f579c2005-06-28 22:48:56 +1000508 dev_priv->dma_low = 0;
509
510 agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
511 start_addr = agp_base;
512 end_addr = agp_base + dev_priv->dma_high;
513
514 start_addr_lo = ((HC_SubA_HAGPBstL << 24) | (start_addr & 0xFFFFFF));
515 end_addr_lo = ((HC_SubA_HAGPBendL << 24) | (end_addr & 0xFFFFFF));
516 command = ((HC_SubA_HAGPCMNT << 24) | (start_addr >> 24) |
517 ((end_addr & 0xff000000) >> 16));
518
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000519 dev_priv->last_pause_ptr =
520 via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0,
521 &pause_addr_hi, &pause_addr_lo, 1) - 1;
Dave Airlie22f579c2005-06-28 22:48:56 +1000522
523 via_flush_write_combine();
Thomas Hellstromef68d292007-05-08 15:48:39 +1000524 (void) *(volatile uint32_t *)dev_priv->last_pause_ptr;
Dave Airlie22f579c2005-06-28 22:48:56 +1000525
526 VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16));
527 VIA_WRITE(VIA_REG_TRANSPACE, command);
528 VIA_WRITE(VIA_REG_TRANSPACE, start_addr_lo);
529 VIA_WRITE(VIA_REG_TRANSPACE, end_addr_lo);
530
531 VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi);
532 VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo);
Thomas Hellstrom76f62552007-01-08 21:03:23 +1100533 DRM_WRITEMEMORYBARRIER();
Dave Airlie22f579c2005-06-28 22:48:56 +1000534 VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK);
Thomas Hellstrom76f62552007-01-08 21:03:23 +1100535 VIA_READ(VIA_REG_TRANSPACE);
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000536
537 dev_priv->dma_diff = 0;
538
539 count = 10000000;
540 while (!(VIA_READ(0x41c) & 0x80000000) && count--);
541
542 reader = *(dev_priv->hw_addr_ptr);
543 ptr = ((volatile char *)dev_priv->last_pause_ptr - dev_priv->dma_ptr) +
544 dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4;
545
546 /*
547 * This is the difference between where we tell the
548 * command reader to pause and where it actually pauses.
549 * This differs between hw implementation so we need to
550 * detect it.
551 */
552
553 dev_priv->dma_diff = ptr - reader;
Dave Airlie22f579c2005-06-28 22:48:56 +1000554}
555
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000556static void via_pad_cache(drm_via_private_t * dev_priv, int qwords)
Dave Airlie22f579c2005-06-28 22:48:56 +1000557{
558 uint32_t *vb;
559
560 via_cmdbuf_wait(dev_priv, qwords + 2);
561 vb = via_get_dma(dev_priv);
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000562 VIA_OUT_RING_QW(HC_HEADER2, HC_ParaType_NotTex << 16);
563 via_align_buffer(dev_priv, vb, qwords);
Dave Airlie22f579c2005-06-28 22:48:56 +1000564}
565
566static inline void via_dummy_bitblt(drm_via_private_t * dev_priv)
567{
568 uint32_t *vb = via_get_dma(dev_priv);
569 SetReg2DAGP(0x0C, (0 | (0 << 16)));
570 SetReg2DAGP(0x10, 0 | (0 << 16));
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000571 SetReg2DAGP(0x0, 0x1 | 0x2000 | 0xAA000000);
Dave Airlie22f579c2005-06-28 22:48:56 +1000572}
573
Dave Airlie22f579c2005-06-28 22:48:56 +1000574static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
575{
576 uint32_t agp_base;
577 uint32_t pause_addr_lo, pause_addr_hi;
578 uint32_t jump_addr_lo, jump_addr_hi;
579 volatile uint32_t *last_pause_ptr;
Dave Airlie22f579c2005-06-28 22:48:56 +1000580
581 agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000582 via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi,
Dave Airlie22f579c2005-06-28 22:48:56 +1000583 &jump_addr_lo, 0);
Dave Airlie22f579c2005-06-28 22:48:56 +1000584
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000585 dev_priv->dma_wrap = dev_priv->dma_low;
Dave Airlie22f579c2005-06-28 22:48:56 +1000586
587 /*
588 * Wrap command buffer to the beginning.
589 */
590
591 dev_priv->dma_low = 0;
592 if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) {
593 DRM_ERROR("via_cmdbuf_jump failed\n");
594 }
595
596 via_dummy_bitblt(dev_priv);
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000597 via_dummy_bitblt(dev_priv);
Dave Airlie22f579c2005-06-28 22:48:56 +1000598
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000599 last_pause_ptr =
600 via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
601 &pause_addr_lo, 0) - 1;
602 via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
Dave Airlie22f579c2005-06-28 22:48:56 +1000603 &pause_addr_lo, 0);
604
605 *last_pause_ptr = pause_addr_lo;
Dave Airlie22f579c2005-06-28 22:48:56 +1000606
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000607 via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0);
Dave Airlie22f579c2005-06-28 22:48:56 +1000608}
609
Thomas Hellstroma0a6dd02007-05-08 15:47:41 +1000610
Dave Airlie22f579c2005-06-28 22:48:56 +1000611static void via_cmdbuf_rewind(drm_via_private_t * dev_priv)
612{
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000613 via_cmdbuf_jump(dev_priv);
Dave Airlie22f579c2005-06-28 22:48:56 +1000614}
615
616static void via_cmdbuf_flush(drm_via_private_t * dev_priv, uint32_t cmd_type)
617{
618 uint32_t pause_addr_lo, pause_addr_hi;
619
620 via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, 0);
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000621 via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0);
Dave Airlie22f579c2005-06-28 22:48:56 +1000622}
623
Dave Airlie22f579c2005-06-28 22:48:56 +1000624static void via_cmdbuf_pause(drm_via_private_t * dev_priv)
625{
626 via_cmdbuf_flush(dev_priv, HC_HAGPBpID_PAUSE);
627}
628
629static void via_cmdbuf_reset(drm_via_private_t * dev_priv)
630{
631 via_cmdbuf_flush(dev_priv, HC_HAGPBpID_STOP);
632 via_wait_idle(dev_priv);
633}
634
635/*
636 * User interface to the space and lag functions.
637 */
638
Eric Anholtc153f452007-09-03 12:06:45 +1000639static int via_cmdbuf_size(struct drm_device *dev, void *data, struct drm_file *file_priv)
Dave Airlie22f579c2005-06-28 22:48:56 +1000640{
Eric Anholtc153f452007-09-03 12:06:45 +1000641 drm_via_cmdbuf_size_t *d_siz = data;
Dave Airlie22f579c2005-06-28 22:48:56 +1000642 int ret = 0;
643 uint32_t tmp_size, count;
644 drm_via_private_t *dev_priv;
645
Márton Németh3e684ea2008-01-24 15:58:57 +1000646 DRM_DEBUG("\n");
Eric Anholt6c340ea2007-08-25 20:23:09 +1000647 LOCK_TEST_WITH_RETURN(dev, file_priv);
Dave Airlie22f579c2005-06-28 22:48:56 +1000648
649 dev_priv = (drm_via_private_t *) dev->dev_private;
650
651 if (dev_priv->ring.virtual_start == NULL) {
Márton Németh3e684ea2008-01-24 15:58:57 +1000652 DRM_ERROR("called without initializing AGP ring buffer.\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000653 return -EFAULT;
Dave Airlie22f579c2005-06-28 22:48:56 +1000654 }
655
Dave Airlie22f579c2005-06-28 22:48:56 +1000656 count = 1000000;
Eric Anholtc153f452007-09-03 12:06:45 +1000657 tmp_size = d_siz->size;
658 switch (d_siz->func) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000659 case VIA_CMDBUF_SPACE:
Eric Anholtc153f452007-09-03 12:06:45 +1000660 while (((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz->size)
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000661 && count--) {
Eric Anholtc153f452007-09-03 12:06:45 +1000662 if (!d_siz->wait) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000663 break;
664 }
665 }
666 if (!count) {
667 DRM_ERROR("VIA_CMDBUF_SPACE timed out.\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000668 ret = -EAGAIN;
Dave Airlie22f579c2005-06-28 22:48:56 +1000669 }
670 break;
671 case VIA_CMDBUF_LAG:
Eric Anholtc153f452007-09-03 12:06:45 +1000672 while (((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz->size)
Dave Airlieb5e89ed2005-09-25 14:28:13 +1000673 && count--) {
Eric Anholtc153f452007-09-03 12:06:45 +1000674 if (!d_siz->wait) {
Dave Airlie22f579c2005-06-28 22:48:56 +1000675 break;
676 }
677 }
678 if (!count) {
679 DRM_ERROR("VIA_CMDBUF_LAG timed out.\n");
Eric Anholt20caafa2007-08-25 19:22:43 +1000680 ret = -EAGAIN;
Dave Airlie22f579c2005-06-28 22:48:56 +1000681 }
682 break;
683 default:
Eric Anholt20caafa2007-08-25 19:22:43 +1000684 ret = -EFAULT;
Dave Airlie22f579c2005-06-28 22:48:56 +1000685 }
Eric Anholtc153f452007-09-03 12:06:45 +1000686 d_siz->size = tmp_size;
Dave Airlie22f579c2005-06-28 22:48:56 +1000687
Dave Airlie22f579c2005-06-28 22:48:56 +1000688 return ret;
689}
Dave Airlie92514242005-11-12 21:52:46 +1100690
Eric Anholtc153f452007-09-03 12:06:45 +1000691struct drm_ioctl_desc via_ioctls[] = {
692 DRM_IOCTL_DEF(DRM_VIA_ALLOCMEM, via_mem_alloc, DRM_AUTH),
693 DRM_IOCTL_DEF(DRM_VIA_FREEMEM, via_mem_free, DRM_AUTH),
694 DRM_IOCTL_DEF(DRM_VIA_AGP_INIT, via_agp_init, DRM_AUTH|DRM_MASTER),
695 DRM_IOCTL_DEF(DRM_VIA_FB_INIT, via_fb_init, DRM_AUTH|DRM_MASTER),
696 DRM_IOCTL_DEF(DRM_VIA_MAP_INIT, via_map_init, DRM_AUTH|DRM_MASTER),
697 DRM_IOCTL_DEF(DRM_VIA_DEC_FUTEX, via_decoder_futex, DRM_AUTH),
698 DRM_IOCTL_DEF(DRM_VIA_DMA_INIT, via_dma_init, DRM_AUTH),
699 DRM_IOCTL_DEF(DRM_VIA_CMDBUFFER, via_cmdbuffer, DRM_AUTH),
700 DRM_IOCTL_DEF(DRM_VIA_FLUSH, via_flush_ioctl, DRM_AUTH),
701 DRM_IOCTL_DEF(DRM_VIA_PCICMD, via_pci_cmdbuffer, DRM_AUTH),
702 DRM_IOCTL_DEF(DRM_VIA_CMDBUF_SIZE, via_cmdbuf_size, DRM_AUTH),
703 DRM_IOCTL_DEF(DRM_VIA_WAIT_IRQ, via_wait_irq, DRM_AUTH),
704 DRM_IOCTL_DEF(DRM_VIA_DMA_BLIT, via_dma_blit, DRM_AUTH),
705 DRM_IOCTL_DEF(DRM_VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH)
Dave Airlie92514242005-11-12 21:52:46 +1100706};
707
708int via_max_ioctl = DRM_ARRAY_SIZE(via_ioctls);