From c0e09200dc0813972442e550a5905a132768e56c Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 29 May 2008 10:09:59 +1000 Subject: drm: reorganise drm tree to be more future proof. With the coming of kernel based modesetting and the memory manager stuff, the everything in one directory approach was getting very ugly and starting to be unmanageable. This restructures the drm along the lines of other kernel components. It creates a drivers/gpu/drm directory and moves the hw drivers into subdirectores. It moves the includes into an include/drm, and sets up the unifdef for the userspace headers we should be exporting. Signed-off-by: Dave Airlie --- drivers/gpu/drm/r128/Makefile | 10 + drivers/gpu/drm/r128/r128_cce.c | 935 +++++++++++++++++++++ drivers/gpu/drm/r128/r128_drv.c | 103 +++ drivers/gpu/drm/r128/r128_drv.h | 522 ++++++++++++ drivers/gpu/drm/r128/r128_ioc32.c | 221 +++++ drivers/gpu/drm/r128/r128_irq.c | 101 +++ drivers/gpu/drm/r128/r128_state.c | 1681 +++++++++++++++++++++++++++++++++++++ 7 files changed, 3573 insertions(+) create mode 100644 drivers/gpu/drm/r128/Makefile create mode 100644 drivers/gpu/drm/r128/r128_cce.c create mode 100644 drivers/gpu/drm/r128/r128_drv.c create mode 100644 drivers/gpu/drm/r128/r128_drv.h create mode 100644 drivers/gpu/drm/r128/r128_ioc32.c create mode 100644 drivers/gpu/drm/r128/r128_irq.c create mode 100644 drivers/gpu/drm/r128/r128_state.c (limited to 'drivers/gpu/drm/r128') diff --git a/drivers/gpu/drm/r128/Makefile b/drivers/gpu/drm/r128/Makefile new file mode 100644 index 00000000000..1cc72ae3a88 --- /dev/null +++ b/drivers/gpu/drm/r128/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm +r128-y := r128_drv.o r128_cce.o r128_state.o r128_irq.o + +r128-$(CONFIG_COMPAT) += r128_ioc32.o + +obj-$(CONFIG_DRM_R128) += r128.o diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c new file mode 100644 index 00000000000..c31afbde62e --- /dev/null +++ b/drivers/gpu/drm/r128/r128_cce.c @@ -0,0 +1,935 @@ +/* r128_cce.c -- ATI Rage 128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + */ +/* + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Gareth Hughes + */ + +#include "drmP.h" +#include "drm.h" +#include "r128_drm.h" +#include "r128_drv.h" + +#define R128_FIFO_DEBUG 0 + +/* CCE microcode (from ATI) */ +static u32 r128_cce_microcode[] = { + 0, 276838400, 0, 268449792, 2, 142, 2, 145, 0, 1076765731, 0, + 1617039951, 0, 774592877, 0, 1987540286, 0, 2307490946U, 0, + 599558925, 0, 589505315, 0, 596487092, 0, 589505315, 1, + 11544576, 1, 206848, 1, 311296, 1, 198656, 2, 912273422, 11, + 262144, 0, 0, 1, 33559837, 1, 7438, 1, 14809, 1, 6615, 12, 28, + 1, 6614, 12, 28, 2, 23, 11, 18874368, 0, 16790922, 1, 409600, 9, + 30, 1, 147854772, 16, 420483072, 3, 8192, 0, 10240, 1, 198656, + 1, 15630, 1, 51200, 10, 34858, 9, 42, 1, 33559823, 2, 10276, 1, + 15717, 1, 15718, 2, 43, 1, 15936948, 1, 570480831, 1, 14715071, + 12, 322123831, 1, 33953125, 12, 55, 1, 33559908, 1, 15718, 2, + 46, 4, 2099258, 1, 526336, 1, 442623, 4, 4194365, 1, 509952, 1, + 459007, 3, 0, 12, 92, 2, 46, 12, 176, 1, 15734, 1, 206848, 1, + 18432, 1, 133120, 1, 100670734, 1, 149504, 1, 165888, 1, + 15975928, 1, 1048576, 6, 3145806, 1, 15715, 16, 2150645232U, 2, + 268449859, 2, 10307, 12, 176, 1, 15734, 1, 15735, 1, 15630, 1, + 15631, 1, 5253120, 6, 3145810, 16, 2150645232U, 1, 15864, 2, 82, + 1, 343310, 1, 1064207, 2, 3145813, 1, 15728, 1, 7817, 1, 15729, + 3, 15730, 12, 92, 2, 98, 1, 16168, 1, 16167, 1, 16002, 1, 16008, + 1, 15974, 1, 15975, 1, 15990, 1, 15976, 1, 15977, 1, 15980, 0, + 15981, 1, 10240, 1, 5253120, 1, 15720, 1, 198656, 6, 110, 1, + 180224, 1, 103824738, 2, 112, 2, 3145839, 0, 536885440, 1, + 114880, 14, 125, 12, 206975, 1, 33559995, 12, 198784, 0, + 33570236, 1, 15803, 0, 15804, 3, 294912, 1, 294912, 3, 442370, + 1, 11544576, 0, 811612160, 1, 12593152, 1, 11536384, 1, + 14024704, 7, 310382726, 0, 10240, 1, 14796, 1, 14797, 1, 14793, + 1, 14794, 0, 14795, 1, 268679168, 1, 9437184, 1, 268449792, 1, + 198656, 1, 9452827, 1, 1075854602, 1, 1075854603, 1, 557056, 1, + 114880, 14, 159, 12, 198784, 1, 1109409213, 12, 198783, 1, + 1107312059, 12, 198784, 1, 1109409212, 2, 162, 1, 1075854781, 1, + 1073757627, 1, 1075854780, 1, 540672, 1, 10485760, 6, 3145894, + 16, 274741248, 9, 168, 3, 4194304, 3, 4209949, 0, 0, 0, 256, 14, + 174, 1, 114857, 1, 33560007, 12, 176, 0, 10240, 1, 114858, 1, + 33560018, 1, 114857, 3, 33560007, 1, 16008, 1, 114874, 1, + 33560360, 1, 114875, 1, 33560154, 0, 15963, 0, 256, 0, 4096, 1, + 409611, 9, 188, 0, 10240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static int R128_READ_PLL(struct drm_device * dev, int addr) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + + R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f); + return R128_READ(R128_CLOCK_CNTL_DATA); +} + +#if R128_FIFO_DEBUG +static void r128_status(drm_r128_private_t * dev_priv) +{ + printk("GUI_STAT = 0x%08x\n", + (unsigned int)R128_READ(R128_GUI_STAT)); + printk("PM4_STAT = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_STAT)); + printk("PM4_BUFFER_DL_WPTR = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_BUFFER_DL_WPTR)); + printk("PM4_BUFFER_DL_RPTR = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_BUFFER_DL_RPTR)); + printk("PM4_MICRO_CNTL = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_MICRO_CNTL)); + printk("PM4_BUFFER_CNTL = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_BUFFER_CNTL)); +} +#endif + +/* ================================================================ + * Engine, FIFO control + */ + +static int r128_do_pixcache_flush(drm_r128_private_t * dev_priv) +{ + u32 tmp; + int i; + + tmp = R128_READ(R128_PC_NGUI_CTLSTAT) | R128_PC_FLUSH_ALL; + R128_WRITE(R128_PC_NGUI_CTLSTAT, tmp); + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(R128_READ(R128_PC_NGUI_CTLSTAT) & R128_PC_BUSY)) { + return 0; + } + DRM_UDELAY(1); + } + +#if R128_FIFO_DEBUG + DRM_ERROR("failed!\n"); +#endif + return -EBUSY; +} + +static int r128_do_wait_for_fifo(drm_r128_private_t * dev_priv, int entries) +{ + int i; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + int slots = R128_READ(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK; + if (slots >= entries) + return 0; + DRM_UDELAY(1); + } + +#if R128_FIFO_DEBUG + DRM_ERROR("failed!\n"); +#endif + return -EBUSY; +} + +static int r128_do_wait_for_idle(drm_r128_private_t * dev_priv) +{ + int i, ret; + + ret = r128_do_wait_for_fifo(dev_priv, 64); + if (ret) + return ret; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(R128_READ(R128_GUI_STAT) & R128_GUI_ACTIVE)) { + r128_do_pixcache_flush(dev_priv); + return 0; + } + DRM_UDELAY(1); + } + +#if R128_FIFO_DEBUG + DRM_ERROR("failed!\n"); +#endif + return -EBUSY; +} + +/* ================================================================ + * CCE control, initialization + */ + +/* Load the microcode for the CCE */ +static void r128_cce_load_microcode(drm_r128_private_t * dev_priv) +{ + int i; + + DRM_DEBUG("\n"); + + r128_do_wait_for_idle(dev_priv); + + R128_WRITE(R128_PM4_MICROCODE_ADDR, 0); + for (i = 0; i < 256; i++) { + R128_WRITE(R128_PM4_MICROCODE_DATAH, r128_cce_microcode[i * 2]); + R128_WRITE(R128_PM4_MICROCODE_DATAL, + r128_cce_microcode[i * 2 + 1]); + } +} + +/* Flush any pending commands to the CCE. This should only be used just + * prior to a wait for idle, as it informs the engine that the command + * stream is ending. + */ +static void r128_do_cce_flush(drm_r128_private_t * dev_priv) +{ + u32 tmp; + + tmp = R128_READ(R128_PM4_BUFFER_DL_WPTR) | R128_PM4_BUFFER_DL_DONE; + R128_WRITE(R128_PM4_BUFFER_DL_WPTR, tmp); +} + +/* Wait for the CCE to go idle. + */ +int r128_do_cce_idle(drm_r128_private_t * dev_priv) +{ + int i; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (GET_RING_HEAD(dev_priv) == dev_priv->ring.tail) { + int pm4stat = R128_READ(R128_PM4_STAT); + if (((pm4stat & R128_PM4_FIFOCNT_MASK) >= + dev_priv->cce_fifo_size) && + !(pm4stat & (R128_PM4_BUSY | + R128_PM4_GUI_ACTIVE))) { + return r128_do_pixcache_flush(dev_priv); + } + } + DRM_UDELAY(1); + } + +#if R128_FIFO_DEBUG + DRM_ERROR("failed!\n"); + r128_status(dev_priv); +#endif + return -EBUSY; +} + +/* Start the Concurrent Command Engine. + */ +static void r128_do_cce_start(drm_r128_private_t * dev_priv) +{ + r128_do_wait_for_idle(dev_priv); + + R128_WRITE(R128_PM4_BUFFER_CNTL, + dev_priv->cce_mode | dev_priv->ring.size_l2qw + | R128_PM4_BUFFER_CNTL_NOUPDATE); + R128_READ(R128_PM4_BUFFER_ADDR); /* as per the sample code */ + R128_WRITE(R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN); + + dev_priv->cce_running = 1; +} + +/* Reset the Concurrent Command Engine. This will not flush any pending + * commands, so you must wait for the CCE command stream to complete + * before calling this routine. + */ +static void r128_do_cce_reset(drm_r128_private_t * dev_priv) +{ + R128_WRITE(R128_PM4_BUFFER_DL_WPTR, 0); + R128_WRITE(R128_PM4_BUFFER_DL_RPTR, 0); + dev_priv->ring.tail = 0; +} + +/* Stop the Concurrent Command Engine. This will not flush any pending + * commands, so you must flush the command stream and wait for the CCE + * to go idle before calling this routine. + */ +static void r128_do_cce_stop(drm_r128_private_t * dev_priv) +{ + R128_WRITE(R128_PM4_MICRO_CNTL, 0); + R128_WRITE(R128_PM4_BUFFER_CNTL, + R128_PM4_NONPM4 | R128_PM4_BUFFER_CNTL_NOUPDATE); + + dev_priv->cce_running = 0; +} + +/* Reset the engine. This will stop the CCE if it is running. + */ +static int r128_do_engine_reset(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + u32 clock_cntl_index, mclk_cntl, gen_reset_cntl; + + r128_do_pixcache_flush(dev_priv); + + clock_cntl_index = R128_READ(R128_CLOCK_CNTL_INDEX); + mclk_cntl = R128_READ_PLL(dev, R128_MCLK_CNTL); + + R128_WRITE_PLL(R128_MCLK_CNTL, + mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP); + + gen_reset_cntl = R128_READ(R128_GEN_RESET_CNTL); + + /* Taken from the sample code - do not change */ + R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl | R128_SOFT_RESET_GUI); + R128_READ(R128_GEN_RESET_CNTL); + R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl & ~R128_SOFT_RESET_GUI); + R128_READ(R128_GEN_RESET_CNTL); + + R128_WRITE_PLL(R128_MCLK_CNTL, mclk_cntl); + R128_WRITE(R128_CLOCK_CNTL_INDEX, clock_cntl_index); + R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl); + + /* Reset the CCE ring */ + r128_do_cce_reset(dev_priv); + + /* The CCE is no longer running after an engine reset */ + dev_priv->cce_running = 0; + + /* Reset any pending vertex, indirect buffers */ + r128_freelist_reset(dev); + + return 0; +} + +static void r128_cce_init_ring_buffer(struct drm_device * dev, + drm_r128_private_t * dev_priv) +{ + u32 ring_start; + u32 tmp; + + DRM_DEBUG("\n"); + + /* The manual (p. 2) says this address is in "VM space". This + * means it's an offset from the start of AGP space. + */ +#if __OS_HAS_AGP + if (!dev_priv->is_pci) + ring_start = dev_priv->cce_ring->offset - dev->agp->base; + else +#endif + ring_start = dev_priv->cce_ring->offset - + (unsigned long)dev->sg->virtual; + + R128_WRITE(R128_PM4_BUFFER_OFFSET, ring_start | R128_AGP_OFFSET); + + R128_WRITE(R128_PM4_BUFFER_DL_WPTR, 0); + R128_WRITE(R128_PM4_BUFFER_DL_RPTR, 0); + + /* Set watermark control */ + R128_WRITE(R128_PM4_BUFFER_WM_CNTL, + ((R128_WATERMARK_L / 4) << R128_WMA_SHIFT) + | ((R128_WATERMARK_M / 4) << R128_WMB_SHIFT) + | ((R128_WATERMARK_N / 4) << R128_WMC_SHIFT) + | ((R128_WATERMARK_K / 64) << R128_WB_WM_SHIFT)); + + /* Force read. Why? Because it's in the examples... */ + R128_READ(R128_PM4_BUFFER_ADDR); + + /* Turn on bus mastering */ + tmp = R128_READ(R128_BUS_CNTL) & ~R128_BUS_MASTER_DIS; + R128_WRITE(R128_BUS_CNTL, tmp); +} + +static int r128_do_init_cce(struct drm_device * dev, drm_r128_init_t * init) +{ + drm_r128_private_t *dev_priv; + + DRM_DEBUG("\n"); + + dev_priv = drm_alloc(sizeof(drm_r128_private_t), DRM_MEM_DRIVER); + if (dev_priv == NULL) + return -ENOMEM; + + memset(dev_priv, 0, sizeof(drm_r128_private_t)); + + dev_priv->is_pci = init->is_pci; + + if (dev_priv->is_pci && !dev->sg) { + DRM_ERROR("PCI GART memory not allocated!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + + dev_priv->usec_timeout = init->usec_timeout; + if (dev_priv->usec_timeout < 1 || + dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT) { + DRM_DEBUG("TIMEOUT problem!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + + dev_priv->cce_mode = init->cce_mode; + + /* GH: Simple idle check. + */ + atomic_set(&dev_priv->idle_count, 0); + + /* We don't support anything other than bus-mastering ring mode, + * but the ring can be in either AGP or PCI space for the ring + * read pointer. + */ + if ((init->cce_mode != R128_PM4_192BM) && + (init->cce_mode != R128_PM4_128BM_64INDBM) && + (init->cce_mode != R128_PM4_64BM_128INDBM) && + (init->cce_mode != R128_PM4_64BM_64VCBM_64INDBM)) { + DRM_DEBUG("Bad cce_mode!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + + switch (init->cce_mode) { + case R128_PM4_NONPM4: + dev_priv->cce_fifo_size = 0; + break; + case R128_PM4_192PIO: + case R128_PM4_192BM: + dev_priv->cce_fifo_size = 192; + break; + case R128_PM4_128PIO_64INDBM: + case R128_PM4_128BM_64INDBM: + dev_priv->cce_fifo_size = 128; + break; + case R128_PM4_64PIO_128INDBM: + case R128_PM4_64BM_128INDBM: + case R128_PM4_64PIO_64VCBM_64INDBM: + case R128_PM4_64BM_64VCBM_64INDBM: + case R128_PM4_64PIO_64VCPIO_64INDPIO: + dev_priv->cce_fifo_size = 64; + break; + } + + switch (init->fb_bpp) { + case 16: + dev_priv->color_fmt = R128_DATATYPE_RGB565; + break; + case 32: + default: + dev_priv->color_fmt = R128_DATATYPE_ARGB8888; + break; + } + dev_priv->front_offset = init->front_offset; + dev_priv->front_pitch = init->front_pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->back_pitch = init->back_pitch; + + switch (init->depth_bpp) { + case 16: + dev_priv->depth_fmt = R128_DATATYPE_RGB565; + break; + case 24: + case 32: + default: + dev_priv->depth_fmt = R128_DATATYPE_ARGB8888; + break; + } + dev_priv->depth_offset = init->depth_offset; + dev_priv->depth_pitch = init->depth_pitch; + dev_priv->span_offset = init->span_offset; + + dev_priv->front_pitch_offset_c = (((dev_priv->front_pitch / 8) << 21) | + (dev_priv->front_offset >> 5)); + dev_priv->back_pitch_offset_c = (((dev_priv->back_pitch / 8) << 21) | + (dev_priv->back_offset >> 5)); + dev_priv->depth_pitch_offset_c = (((dev_priv->depth_pitch / 8) << 21) | + (dev_priv->depth_offset >> 5) | + R128_DST_TILE); + dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch / 8) << 21) | + (dev_priv->span_offset >> 5)); + + dev_priv->sarea = drm_getsarea(dev); + if (!dev_priv->sarea) { + DRM_ERROR("could not find sarea!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + + dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); + if (!dev_priv->mmio) { + DRM_ERROR("could not find mmio region!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + dev_priv->cce_ring = drm_core_findmap(dev, init->ring_offset); + if (!dev_priv->cce_ring) { + DRM_ERROR("could not find cce ring region!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset); + if (!dev_priv->ring_rptr) { + DRM_ERROR("could not find ring read pointer!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + dev->agp_buffer_token = init->buffers_offset; + dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); + if (!dev->agp_buffer_map) { + DRM_ERROR("could not find dma buffer region!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + + if (!dev_priv->is_pci) { + dev_priv->agp_textures = + drm_core_findmap(dev, init->agp_textures_offset); + if (!dev_priv->agp_textures) { + DRM_ERROR("could not find agp texture region!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -EINVAL; + } + } + + dev_priv->sarea_priv = + (drm_r128_sarea_t *) ((u8 *) dev_priv->sarea->handle + + init->sarea_priv_offset); + +#if __OS_HAS_AGP + if (!dev_priv->is_pci) { + drm_core_ioremap(dev_priv->cce_ring, dev); + drm_core_ioremap(dev_priv->ring_rptr, dev); + drm_core_ioremap(dev->agp_buffer_map, dev); + if (!dev_priv->cce_ring->handle || + !dev_priv->ring_rptr->handle || + !dev->agp_buffer_map->handle) { + DRM_ERROR("Could not ioremap agp regions!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -ENOMEM; + } + } else +#endif + { + dev_priv->cce_ring->handle = (void *)dev_priv->cce_ring->offset; + dev_priv->ring_rptr->handle = + (void *)dev_priv->ring_rptr->offset; + dev->agp_buffer_map->handle = + (void *)dev->agp_buffer_map->offset; + } + +#if __OS_HAS_AGP + if (!dev_priv->is_pci) + dev_priv->cce_buffers_offset = dev->agp->base; + else +#endif + dev_priv->cce_buffers_offset = (unsigned long)dev->sg->virtual; + + dev_priv->ring.start = (u32 *) dev_priv->cce_ring->handle; + dev_priv->ring.end = ((u32 *) dev_priv->cce_ring->handle + + init->ring_size / sizeof(u32)); + dev_priv->ring.size = init->ring_size; + dev_priv->ring.size_l2qw = drm_order(init->ring_size / 8); + + dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1; + + dev_priv->ring.high_mark = 128; + + dev_priv->sarea_priv->last_frame = 0; + R128_WRITE(R128_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame); + + dev_priv->sarea_priv->last_dispatch = 0; + R128_WRITE(R128_LAST_DISPATCH_REG, dev_priv->sarea_priv->last_dispatch); + +#if __OS_HAS_AGP + if (dev_priv->is_pci) { +#endif + dev_priv->gart_info.table_mask = DMA_BIT_MASK(32); + dev_priv->gart_info.gart_table_location = DRM_ATI_GART_MAIN; + dev_priv->gart_info.table_size = R128_PCIGART_TABLE_SIZE; + dev_priv->gart_info.addr = NULL; + dev_priv->gart_info.bus_addr = 0; + dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; + if (!drm_ati_pcigart_init(dev, &dev_priv->gart_info)) { + DRM_ERROR("failed to init PCI GART!\n"); + dev->dev_private = (void *)dev_priv; + r128_do_cleanup_cce(dev); + return -ENOMEM; + } + R128_WRITE(R128_PCI_GART_PAGE, dev_priv->gart_info.bus_addr); +#if __OS_HAS_AGP + } +#endif + + r128_cce_init_ring_buffer(dev, dev_priv); + r128_cce_load_microcode(dev_priv); + + dev->dev_private = (void *)dev_priv; + + r128_do_engine_reset(dev); + + return 0; +} + +int r128_do_cleanup_cce(struct drm_device * dev) +{ + + /* Make sure interrupts are disabled here because the uninstall ioctl + * may not have been called from userspace and after dev_private + * is freed, it's too late. + */ + if (dev->irq_enabled) + drm_irq_uninstall(dev); + + if (dev->dev_private) { + drm_r128_private_t *dev_priv = dev->dev_private; + +#if __OS_HAS_AGP + if (!dev_priv->is_pci) { + if (dev_priv->cce_ring != NULL) + drm_core_ioremapfree(dev_priv->cce_ring, dev); + if (dev_priv->ring_rptr != NULL) + drm_core_ioremapfree(dev_priv->ring_rptr, dev); + if (dev->agp_buffer_map != NULL) { + drm_core_ioremapfree(dev->agp_buffer_map, dev); + dev->agp_buffer_map = NULL; + } + } else +#endif + { + if (dev_priv->gart_info.bus_addr) + if (!drm_ati_pcigart_cleanup(dev, + &dev_priv->gart_info)) + DRM_ERROR + ("failed to cleanup PCI GART!\n"); + } + + drm_free(dev->dev_private, sizeof(drm_r128_private_t), + DRM_MEM_DRIVER); + dev->dev_private = NULL; + } + + return 0; +} + +int r128_cce_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_init_t *init = data; + + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + switch (init->func) { + case R128_INIT_CCE: + return r128_do_init_cce(dev, init); + case R128_CLEANUP_CCE: + return r128_do_cleanup_cce(dev); + } + + return -EINVAL; +} + +int r128_cce_start(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (dev_priv->cce_running || dev_priv->cce_mode == R128_PM4_NONPM4) { + DRM_DEBUG("while CCE running\n"); + return 0; + } + + r128_do_cce_start(dev_priv); + + return 0; +} + +/* Stop the CCE. The engine must have been idled before calling this + * routine. + */ +int r128_cce_stop(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_cce_stop_t *stop = data; + int ret; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + /* Flush any pending CCE commands. This ensures any outstanding + * commands are exectuted by the engine before we turn it off. + */ + if (stop->flush) { + r128_do_cce_flush(dev_priv); + } + + /* If we fail to make the engine go idle, we return an error + * code so that the DRM ioctl wrapper can try again. + */ + if (stop->idle) { + ret = r128_do_cce_idle(dev_priv); + if (ret) + return ret; + } + + /* Finally, we can turn off the CCE. If the engine isn't idle, + * we will get some dropped triangles as they won't be fully + * rendered before the CCE is shut down. + */ + r128_do_cce_stop(dev_priv); + + /* Reset the engine */ + r128_do_engine_reset(dev); + + return 0; +} + +/* Just reset the CCE ring. Called as part of an X Server engine reset. + */ +int r128_cce_reset(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (!dev_priv) { + DRM_DEBUG("called before init done\n"); + return -EINVAL; + } + + r128_do_cce_reset(dev_priv); + + /* The CCE is no longer running after an engine reset */ + dev_priv->cce_running = 0; + + return 0; +} + +int r128_cce_idle(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (dev_priv->cce_running) { + r128_do_cce_flush(dev_priv); + } + + return r128_do_cce_idle(dev_priv); +} + +int r128_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + return r128_do_engine_reset(dev); +} + +int r128_fullscreen(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + return -EINVAL; +} + +/* ================================================================ + * Freelist management + */ +#define R128_BUFFER_USED 0xffffffff +#define R128_BUFFER_FREE 0 + +#if 0 +static int r128_freelist_init(struct drm_device * dev) +{ + struct drm_device_dma *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + struct drm_buf *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_freelist_t *entry; + int i; + + dev_priv->head = drm_alloc(sizeof(drm_r128_freelist_t), DRM_MEM_DRIVER); + if (dev_priv->head == NULL) + return -ENOMEM; + + memset(dev_priv->head, 0, sizeof(drm_r128_freelist_t)); + dev_priv->head->age = R128_BUFFER_USED; + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + + entry = drm_alloc(sizeof(drm_r128_freelist_t), DRM_MEM_DRIVER); + if (!entry) + return -ENOMEM; + + entry->age = R128_BUFFER_FREE; + entry->buf = buf; + entry->prev = dev_priv->head; + entry->next = dev_priv->head->next; + if (!entry->next) + dev_priv->tail = entry; + + buf_priv->discard = 0; + buf_priv->dispatched = 0; + buf_priv->list_entry = entry; + + dev_priv->head->next = entry; + + if (dev_priv->head->next) + dev_priv->head->next->prev = entry; + } + + return 0; + +} +#endif + +static struct drm_buf *r128_freelist_get(struct drm_device * dev) +{ + struct drm_device_dma *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv; + struct drm_buf *buf; + int i, t; + + /* FIXME: Optimize -- use freelist code */ + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if (!buf->file_priv) + return buf; + } + + for (t = 0; t < dev_priv->usec_timeout; t++) { + u32 done_age = R128_READ(R128_LAST_DISPATCH_REG); + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if (buf->pending && buf_priv->age <= done_age) { + /* The buffer has been processed, so it + * can now be used. + */ + buf->pending = 0; + return buf; + } + } + DRM_UDELAY(1); + } + + DRM_DEBUG("returning NULL!\n"); + return NULL; +} + +void r128_freelist_reset(struct drm_device * dev) +{ + struct drm_device_dma *dma = dev->dma; + int i; + + for (i = 0; i < dma->buf_count; i++) { + struct drm_buf *buf = dma->buflist[i]; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + buf_priv->age = 0; + } +} + +/* ================================================================ + * CCE command submission + */ + +int r128_wait_ring(drm_r128_private_t * dev_priv, int n) +{ + drm_r128_ring_buffer_t *ring = &dev_priv->ring; + int i; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + r128_update_ring_snapshot(dev_priv); + if (ring->space >= n) + return 0; + DRM_UDELAY(1); + } + + /* FIXME: This is being ignored... */ + DRM_ERROR("failed!\n"); + return -EBUSY; +} + +static int r128_cce_get_buffers(struct drm_device * dev, + struct drm_file *file_priv, + struct drm_dma * d) +{ + int i; + struct drm_buf *buf; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = r128_freelist_get(dev); + if (!buf) + return -EAGAIN; + + buf->file_priv = file_priv; + + if (DRM_COPY_TO_USER(&d->request_indices[i], &buf->idx, + sizeof(buf->idx))) + return -EFAULT; + if (DRM_COPY_TO_USER(&d->request_sizes[i], &buf->total, + sizeof(buf->total))) + return -EFAULT; + + d->granted_count++; + } + return 0; +} + +int r128_cce_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + int ret = 0; + struct drm_dma *d = data; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + /* Please don't send us buffers. + */ + if (d->send_count != 0) { + DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n", + DRM_CURRENTPID, d->send_count); + return -EINVAL; + } + + /* We'll send you buffers. + */ + if (d->request_count < 0 || d->request_count > dma->buf_count) { + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + DRM_CURRENTPID, d->request_count, dma->buf_count); + return -EINVAL; + } + + d->granted_count = 0; + + if (d->request_count) { + ret = r128_cce_get_buffers(dev, file_priv, d); + } + + return ret; +} diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c new file mode 100644 index 00000000000..6108e7587e1 --- /dev/null +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -0,0 +1,103 @@ +/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*- + * Created: Mon Dec 13 09:47:27 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Rickard E. (Rik) Faith + * Gareth Hughes + */ + +#include "drmP.h" +#include "drm.h" +#include "r128_drm.h" +#include "r128_drv.h" + +#include "drm_pciids.h" + +static struct pci_device_id pciidlist[] = { + r128_PCI_IDS +}; + +static struct drm_driver driver = { + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | + DRIVER_IRQ_VBL, + .dev_priv_size = sizeof(drm_r128_buf_priv_t), + .preclose = r128_driver_preclose, + .lastclose = r128_driver_lastclose, + .vblank_wait = r128_driver_vblank_wait, + .irq_preinstall = r128_driver_irq_preinstall, + .irq_postinstall = r128_driver_irq_postinstall, + .irq_uninstall = r128_driver_irq_uninstall, + .irq_handler = r128_driver_irq_handler, + .reclaim_buffers = drm_core_reclaim_buffers, + .get_map_ofs = drm_core_get_map_ofs, + .get_reg_ofs = drm_core_get_reg_ofs, + .ioctls = r128_ioctls, + .dma_ioctl = r128_cce_buffers, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, +#ifdef CONFIG_COMPAT + .compat_ioctl = r128_compat_ioctl, +#endif + }, + + .pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + }, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static int __init r128_init(void) +{ + driver.num_ioctls = r128_max_ioctl; + return drm_init(&driver); +} + +static void __exit r128_exit(void) +{ + drm_exit(&driver); +} + +module_init(r128_init); +module_exit(r128_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h new file mode 100644 index 00000000000..011105e51ac --- /dev/null +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -0,0 +1,522 @@ +/* r128_drv.h -- Private header for r128 driver -*- linux-c -*- + * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com + */ +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Rickard E. (Rik) Faith + * Kevin E. Martin + * Gareth Hughes + * Michel Dänzer + */ + +#ifndef __R128_DRV_H__ +#define __R128_DRV_H__ + +/* General customization: + */ +#define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." + +#define DRIVER_NAME "r128" +#define DRIVER_DESC "ATI Rage 128" +#define DRIVER_DATE "20030725" + +/* Interface history: + * + * ?? - ?? + * 2.4 - Add support for ycbcr textures (no new ioctls) + * 2.5 - Add FLIP ioctl, disable FULLSCREEN. + */ +#define DRIVER_MAJOR 2 +#define DRIVER_MINOR 5 +#define DRIVER_PATCHLEVEL 0 + +#define GET_RING_HEAD(dev_priv) R128_READ( R128_PM4_BUFFER_DL_RPTR ) + +typedef struct drm_r128_freelist { + unsigned int age; + struct drm_buf *buf; + struct drm_r128_freelist *next; + struct drm_r128_freelist *prev; +} drm_r128_freelist_t; + +typedef struct drm_r128_ring_buffer { + u32 *start; + u32 *end; + int size; + int size_l2qw; + + u32 tail; + u32 tail_mask; + int space; + + int high_mark; +} drm_r128_ring_buffer_t; + +typedef struct drm_r128_private { + drm_r128_ring_buffer_t ring; + drm_r128_sarea_t *sarea_priv; + + int cce_mode; + int cce_fifo_size; + int cce_running; + + drm_r128_freelist_t *head; + drm_r128_freelist_t *tail; + + int usec_timeout; + int is_pci; + unsigned long cce_buffers_offset; + + atomic_t idle_count; + + int page_flipping; + int current_page; + u32 crtc_offset; + u32 crtc_offset_cntl; + + u32 color_fmt; + unsigned int front_offset; + unsigned int front_pitch; + unsigned int back_offset; + unsigned int back_pitch; + + u32 depth_fmt; + unsigned int depth_offset; + unsigned int depth_pitch; + unsigned int span_offset; + + u32 front_pitch_offset_c; + u32 back_pitch_offset_c; + u32 depth_pitch_offset_c; + u32 span_pitch_offset_c; + + drm_local_map_t *sarea; + drm_local_map_t *mmio; + drm_local_map_t *cce_ring; + drm_local_map_t *ring_rptr; + drm_local_map_t *agp_textures; + struct drm_ati_pcigart_info gart_info; +} drm_r128_private_t; + +typedef struct drm_r128_buf_priv { + u32 age; + int prim; + int discard; + int dispatched; + drm_r128_freelist_t *list_entry; +} drm_r128_buf_priv_t; + +extern struct drm_ioctl_desc r128_ioctls[]; +extern int r128_max_ioctl; + + /* r128_cce.c */ +extern int r128_cce_init(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_cce_start(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_cce_stop(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_cce_reset(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_cce_idle(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_fullscreen(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int r128_cce_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv); + +extern void r128_freelist_reset(struct drm_device * dev); + +extern int r128_wait_ring(drm_r128_private_t * dev_priv, int n); + +extern int r128_do_cce_idle(drm_r128_private_t * dev_priv); +extern int r128_do_cleanup_cce(struct drm_device * dev); + +extern int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); + +extern irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS); +extern void r128_driver_irq_preinstall(struct drm_device * dev); +extern void r128_driver_irq_postinstall(struct drm_device * dev); +extern void r128_driver_irq_uninstall(struct drm_device * dev); +extern void r128_driver_lastclose(struct drm_device * dev); +extern void r128_driver_preclose(struct drm_device * dev, + struct drm_file *file_priv); + +extern long r128_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); + +/* Register definitions, register access macros and drmAddMap constants + * for Rage 128 kernel driver. + */ + +#define R128_AUX_SC_CNTL 0x1660 +# define R128_AUX1_SC_EN (1 << 0) +# define R128_AUX1_SC_MODE_OR (0 << 1) +# define R128_AUX1_SC_MODE_NAND (1 << 1) +# define R128_AUX2_SC_EN (1 << 2) +# define R128_AUX2_SC_MODE_OR (0 << 3) +# define R128_AUX2_SC_MODE_NAND (1 << 3) +# define R128_AUX3_SC_EN (1 << 4) +# define R128_AUX3_SC_MODE_OR (0 << 5) +# define R128_AUX3_SC_MODE_NAND (1 << 5) +#define R128_AUX1_SC_LEFT 0x1664 +#define R128_AUX1_SC_RIGHT 0x1668 +#define R128_AUX1_SC_TOP 0x166c +#define R128_AUX1_SC_BOTTOM 0x1670 +#define R128_AUX2_SC_LEFT 0x1674 +#define R128_AUX2_SC_RIGHT 0x1678 +#define R128_AUX2_SC_TOP 0x167c +#define R128_AUX2_SC_BOTTOM 0x1680 +#define R128_AUX3_SC_LEFT 0x1684 +#define R128_AUX3_SC_RIGHT 0x1688 +#define R128_AUX3_SC_TOP 0x168c +#define R128_AUX3_SC_BOTTOM 0x1690 + +#define R128_BRUSH_DATA0 0x1480 +#define R128_BUS_CNTL 0x0030 +# define R128_BUS_MASTER_DIS (1 << 6) + +#define R128_CLOCK_CNTL_INDEX 0x0008 +#define R128_CLOCK_CNTL_DATA 0x000c +# define R128_PLL_WR_EN (1 << 7) +#define R128_CONSTANT_COLOR_C 0x1d34 +#define R128_CRTC_OFFSET 0x0224 +#define R128_CRTC_OFFSET_CNTL 0x0228 +# define R128_CRTC_OFFSET_FLIP_CNTL (1 << 16) + +#define R128_DP_GUI_MASTER_CNTL 0x146c +# define R128_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) +# define R128_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) +# define R128_GMC_BRUSH_SOLID_COLOR (13 << 4) +# define R128_GMC_BRUSH_NONE (15 << 4) +# define R128_GMC_DST_16BPP (4 << 8) +# define R128_GMC_DST_24BPP (5 << 8) +# define R128_GMC_DST_32BPP (6 << 8) +# define R128_GMC_DST_DATATYPE_SHIFT 8 +# define R128_GMC_SRC_DATATYPE_COLOR (3 << 12) +# define R128_DP_SRC_SOURCE_MEMORY (2 << 24) +# define R128_DP_SRC_SOURCE_HOST_DATA (3 << 24) +# define R128_GMC_CLR_CMP_CNTL_DIS (1 << 28) +# define R128_GMC_AUX_CLIP_DIS (1 << 29) +# define R128_GMC_WR_MSK_DIS (1 << 30) +# define R128_ROP3_S 0x00cc0000 +# define R128_ROP3_P 0x00f00000 +#define R128_DP_WRITE_MASK 0x16cc +#define R128_DST_PITCH_OFFSET_C 0x1c80 +# define R128_DST_TILE (1 << 31) + +#define R128_GEN_INT_CNTL 0x0040 +# define R128_CRTC_VBLANK_INT_EN (1 << 0) +#define R128_GEN_INT_STATUS 0x0044 +# define R128_CRTC_VBLANK_INT (1 << 0) +# define R128_CRTC_VBLANK_INT_AK (1 << 0) +#define R128_GEN_RESET_CNTL 0x00f0 +# define R128_SOFT_RESET_GUI (1 << 0) + +#define R128_GUI_SCRATCH_REG0 0x15e0 +#define R128_GUI_SCRATCH_REG1 0x15e4 +#define R128_GUI_SCRATCH_REG2 0x15e8 +#define R128_GUI_SCRATCH_REG3 0x15ec +#define R128_GUI_SCRATCH_REG4 0x15f0 +#define R128_GUI_SCRATCH_REG5 0x15f4 + +#define R128_GUI_STAT 0x1740 +# define R128_GUI_FIFOCNT_MASK 0x0fff +# define R128_GUI_ACTIVE (1 << 31) + +#define R128_MCLK_CNTL 0x000f +# define R128_FORCE_GCP (1 << 16) +# define R128_FORCE_PIPE3D_CP (1 << 17) +# define R128_FORCE_RCP (1 << 18) + +#define R128_PC_GUI_CTLSTAT 0x1748 +#define R128_PC_NGUI_CTLSTAT 0x0184 +# define R128_PC_FLUSH_GUI (3 << 0) +# define R128_PC_RI_GUI (1 << 2) +# define R128_PC_FLUSH_ALL 0x00ff +# define R128_PC_BUSY (1 << 31) + +#define R128_PCI_GART_PAGE 0x017c +#define R128_PRIM_TEX_CNTL_C 0x1cb0 + +#define R128_SCALE_3D_CNTL 0x1a00 +#define R128_SEC_TEX_CNTL_C 0x1d00 +#define R128_SEC_TEXTURE_BORDER_COLOR_C 0x1d3c +#define R128_SETUP_CNTL 0x1bc4 +#define R128_STEN_REF_MASK_C 0x1d40 + +#define R128_TEX_CNTL_C 0x1c9c +# define R128_TEX_CACHE_FLUSH (1 << 23) + +#define R128_WAIT_UNTIL 0x1720 +# define R128_EVENT_CRTC_OFFSET (1 << 0) +#define R128_WINDOW_XY_OFFSET 0x1bcc + +/* CCE registers + */ +#define R128_PM4_BUFFER_OFFSET 0x0700 +#define R128_PM4_BUFFER_CNTL 0x0704 +# define R128_PM4_MASK (15 << 28) +# define R128_PM4_NONPM4 (0 << 28) +# define R128_PM4_192PIO (1 << 28) +# define R128_PM4_192BM (2 << 28) +# define R128_PM4_128PIO_64INDBM (3 << 28) +# define R128_PM4_128BM_64INDBM (4 << 28) +# define R128_PM4_64PIO_128INDBM (5 << 28) +# define R128_PM4_64BM_128INDBM (6 << 28) +# define R128_PM4_64PIO_64VCBM_64INDBM (7 << 28) +# define R128_PM4_64BM_64VCBM_64INDBM (8 << 28) +# define R128_PM4_64PIO_64VCPIO_64INDPIO (15 << 28) +# define R128_PM4_BUFFER_CNTL_NOUPDATE (1 << 27) + +#define R128_PM4_BUFFER_WM_CNTL 0x0708 +# define R128_WMA_SHIFT 0 +# define R128_WMB_SHIFT 8 +# define R128_WMC_SHIFT 16 +# define R128_WB_WM_SHIFT 24 + +#define R128_PM4_BUFFER_DL_RPTR_ADDR 0x070c +#define R128_PM4_BUFFER_DL_RPTR 0x0710 +#define R128_PM4_BUFFER_DL_WPTR 0x0714 +# define R128_PM4_BUFFER_DL_DONE (1 << 31) + +#define R128_PM4_VC_FPU_SETUP 0x071c + +#define R128_PM4_IW_INDOFF 0x0738 +#define R128_PM4_IW_INDSIZE 0x073c + +#define R128_PM4_STAT 0x07b8 +# define R128_PM4_FIFOCNT_MASK 0x0fff +# define R128_PM4_BUSY (1 << 16) +# define R128_PM4_GUI_ACTIVE (1 << 31) + +#define R128_PM4_MICROCODE_ADDR 0x07d4 +#define R128_PM4_MICROCODE_RADDR 0x07d8 +#define R128_PM4_MICROCODE_DATAH 0x07dc +#define R128_PM4_MICROCODE_DATAL 0x07e0 + +#define R128_PM4_BUFFER_ADDR 0x07f0 +#define R128_PM4_MICRO_CNTL 0x07fc +# define R128_PM4_MICRO_FREERUN (1 << 30) + +#define R128_PM4_FIFO_DATA_EVEN 0x1000 +#define R128_PM4_FIFO_DATA_ODD 0x1004 + +/* CCE command packets + */ +#define R128_CCE_PACKET0 0x00000000 +#define R128_CCE_PACKET1 0x40000000 +#define R128_CCE_PACKET2 0x80000000 +#define R128_CCE_PACKET3 0xC0000000 +# define R128_CNTL_HOSTDATA_BLT 0x00009400 +# define R128_CNTL_PAINT_MULTI 0x00009A00 +# define R128_CNTL_BITBLT_MULTI 0x00009B00 +# define R128_3D_RNDR_GEN_INDX_PRIM 0x00002300 + +#define R128_CCE_PACKET_MASK 0xC0000000 +#define R128_CCE_PACKET_COUNT_MASK 0x3fff0000 +#define R128_CCE_PACKET0_REG_MASK 0x000007ff +#define R128_CCE_PACKET1_REG0_MASK 0x000007ff +#define R128_CCE_PACKET1_REG1_MASK 0x003ff800 + +#define R128_CCE_VC_CNTL_PRIM_TYPE_NONE 0x00000000 +#define R128_CCE_VC_CNTL_PRIM_TYPE_POINT 0x00000001 +#define R128_CCE_VC_CNTL_PRIM_TYPE_LINE 0x00000002 +#define R128_CCE_VC_CNTL_PRIM_TYPE_POLY_LINE 0x00000003 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_LIST 0x00000004 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_FAN 0x00000005 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_STRIP 0x00000006 +#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 0x00000007 +#define R128_CCE_VC_CNTL_PRIM_WALK_IND 0x00000010 +#define R128_CCE_VC_CNTL_PRIM_WALK_LIST 0x00000020 +#define R128_CCE_VC_CNTL_PRIM_WALK_RING 0x00000030 +#define R128_CCE_VC_CNTL_NUM_SHIFT 16 + +#define R128_DATATYPE_VQ 0 +#define R128_DATATYPE_CI4 1 +#define R128_DATATYPE_CI8 2 +#define R128_DATATYPE_ARGB1555 3 +#define R128_DATATYPE_RGB565 4 +#define R128_DATATYPE_RGB888 5 +#define R128_DATATYPE_ARGB8888 6 +#define R128_DATATYPE_RGB332 7 +#define R128_DATATYPE_Y8 8 +#define R128_DATATYPE_RGB8 9 +#define R128_DATATYPE_CI16 10 +#define R128_DATATYPE_YVYU422 11 +#define R128_DATATYPE_VYUY422 12 +#define R128_DATATYPE_AYUV444 14 +#define R128_DATATYPE_ARGB4444 15 + +/* Constants */ +#define R128_AGP_OFFSET 0x02000000 + +#define R128_WATERMARK_L 16 +#define R128_WATERMARK_M 8 +#define R128_WATERMARK_N 8 +#define R128_WATERMARK_K 128 + +#define R128_MAX_USEC_TIMEOUT 100000 /* 100 ms */ + +#define R128_LAST_FRAME_REG R128_GUI_SCRATCH_REG0 +#define R128_LAST_DISPATCH_REG R128_GUI_SCRATCH_REG1 +#define R128_MAX_VB_AGE 0x7fffffff +#define R128_MAX_VB_VERTS (0xffff) + +#define R128_RING_HIGH_MARK 128 + +#define R128_PERFORMANCE_BOXES 0 + +#define R128_PCIGART_TABLE_SIZE 32768 + +#define R128_READ(reg) DRM_READ32( dev_priv->mmio, (reg) ) +#define R128_WRITE(reg,val) DRM_WRITE32( dev_priv->mmio, (reg), (val) ) +#define R128_READ8(reg) DRM_READ8( dev_priv->mmio, (reg) ) +#define R128_WRITE8(reg,val) DRM_WRITE8( dev_priv->mmio, (reg), (val) ) + +#define R128_WRITE_PLL(addr,val) \ +do { \ + R128_WRITE8(R128_CLOCK_CNTL_INDEX, \ + ((addr) & 0x1f) | R128_PLL_WR_EN); \ + R128_WRITE(R128_CLOCK_CNTL_DATA, (val)); \ +} while (0) + +#define CCE_PACKET0( reg, n ) (R128_CCE_PACKET0 | \ + ((n) << 16) | ((reg) >> 2)) +#define CCE_PACKET1( reg0, reg1 ) (R128_CCE_PACKET1 | \ + (((reg1) >> 2) << 11) | ((reg0) >> 2)) +#define CCE_PACKET2() (R128_CCE_PACKET2) +#define CCE_PACKET3( pkt, n ) (R128_CCE_PACKET3 | \ + (pkt) | ((n) << 16)) + +static __inline__ void r128_update_ring_snapshot(drm_r128_private_t * dev_priv) +{ + drm_r128_ring_buffer_t *ring = &dev_priv->ring; + ring->space = (GET_RING_HEAD(dev_priv) - ring->tail) * sizeof(u32); + if (ring->space <= 0) + ring->space += ring->size; +} + +/* ================================================================ + * Misc helper macros + */ + +#define RING_SPACE_TEST_WITH_RETURN( dev_priv ) \ +do { \ + drm_r128_ring_buffer_t *ring = &dev_priv->ring; int i; \ + if ( ring->space < ring->high_mark ) { \ + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { \ + r128_update_ring_snapshot( dev_priv ); \ + if ( ring->space >= ring->high_mark ) \ + goto __ring_space_done; \ + DRM_UDELAY(1); \ + } \ + DRM_ERROR( "ring space check failed!\n" ); \ + return -EBUSY; \ + } \ + __ring_space_done: \ + ; \ +} while (0) + +#define VB_AGE_TEST_WITH_RETURN( dev_priv ) \ +do { \ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; \ + if ( sarea_priv->last_dispatch >= R128_MAX_VB_AGE ) { \ + int __ret = r128_do_cce_idle( dev_priv ); \ + if ( __ret ) return __ret; \ + sarea_priv->last_dispatch = 0; \ + r128_freelist_reset( dev ); \ + } \ +} while (0) + +#define R128_WAIT_UNTIL_PAGE_FLIPPED() do { \ + OUT_RING( CCE_PACKET0( R128_WAIT_UNTIL, 0 ) ); \ + OUT_RING( R128_EVENT_CRTC_OFFSET ); \ +} while (0) + +/* ================================================================ + * Ring control + */ + +#define R128_VERBOSE 0 + +#define RING_LOCALS \ + int write, _nr; unsigned int tail_mask; volatile u32 *ring; + +#define BEGIN_RING( n ) do { \ + if ( R128_VERBOSE ) { \ + DRM_INFO( "BEGIN_RING( %d )\n", (n)); \ + } \ + if ( dev_priv->ring.space <= (n) * sizeof(u32) ) { \ + COMMIT_RING(); \ + r128_wait_ring( dev_priv, (n) * sizeof(u32) ); \ + } \ + _nr = n; dev_priv->ring.space -= (n) * sizeof(u32); \ + ring = dev_priv->ring.start; \ + write = dev_priv->ring.tail; \ + tail_mask = dev_priv->ring.tail_mask; \ +} while (0) + +/* You can set this to zero if you want. If the card locks up, you'll + * need to keep this set. It works around a bug in early revs of the + * Rage 128 chipset, where the CCE would read 32 dwords past the end of + * the ring buffer before wrapping around. + */ +#define R128_BROKEN_CCE 1 + +#define ADVANCE_RING() do { \ + if ( R128_VERBOSE ) { \ + DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ + write, dev_priv->ring.tail ); \ + } \ + if ( R128_BROKEN_CCE && write < 32 ) { \ + memcpy( dev_priv->ring.end, \ + dev_priv->ring.start, \ + write * sizeof(u32) ); \ + } \ + if (((dev_priv->ring.tail + _nr) & tail_mask) != write) { \ + DRM_ERROR( \ + "ADVANCE_RING(): mismatch: nr: %x write: %x line: %d\n", \ + ((dev_priv->ring.tail + _nr) & tail_mask), \ + write, __LINE__); \ + } else \ + dev_priv->ring.tail = write; \ +} while (0) + +#define COMMIT_RING() do { \ + if ( R128_VERBOSE ) { \ + DRM_INFO( "COMMIT_RING() tail=0x%06x\n", \ + dev_priv->ring.tail ); \ + } \ + DRM_MEMORYBARRIER(); \ + R128_WRITE( R128_PM4_BUFFER_DL_WPTR, dev_priv->ring.tail ); \ + R128_READ( R128_PM4_BUFFER_DL_WPTR ); \ +} while (0) + +#define OUT_RING( x ) do { \ + if ( R128_VERBOSE ) { \ + DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \ + (unsigned int)(x), write ); \ + } \ + ring[write++] = cpu_to_le32( x ); \ + write &= tail_mask; \ +} while (0) + +#endif /* __R128_DRV_H__ */ diff --git a/drivers/gpu/drm/r128/r128_ioc32.c b/drivers/gpu/drm/r128/r128_ioc32.c new file mode 100644 index 00000000000..d3cb676eee8 --- /dev/null +++ b/drivers/gpu/drm/r128/r128_ioc32.c @@ -0,0 +1,221 @@ +/** + * \file r128_ioc32.c + * + * 32-bit ioctl compatibility routines for the R128 DRM. + * + * \author Dave Airlie with code from patches by Egbert Eich + * + * Copyright (C) Paul Mackerras 2005 + * Copyright (C) Egbert Eich 2003,2004 + * Copyright (C) Dave Airlie 2005 + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 AUTHOR 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 "drmP.h" +#include "drm.h" +#include "r128_drm.h" + +typedef struct drm_r128_init32 { + int func; + unsigned int sarea_priv_offset; + int is_pci; + int cce_mode; + int cce_secure; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + unsigned int span_offset; + + unsigned int fb_offset; + unsigned int mmio_offset; + unsigned int ring_offset; + unsigned int ring_rptr_offset; + unsigned int buffers_offset; + unsigned int agp_textures_offset; +} drm_r128_init32_t; + +static int compat_r128_init(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_r128_init32_t init32; + drm_r128_init_t __user *init; + + if (copy_from_user(&init32, (void __user *)arg, sizeof(init32))) + return -EFAULT; + + init = compat_alloc_user_space(sizeof(*init)); + if (!access_ok(VERIFY_WRITE, init, sizeof(*init)) + || __put_user(init32.func, &init->func) + || __put_user(init32.sarea_priv_offset, &init->sarea_priv_offset) + || __put_user(init32.is_pci, &init->is_pci) + || __put_user(init32.cce_mode, &init->cce_mode) + || __put_user(init32.cce_secure, &init->cce_secure) + || __put_user(init32.ring_size, &init->ring_size) + || __put_user(init32.usec_timeout, &init->usec_timeout) + || __put_user(init32.fb_bpp, &init->fb_bpp) + || __put_user(init32.front_offset, &init->front_offset) + || __put_user(init32.front_pitch, &init->front_pitch) + || __put_user(init32.back_offset, &init->back_offset) + || __put_user(init32.back_pitch, &init->back_pitch) + || __put_user(init32.depth_bpp, &init->depth_bpp) + || __put_user(init32.depth_offset, &init->depth_offset) + || __put_user(init32.depth_pitch, &init->depth_pitch) + || __put_user(init32.span_offset, &init->span_offset) + || __put_user(init32.fb_offset, &init->fb_offset) + || __put_user(init32.mmio_offset, &init->mmio_offset) + || __put_user(init32.ring_offset, &init->ring_offset) + || __put_user(init32.ring_rptr_offset, &init->ring_rptr_offset) + || __put_user(init32.buffers_offset, &init->buffers_offset) + || __put_user(init32.agp_textures_offset, + &init->agp_textures_offset)) + return -EFAULT; + + return drm_ioctl(file->f_path.dentry->d_inode, file, + DRM_IOCTL_R128_INIT, (unsigned long)init); +} + +typedef struct drm_r128_depth32 { + int func; + int n; + u32 x; + u32 y; + u32 buffer; + u32 mask; +} drm_r128_depth32_t; + +static int compat_r128_depth(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_r128_depth32_t depth32; + drm_r128_depth_t __user *depth; + + if (copy_from_user(&depth32, (void __user *)arg, sizeof(depth32))) + return -EFAULT; + + depth = compat_alloc_user_space(sizeof(*depth)); + if (!access_ok(VERIFY_WRITE, depth, sizeof(*depth)) + || __put_user(depth32.func, &depth->func) + || __put_user(depth32.n, &depth->n) + || __put_user((int __user *)(unsigned long)depth32.x, &depth->x) + || __put_user((int __user *)(unsigned long)depth32.y, &depth->y) + || __put_user((unsigned int __user *)(unsigned long)depth32.buffer, + &depth->buffer) + || __put_user((unsigned char __user *)(unsigned long)depth32.mask, + &depth->mask)) + return -EFAULT; + + return drm_ioctl(file->f_path.dentry->d_inode, file, + DRM_IOCTL_R128_DEPTH, (unsigned long)depth); + +} + +typedef struct drm_r128_stipple32 { + u32 mask; +} drm_r128_stipple32_t; + +static int compat_r128_stipple(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_r128_stipple32_t stipple32; + drm_r128_stipple_t __user *stipple; + + if (copy_from_user(&stipple32, (void __user *)arg, sizeof(stipple32))) + return -EFAULT; + + stipple = compat_alloc_user_space(sizeof(*stipple)); + if (!access_ok(VERIFY_WRITE, stipple, sizeof(*stipple)) + || __put_user((unsigned int __user *)(unsigned long)stipple32.mask, + &stipple->mask)) + return -EFAULT; + + return drm_ioctl(file->f_path.dentry->d_inode, file, + DRM_IOCTL_R128_STIPPLE, (unsigned long)stipple); +} + +typedef struct drm_r128_getparam32 { + int param; + u32 value; +} drm_r128_getparam32_t; + +static int compat_r128_getparam(struct file *file, unsigned int cmd, + unsigned long arg) +{ + drm_r128_getparam32_t getparam32; + drm_r128_getparam_t __user *getparam; + + if (copy_from_user(&getparam32, (void __user *)arg, sizeof(getparam32))) + return -EFAULT; + + getparam = compat_alloc_user_space(sizeof(*getparam)); + if (!access_ok(VERIFY_WRITE, getparam, sizeof(*getparam)) + || __put_user(getparam32.param, &getparam->param) + || __put_user((void __user *)(unsigned long)getparam32.value, + &getparam->value)) + return -EFAULT; + + return drm_ioctl(file->f_path.dentry->d_inode, file, + DRM_IOCTL_R128_GETPARAM, (unsigned long)getparam); +} + +drm_ioctl_compat_t *r128_compat_ioctls[] = { + [DRM_R128_INIT] = compat_r128_init, + [DRM_R128_DEPTH] = compat_r128_depth, + [DRM_R128_STIPPLE] = compat_r128_stipple, + [DRM_R128_GETPARAM] = compat_r128_getparam, +}; + +/** + * Called whenever a 32-bit process running under a 64-bit kernel + * performs an ioctl on /dev/dri/card. + * + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + */ +long r128_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned int nr = DRM_IOCTL_NR(cmd); + drm_ioctl_compat_t *fn = NULL; + int ret; + + if (nr < DRM_COMMAND_BASE) + return drm_compat_ioctl(filp, cmd, arg); + + if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(r128_compat_ioctls)) + fn = r128_compat_ioctls[nr - DRM_COMMAND_BASE]; + + lock_kernel(); /* XXX for now */ + if (fn != NULL) + ret = (*fn) (filp, cmd, arg); + else + ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg); + unlock_kernel(); + + return ret; +} diff --git a/drivers/gpu/drm/r128/r128_irq.c b/drivers/gpu/drm/r128/r128_irq.c new file mode 100644 index 00000000000..c76fdca7662 --- /dev/null +++ b/drivers/gpu/drm/r128/r128_irq.c @@ -0,0 +1,101 @@ +/* r128_irq.c -- IRQ handling for radeon -*- linux-c -*- */ +/* + * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + * + * The Weather Channel (TM) funded Tungsten Graphics to develop the + * initial release of the Radeon 8500 driver under the XFree86 license. + * This notice must be preserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Keith Whitwell + * Eric Anholt + */ + +#include "drmP.h" +#include "drm.h" +#include "r128_drm.h" +#include "r128_drv.h" + +irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private; + int status; + + status = R128_READ(R128_GEN_INT_STATUS); + + /* VBLANK interrupt */ + if (status & R128_CRTC_VBLANK_INT) { + R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK); + atomic_inc(&dev->vbl_received); + DRM_WAKEUP(&dev->vbl_queue); + drm_vbl_send_signals(dev); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +{ + unsigned int cur_vblank; + int ret = 0; + + /* Assume that the user has missed the current sequence number + * by about a day rather than she wants to wait for years + * using vertical blanks... + */ + DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, + (((cur_vblank = atomic_read(&dev->vbl_received)) + - *sequence) <= (1 << 23))); + + *sequence = cur_vblank; + + return ret; +} + +void r128_driver_irq_preinstall(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private; + + /* Disable *all* interrupts */ + R128_WRITE(R128_GEN_INT_CNTL, 0); + /* Clear vblank bit if it's already high */ + R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK); +} + +void r128_driver_irq_postinstall(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private; + + /* Turn on VBL interrupt */ + R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN); +} + +void r128_driver_irq_uninstall(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private; + if (!dev_priv) + return; + + /* Disable *all* interrupts */ + R128_WRITE(R128_GEN_INT_CNTL, 0); +} diff --git a/drivers/gpu/drm/r128/r128_state.c b/drivers/gpu/drm/r128/r128_state.c new file mode 100644 index 00000000000..51a9afce7b9 --- /dev/null +++ b/drivers/gpu/drm/r128/r128_state.c @@ -0,0 +1,1681 @@ +/* r128_state.c -- State support for r128 -*- linux-c -*- + * Created: Thu Jan 27 02:53:43 2000 by gareth@valinux.com + */ +/* + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. + * + * Authors: + * Gareth Hughes + */ + +#include "drmP.h" +#include "drm.h" +#include "r128_drm.h" +#include "r128_drv.h" + +/* ================================================================ + * CCE hardware state programming functions + */ + +static void r128_emit_clip_rects(drm_r128_private_t * dev_priv, + struct drm_clip_rect * boxes, int count) +{ + u32 aux_sc_cntl = 0x00000000; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING((count < 3 ? count : 3) * 5 + 2); + + if (count >= 1) { + OUT_RING(CCE_PACKET0(R128_AUX1_SC_LEFT, 3)); + OUT_RING(boxes[0].x1); + OUT_RING(boxes[0].x2 - 1); + OUT_RING(boxes[0].y1); + OUT_RING(boxes[0].y2 - 1); + + aux_sc_cntl |= (R128_AUX1_SC_EN | R128_AUX1_SC_MODE_OR); + } + if (count >= 2) { + OUT_RING(CCE_PACKET0(R128_AUX2_SC_LEFT, 3)); + OUT_RING(boxes[1].x1); + OUT_RING(boxes[1].x2 - 1); + OUT_RING(boxes[1].y1); + OUT_RING(boxes[1].y2 - 1); + + aux_sc_cntl |= (R128_AUX2_SC_EN | R128_AUX2_SC_MODE_OR); + } + if (count >= 3) { + OUT_RING(CCE_PACKET0(R128_AUX3_SC_LEFT, 3)); + OUT_RING(boxes[2].x1); + OUT_RING(boxes[2].x2 - 1); + OUT_RING(boxes[2].y1); + OUT_RING(boxes[2].y2 - 1); + + aux_sc_cntl |= (R128_AUX3_SC_EN | R128_AUX3_SC_MODE_OR); + } + + OUT_RING(CCE_PACKET0(R128_AUX_SC_CNTL, 0)); + OUT_RING(aux_sc_cntl); + + ADVANCE_RING(); +} + +static __inline__ void r128_emit_core(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_SCALE_3D_CNTL, 0)); + OUT_RING(ctx->scale_3d_cntl); + + ADVANCE_RING(); +} + +static __inline__ void r128_emit_context(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(13); + + OUT_RING(CCE_PACKET0(R128_DST_PITCH_OFFSET_C, 11)); + OUT_RING(ctx->dst_pitch_offset_c); + OUT_RING(ctx->dp_gui_master_cntl_c); + OUT_RING(ctx->sc_top_left_c); + OUT_RING(ctx->sc_bottom_right_c); + OUT_RING(ctx->z_offset_c); + OUT_RING(ctx->z_pitch_c); + OUT_RING(ctx->z_sten_cntl_c); + OUT_RING(ctx->tex_cntl_c); + OUT_RING(ctx->misc_3d_state_cntl_reg); + OUT_RING(ctx->texture_clr_cmp_clr_c); + OUT_RING(ctx->texture_clr_cmp_msk_c); + OUT_RING(ctx->fog_color_c); + + ADVANCE_RING(); +} + +static __inline__ void r128_emit_setup(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(3); + + OUT_RING(CCE_PACKET1(R128_SETUP_CNTL, R128_PM4_VC_FPU_SETUP)); + OUT_RING(ctx->setup_cntl); + OUT_RING(ctx->pm4_vc_fpu_setup); + + ADVANCE_RING(); +} + +static __inline__ void r128_emit_masks(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(5); + + OUT_RING(CCE_PACKET0(R128_DP_WRITE_MASK, 0)); + OUT_RING(ctx->dp_write_mask); + + OUT_RING(CCE_PACKET0(R128_STEN_REF_MASK_C, 1)); + OUT_RING(ctx->sten_ref_mask_c); + OUT_RING(ctx->plane_3d_mask_c); + + ADVANCE_RING(); +} + +static __inline__ void r128_emit_window(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_WINDOW_XY_OFFSET, 0)); + OUT_RING(ctx->window_xy_offset); + + ADVANCE_RING(); +} + +static __inline__ void r128_emit_tex0(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_context_regs_t *ctx = &sarea_priv->context_state; + drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[0]; + int i; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(7 + R128_MAX_TEXTURE_LEVELS); + + OUT_RING(CCE_PACKET0(R128_PRIM_TEX_CNTL_C, + 2 + R128_MAX_TEXTURE_LEVELS)); + OUT_RING(tex->tex_cntl); + OUT_RING(tex->tex_combine_cntl); + OUT_RING(ctx->tex_size_pitch_c); + for (i = 0; i < R128_MAX_TEXTURE_LEVELS; i++) { + OUT_RING(tex->tex_offset[i]); + } + + OUT_RING(CCE_PACKET0(R128_CONSTANT_COLOR_C, 1)); + OUT_RING(ctx->constant_color_c); + OUT_RING(tex->tex_border_color); + + ADVANCE_RING(); +} + +static __inline__ void r128_emit_tex1(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[1]; + int i; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(5 + R128_MAX_TEXTURE_LEVELS); + + OUT_RING(CCE_PACKET0(R128_SEC_TEX_CNTL_C, 1 + R128_MAX_TEXTURE_LEVELS)); + OUT_RING(tex->tex_cntl); + OUT_RING(tex->tex_combine_cntl); + for (i = 0; i < R128_MAX_TEXTURE_LEVELS; i++) { + OUT_RING(tex->tex_offset[i]); + } + + OUT_RING(CCE_PACKET0(R128_SEC_TEXTURE_BORDER_COLOR_C, 0)); + OUT_RING(tex->tex_border_color); + + ADVANCE_RING(); +} + +static void r128_emit_state(drm_r128_private_t * dev_priv) +{ + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + + DRM_DEBUG("dirty=0x%08x\n", dirty); + + if (dirty & R128_UPLOAD_CORE) { + r128_emit_core(dev_priv); + sarea_priv->dirty &= ~R128_UPLOAD_CORE; + } + + if (dirty & R128_UPLOAD_CONTEXT) { + r128_emit_context(dev_priv); + sarea_priv->dirty &= ~R128_UPLOAD_CONTEXT; + } + + if (dirty & R128_UPLOAD_SETUP) { + r128_emit_setup(dev_priv); + sarea_priv->dirty &= ~R128_UPLOAD_SETUP; + } + + if (dirty & R128_UPLOAD_MASKS) { + r128_emit_masks(dev_priv); + sarea_priv->dirty &= ~R128_UPLOAD_MASKS; + } + + if (dirty & R128_UPLOAD_WINDOW) { + r128_emit_window(dev_priv); + sarea_priv->dirty &= ~R128_UPLOAD_WINDOW; + } + + if (dirty & R128_UPLOAD_TEX0) { + r128_emit_tex0(dev_priv); + sarea_priv->dirty &= ~R128_UPLOAD_TEX0; + } + + if (dirty & R128_UPLOAD_TEX1) { + r128_emit_tex1(dev_priv); + sarea_priv->dirty &= ~R128_UPLOAD_TEX1; + } + + /* Turn off the texture cache flushing */ + sarea_priv->context_state.tex_cntl_c &= ~R128_TEX_CACHE_FLUSH; + + sarea_priv->dirty &= ~R128_REQUIRE_QUIESCENCE; +} + +#if R128_PERFORMANCE_BOXES +/* ================================================================ + * Performance monitoring functions + */ + +static void r128_clear_box(drm_r128_private_t * dev_priv, + int x, int y, int w, int h, int r, int g, int b) +{ + u32 pitch, offset; + u32 fb_bpp, color; + RING_LOCALS; + + switch (dev_priv->fb_bpp) { + case 16: + fb_bpp = R128_GMC_DST_16BPP; + color = (((r & 0xf8) << 8) | + ((g & 0xfc) << 3) | ((b & 0xf8) >> 3)); + break; + case 24: + fb_bpp = R128_GMC_DST_24BPP; + color = ((r << 16) | (g << 8) | b); + break; + case 32: + fb_bpp = R128_GMC_DST_32BPP; + color = (((0xff) << 24) | (r << 16) | (g << 8) | b); + break; + default: + return; + } + + offset = dev_priv->back_offset; + pitch = dev_priv->back_pitch >> 3; + + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + fb_bpp | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | R128_GMC_AUX_CLIP_DIS); + + OUT_RING((pitch << 21) | (offset >> 5)); + OUT_RING(color); + + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); +} + +static void r128_cce_performance_boxes(drm_r128_private_t * dev_priv) +{ + if (atomic_read(&dev_priv->idle_count) == 0) { + r128_clear_box(dev_priv, 64, 4, 8, 8, 0, 255, 0); + } else { + atomic_set(&dev_priv->idle_count, 0); + } +} + +#endif + +/* ================================================================ + * CCE command dispatch functions + */ + +static void r128_print_dirty(const char *msg, unsigned int flags) +{ + DRM_INFO("%s: (0x%x) %s%s%s%s%s%s%s%s%s\n", + msg, + flags, + (flags & R128_UPLOAD_CORE) ? "core, " : "", + (flags & R128_UPLOAD_CONTEXT) ? "context, " : "", + (flags & R128_UPLOAD_SETUP) ? "setup, " : "", + (flags & R128_UPLOAD_TEX0) ? "tex0, " : "", + (flags & R128_UPLOAD_TEX1) ? "tex1, " : "", + (flags & R128_UPLOAD_MASKS) ? "masks, " : "", + (flags & R128_UPLOAD_WINDOW) ? "window, " : "", + (flags & R128_UPLOAD_CLIPRECTS) ? "cliprects, " : "", + (flags & R128_REQUIRE_QUIESCENCE) ? "quiescence, " : ""); +} + +static void r128_cce_dispatch_clear(struct drm_device * dev, + drm_r128_clear_t * clear) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + struct drm_clip_rect *pbox = sarea_priv->boxes; + unsigned int flags = clear->flags; + int i; + RING_LOCALS; + DRM_DEBUG("\n"); + + if (dev_priv->page_flipping && dev_priv->current_page == 1) { + unsigned int tmp = flags; + + flags &= ~(R128_FRONT | R128_BACK); + if (tmp & R128_FRONT) + flags |= R128_BACK; + if (tmp & R128_BACK) + flags |= R128_FRONT; + } + + for (i = 0; i < nbox; i++) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG("dispatch clear %d,%d-%d,%d flags 0x%x\n", + pbox[i].x1, pbox[i].y1, pbox[i].x2, + pbox[i].y2, flags); + + if (flags & (R128_FRONT | R128_BACK)) { + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_DP_WRITE_MASK, 0)); + OUT_RING(clear->color_mask); + + ADVANCE_RING(); + } + + if (flags & R128_FRONT) { + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_AUX_CLIP_DIS); + + OUT_RING(dev_priv->front_pitch_offset_c); + OUT_RING(clear->clear_color); + + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); + } + + if (flags & R128_BACK) { + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_AUX_CLIP_DIS); + + OUT_RING(dev_priv->back_pitch_offset_c); + OUT_RING(clear->clear_color); + + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); + } + + if (flags & R128_DEPTH) { + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + (dev_priv->depth_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_AUX_CLIP_DIS | R128_GMC_WR_MSK_DIS); + + OUT_RING(dev_priv->depth_pitch_offset_c); + OUT_RING(clear->clear_depth); + + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); + } + } +} + +static void r128_cce_dispatch_swap(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + struct drm_clip_rect *pbox = sarea_priv->boxes; + int i; + RING_LOCALS; + DRM_DEBUG("\n"); + +#if R128_PERFORMANCE_BOXES + /* Do some trivial performance monitoring... + */ + r128_cce_performance_boxes(dev_priv); +#endif + + for (i = 0; i < nbox; i++) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + BEGIN_RING(7); + + OUT_RING(CCE_PACKET3(R128_CNTL_BITBLT_MULTI, 5)); + OUT_RING(R128_GMC_SRC_PITCH_OFFSET_CNTL | + R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_NONE | + (dev_priv->color_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_S | + R128_DP_SRC_SOURCE_MEMORY | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_AUX_CLIP_DIS | R128_GMC_WR_MSK_DIS); + + /* Make this work even if front & back are flipped: + */ + if (dev_priv->current_page == 0) { + OUT_RING(dev_priv->back_pitch_offset_c); + OUT_RING(dev_priv->front_pitch_offset_c); + } else { + OUT_RING(dev_priv->front_pitch_offset_c); + OUT_RING(dev_priv->back_pitch_offset_c); + } + + OUT_RING((x << 16) | y); + OUT_RING((x << 16) | y); + OUT_RING((w << 16) | h); + + ADVANCE_RING(); + } + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + dev_priv->sarea_priv->last_frame++; + + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_LAST_FRAME_REG, 0)); + OUT_RING(dev_priv->sarea_priv->last_frame); + + ADVANCE_RING(); +} + +static void r128_cce_dispatch_flip(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + DRM_DEBUG("page=%d pfCurrentPage=%d\n", + dev_priv->current_page, dev_priv->sarea_priv->pfCurrentPage); + +#if R128_PERFORMANCE_BOXES + /* Do some trivial performance monitoring... + */ + r128_cce_performance_boxes(dev_priv); +#endif + + BEGIN_RING(4); + + R128_WAIT_UNTIL_PAGE_FLIPPED(); + OUT_RING(CCE_PACKET0(R128_CRTC_OFFSET, 0)); + + if (dev_priv->current_page == 0) { + OUT_RING(dev_priv->back_offset); + } else { + OUT_RING(dev_priv->front_offset); + } + + ADVANCE_RING(); + + /* Increment the frame counter. The client-side 3D driver must + * throttle the framerate by waiting for this value before + * performing the swapbuffer ioctl. + */ + dev_priv->sarea_priv->last_frame++; + dev_priv->sarea_priv->pfCurrentPage = dev_priv->current_page = + 1 - dev_priv->current_page; + + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_LAST_FRAME_REG, 0)); + OUT_RING(dev_priv->sarea_priv->last_frame); + + ADVANCE_RING(); +} + +static void r128_cce_dispatch_vertex(struct drm_device * dev, struct drm_buf * buf) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int format = sarea_priv->vc_format; + int offset = buf->bus_address; + int size = buf->used; + int prim = buf_priv->prim; + int i = 0; + RING_LOCALS; + DRM_DEBUG("buf=%d nbox=%d\n", buf->idx, sarea_priv->nbox); + + if (0) + r128_print_dirty("dispatch_vertex", sarea_priv->dirty); + + if (buf->used) { + buf_priv->dispatched = 1; + + if (sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS) { + r128_emit_state(dev_priv); + } + + do { + /* Emit the next set of up to three cliprects */ + if (i < sarea_priv->nbox) { + r128_emit_clip_rects(dev_priv, + &sarea_priv->boxes[i], + sarea_priv->nbox - i); + } + + /* Emit the vertex buffer rendering commands */ + BEGIN_RING(5); + + OUT_RING(CCE_PACKET3(R128_3D_RNDR_GEN_INDX_PRIM, 3)); + OUT_RING(offset); + OUT_RING(size); + OUT_RING(format); + OUT_RING(prim | R128_CCE_VC_CNTL_PRIM_WALK_LIST | + (size << R128_CCE_VC_CNTL_NUM_SHIFT)); + + ADVANCE_RING(); + + i += 3; + } while (i < sarea_priv->nbox); + } + + if (buf_priv->discard) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the vertex buffer age */ + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_LAST_DISPATCH_REG, 0)); + OUT_RING(buf_priv->age); + + ADVANCE_RING(); + + buf->pending = 1; + buf->used = 0; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; + + sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS; + sarea_priv->nbox = 0; +} + +static void r128_cce_dispatch_indirect(struct drm_device * dev, + struct drm_buf * buf, int start, int end) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + RING_LOCALS; + DRM_DEBUG("indirect: buf=%d s=0x%x e=0x%x\n", buf->idx, start, end); + + if (start != end) { + int offset = buf->bus_address + start; + int dwords = (end - start + 3) / sizeof(u32); + + /* Indirect buffer data must be an even number of + * dwords, so if we've been given an odd number we must + * pad the data with a Type-2 CCE packet. + */ + if (dwords & 1) { + u32 *data = (u32 *) + ((char *)dev->agp_buffer_map->handle + + buf->offset + start); + data[dwords++] = cpu_to_le32(R128_CCE_PACKET2); + } + + buf_priv->dispatched = 1; + + /* Fire off the indirect buffer */ + BEGIN_RING(3); + + OUT_RING(CCE_PACKET0(R128_PM4_IW_INDOFF, 1)); + OUT_RING(offset); + OUT_RING(dwords); + + ADVANCE_RING(); + } + + if (buf_priv->discard) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the indirect buffer age */ + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_LAST_DISPATCH_REG, 0)); + OUT_RING(buf_priv->age); + + ADVANCE_RING(); + + buf->pending = 1; + buf->used = 0; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; +} + +static void r128_cce_dispatch_indices(struct drm_device * dev, + struct drm_buf * buf, + int start, int end, int count) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + int format = sarea_priv->vc_format; + int offset = dev->agp_buffer_map->offset - dev_priv->cce_buffers_offset; + int prim = buf_priv->prim; + u32 *data; + int dwords; + int i = 0; + RING_LOCALS; + DRM_DEBUG("indices: s=%d e=%d c=%d\n", start, end, count); + + if (0) + r128_print_dirty("dispatch_indices", sarea_priv->dirty); + + if (start != end) { + buf_priv->dispatched = 1; + + if (sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS) { + r128_emit_state(dev_priv); + } + + dwords = (end - start + 3) / sizeof(u32); + + data = (u32 *) ((char *)dev->agp_buffer_map->handle + + buf->offset + start); + + data[0] = cpu_to_le32(CCE_PACKET3(R128_3D_RNDR_GEN_INDX_PRIM, + dwords - 2)); + + data[1] = cpu_to_le32(offset); + data[2] = cpu_to_le32(R128_MAX_VB_VERTS); + data[3] = cpu_to_le32(format); + data[4] = cpu_to_le32((prim | R128_CCE_VC_CNTL_PRIM_WALK_IND | + (count << 16))); + + if (count & 0x1) { +#ifdef __LITTLE_ENDIAN + data[dwords - 1] &= 0x0000ffff; +#else + data[dwords - 1] &= 0xffff0000; +#endif + } + + do { + /* Emit the next set of up to three cliprects */ + if (i < sarea_priv->nbox) { + r128_emit_clip_rects(dev_priv, + &sarea_priv->boxes[i], + sarea_priv->nbox - i); + } + + r128_cce_dispatch_indirect(dev, buf, start, end); + + i += 3; + } while (i < sarea_priv->nbox); + } + + if (buf_priv->discard) { + buf_priv->age = dev_priv->sarea_priv->last_dispatch; + + /* Emit the vertex buffer age */ + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_LAST_DISPATCH_REG, 0)); + OUT_RING(buf_priv->age); + + ADVANCE_RING(); + + buf->pending = 1; + /* FIXME: Check dispatched field */ + buf_priv->dispatched = 0; + } + + dev_priv->sarea_priv->last_dispatch++; + + sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS; + sarea_priv->nbox = 0; +} + +static int r128_cce_dispatch_blit(struct drm_device * dev, + struct drm_file *file_priv, + drm_r128_blit_t * blit) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_r128_buf_priv_t *buf_priv; + u32 *data; + int dword_shift, dwords; + RING_LOCALS; + DRM_DEBUG("\n"); + + /* The compiler won't optimize away a division by a variable, + * even if the only legal values are powers of two. Thus, we'll + * use a shift instead. + */ + switch (blit->format) { + case R128_DATATYPE_ARGB8888: + dword_shift = 0; + break; + case R128_DATATYPE_ARGB1555: + case R128_DATATYPE_RGB565: + case R128_DATATYPE_ARGB4444: + case R128_DATATYPE_YVYU422: + case R128_DATATYPE_VYUY422: + dword_shift = 1; + break; + case R128_DATATYPE_CI8: + case R128_DATATYPE_RGB8: + dword_shift = 2; + break; + default: + DRM_ERROR("invalid blit format %d\n", blit->format); + return -EINVAL; + } + + /* Flush the pixel cache, and mark the contents as Read Invalid. + * This ensures no pixel data gets mixed up with the texture + * data from the host data blit, otherwise part of the texture + * image may be corrupted. + */ + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_PC_GUI_CTLSTAT, 0)); + OUT_RING(R128_PC_RI_GUI | R128_PC_FLUSH_GUI); + + ADVANCE_RING(); + + /* Dispatch the indirect buffer. + */ + buf = dma->buflist[blit->idx]; + buf_priv = buf->dev_private; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", blit->idx); + return -EINVAL; + } + + buf_priv->discard = 1; + + dwords = (blit->width * blit->height) >> dword_shift; + + data = (u32 *) ((char *)dev->agp_buffer_map->handle + buf->offset); + + data[0] = cpu_to_le32(CCE_PACKET3(R128_CNTL_HOSTDATA_BLT, dwords + 6)); + data[1] = cpu_to_le32((R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_NONE | + (blit->format << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_S | + R128_DP_SRC_SOURCE_HOST_DATA | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_AUX_CLIP_DIS | R128_GMC_WR_MSK_DIS)); + + data[2] = cpu_to_le32((blit->pitch << 21) | (blit->offset >> 5)); + data[3] = cpu_to_le32(0xffffffff); + data[4] = cpu_to_le32(0xffffffff); + data[5] = cpu_to_le32((blit->y << 16) | blit->x); + data[6] = cpu_to_le32((blit->height << 16) | blit->width); + data[7] = cpu_to_le32(dwords); + + buf->used = (dwords + 8) * sizeof(u32); + + r128_cce_dispatch_indirect(dev, buf, 0, buf->used); + + /* Flush the pixel cache after the blit completes. This ensures + * the texture data is written out to memory before rendering + * continues. + */ + BEGIN_RING(2); + + OUT_RING(CCE_PACKET0(R128_PC_GUI_CTLSTAT, 0)); + OUT_RING(R128_PC_FLUSH_GUI); + + ADVANCE_RING(); + + return 0; +} + +/* ================================================================ + * Tiled depth buffer management + * + * FIXME: These should all set the destination write mask for when we + * have hardware stencil support. + */ + +static int r128_cce_dispatch_write_span(struct drm_device * dev, + drm_r128_depth_t * depth) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, x, y; + u32 *buffer; + u8 *mask; + int i, buffer_size, mask_size; + RING_LOCALS; + DRM_DEBUG("\n"); + + count = depth->n; + if (count > 4096 || count <= 0) + return -EMSGSIZE; + + if (DRM_COPY_FROM_USER(&x, depth->x, sizeof(x))) { + return -EFAULT; + } + if (DRM_COPY_FROM_USER(&y, depth->y, sizeof(y))) { + return -EFAULT; + } + + buffer_size = depth->n * sizeof(u32); + buffer = drm_alloc(buffer_size, DRM_MEM_BUFS); + if (buffer == NULL) + return -ENOMEM; + if (DRM_COPY_FROM_USER(buffer, depth->buffer, buffer_size)) { + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + return -EFAULT; + } + + mask_size = depth->n * sizeof(u8); + if (depth->mask) { + mask = drm_alloc(mask_size, DRM_MEM_BUFS); + if (mask == NULL) { + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(mask, depth->mask, mask_size)) { + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + drm_free(mask, mask_size, DRM_MEM_BUFS); + return -EFAULT; + } + + for (i = 0; i < count; i++, x++) { + if (mask[i]) { + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + (dev_priv->depth_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_WR_MSK_DIS); + + OUT_RING(dev_priv->depth_pitch_offset_c); + OUT_RING(buffer[i]); + + OUT_RING((x << 16) | y); + OUT_RING((1 << 16) | 1); + + ADVANCE_RING(); + } + } + + drm_free(mask, mask_size, DRM_MEM_BUFS); + } else { + for (i = 0; i < count; i++, x++) { + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + (dev_priv->depth_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_WR_MSK_DIS); + + OUT_RING(dev_priv->depth_pitch_offset_c); + OUT_RING(buffer[i]); + + OUT_RING((x << 16) | y); + OUT_RING((1 << 16) | 1); + + ADVANCE_RING(); + } + } + + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + + return 0; +} + +static int r128_cce_dispatch_write_pixels(struct drm_device * dev, + drm_r128_depth_t * depth) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, *x, *y; + u32 *buffer; + u8 *mask; + int i, xbuf_size, ybuf_size, buffer_size, mask_size; + RING_LOCALS; + DRM_DEBUG("\n"); + + count = depth->n; + if (count > 4096 || count <= 0) + return -EMSGSIZE; + + xbuf_size = count * sizeof(*x); + ybuf_size = count * sizeof(*y); + x = drm_alloc(xbuf_size, DRM_MEM_BUFS); + if (x == NULL) { + return -ENOMEM; + } + y = drm_alloc(ybuf_size, DRM_MEM_BUFS); + if (y == NULL) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(x, depth->x, xbuf_size)) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + return -EFAULT; + } + if (DRM_COPY_FROM_USER(y, depth->y, xbuf_size)) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + return -EFAULT; + } + + buffer_size = depth->n * sizeof(u32); + buffer = drm_alloc(buffer_size, DRM_MEM_BUFS); + if (buffer == NULL) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(buffer, depth->buffer, buffer_size)) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + return -EFAULT; + } + + if (depth->mask) { + mask_size = depth->n * sizeof(u8); + mask = drm_alloc(mask_size, DRM_MEM_BUFS); + if (mask == NULL) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(mask, depth->mask, mask_size)) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + drm_free(mask, mask_size, DRM_MEM_BUFS); + return -EFAULT; + } + + for (i = 0; i < count; i++) { + if (mask[i]) { + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + (dev_priv->depth_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_WR_MSK_DIS); + + OUT_RING(dev_priv->depth_pitch_offset_c); + OUT_RING(buffer[i]); + + OUT_RING((x[i] << 16) | y[i]); + OUT_RING((1 << 16) | 1); + + ADVANCE_RING(); + } + } + + drm_free(mask, mask_size, DRM_MEM_BUFS); + } else { + for (i = 0; i < count; i++) { + BEGIN_RING(6); + + OUT_RING(CCE_PACKET3(R128_CNTL_PAINT_MULTI, 4)); + OUT_RING(R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_SOLID_COLOR | + (dev_priv->depth_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_P | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_WR_MSK_DIS); + + OUT_RING(dev_priv->depth_pitch_offset_c); + OUT_RING(buffer[i]); + + OUT_RING((x[i] << 16) | y[i]); + OUT_RING((1 << 16) | 1); + + ADVANCE_RING(); + } + } + + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + drm_free(buffer, buffer_size, DRM_MEM_BUFS); + + return 0; +} + +static int r128_cce_dispatch_read_span(struct drm_device * dev, + drm_r128_depth_t * depth) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, x, y; + RING_LOCALS; + DRM_DEBUG("\n"); + + count = depth->n; + if (count > 4096 || count <= 0) + return -EMSGSIZE; + + if (DRM_COPY_FROM_USER(&x, depth->x, sizeof(x))) { + return -EFAULT; + } + if (DRM_COPY_FROM_USER(&y, depth->y, sizeof(y))) { + return -EFAULT; + } + + BEGIN_RING(7); + + OUT_RING(CCE_PACKET3(R128_CNTL_BITBLT_MULTI, 5)); + OUT_RING(R128_GMC_SRC_PITCH_OFFSET_CNTL | + R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_NONE | + (dev_priv->depth_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_S | + R128_DP_SRC_SOURCE_MEMORY | + R128_GMC_CLR_CMP_CNTL_DIS | R128_GMC_WR_MSK_DIS); + + OUT_RING(dev_priv->depth_pitch_offset_c); + OUT_RING(dev_priv->span_pitch_offset_c); + + OUT_RING((x << 16) | y); + OUT_RING((0 << 16) | 0); + OUT_RING((count << 16) | 1); + + ADVANCE_RING(); + + return 0; +} + +static int r128_cce_dispatch_read_pixels(struct drm_device * dev, + drm_r128_depth_t * depth) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int count, *x, *y; + int i, xbuf_size, ybuf_size; + RING_LOCALS; + DRM_DEBUG("\n"); + + count = depth->n; + if (count > 4096 || count <= 0) + return -EMSGSIZE; + + if (count > dev_priv->depth_pitch) { + count = dev_priv->depth_pitch; + } + + xbuf_size = count * sizeof(*x); + ybuf_size = count * sizeof(*y); + x = drm_alloc(xbuf_size, DRM_MEM_BUFS); + if (x == NULL) { + return -ENOMEM; + } + y = drm_alloc(ybuf_size, DRM_MEM_BUFS); + if (y == NULL) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(x, depth->x, xbuf_size)) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + return -EFAULT; + } + if (DRM_COPY_FROM_USER(y, depth->y, ybuf_size)) { + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + return -EFAULT; + } + + for (i = 0; i < count; i++) { + BEGIN_RING(7); + + OUT_RING(CCE_PACKET3(R128_CNTL_BITBLT_MULTI, 5)); + OUT_RING(R128_GMC_SRC_PITCH_OFFSET_CNTL | + R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_NONE | + (dev_priv->depth_fmt << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_S | + R128_DP_SRC_SOURCE_MEMORY | + R128_GMC_CLR_CMP_CNTL_DIS | R128_GMC_WR_MSK_DIS); + + OUT_RING(dev_priv->depth_pitch_offset_c); + OUT_RING(dev_priv->span_pitch_offset_c); + + OUT_RING((x[i] << 16) | y[i]); + OUT_RING((i << 16) | 0); + OUT_RING((1 << 16) | 1); + + ADVANCE_RING(); + } + + drm_free(x, xbuf_size, DRM_MEM_BUFS); + drm_free(y, ybuf_size, DRM_MEM_BUFS); + + return 0; +} + +/* ================================================================ + * Polygon stipple + */ + +static void r128_cce_dispatch_stipple(struct drm_device * dev, u32 * stipple) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int i; + RING_LOCALS; + DRM_DEBUG("\n"); + + BEGIN_RING(33); + + OUT_RING(CCE_PACKET0(R128_BRUSH_DATA0, 31)); + for (i = 0; i < 32; i++) { + OUT_RING(stipple[i]); + } + + ADVANCE_RING(); +} + +/* ================================================================ + * IOCTL functions + */ + +static int r128_cce_clear(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_r128_clear_t *clear = data; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + if (sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS; + + r128_cce_dispatch_clear(dev, clear); + COMMIT_RING(); + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= R128_UPLOAD_CONTEXT | R128_UPLOAD_MASKS; + + return 0; +} + +static int r128_do_init_pageflip(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + dev_priv->crtc_offset = R128_READ(R128_CRTC_OFFSET); + dev_priv->crtc_offset_cntl = R128_READ(R128_CRTC_OFFSET_CNTL); + + R128_WRITE(R128_CRTC_OFFSET, dev_priv->front_offset); + R128_WRITE(R128_CRTC_OFFSET_CNTL, + dev_priv->crtc_offset_cntl | R128_CRTC_OFFSET_FLIP_CNTL); + + dev_priv->page_flipping = 1; + dev_priv->current_page = 0; + dev_priv->sarea_priv->pfCurrentPage = dev_priv->current_page; + + return 0; +} + +static int r128_do_cleanup_pageflip(struct drm_device * dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + R128_WRITE(R128_CRTC_OFFSET, dev_priv->crtc_offset); + R128_WRITE(R128_CRTC_OFFSET_CNTL, dev_priv->crtc_offset_cntl); + + if (dev_priv->current_page != 0) { + r128_cce_dispatch_flip(dev); + COMMIT_RING(); + } + + dev_priv->page_flipping = 0; + return 0; +} + +/* Swapping and flipping are different operations, need different ioctls. + * They can & should be intermixed to support multiple 3d windows. + */ + +static int r128_cce_flip(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + if (!dev_priv->page_flipping) + r128_do_init_pageflip(dev); + + r128_cce_dispatch_flip(dev); + + COMMIT_RING(); + return 0; +} + +static int r128_cce_swap(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; + DRM_DEBUG("\n"); + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + if (sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS; + + r128_cce_dispatch_swap(dev); + dev_priv->sarea_priv->dirty |= (R128_UPLOAD_CONTEXT | + R128_UPLOAD_MASKS); + + COMMIT_RING(); + return 0; +} + +static int r128_cce_vertex(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_vertex_t *vertex = data; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + DRM_DEBUG("pid=%d index=%d count=%d discard=%d\n", + DRM_CURRENTPID, vertex->idx, vertex->count, vertex->discard); + + if (vertex->idx < 0 || vertex->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + vertex->idx, dma->buf_count - 1); + return -EINVAL; + } + if (vertex->prim < 0 || + vertex->prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2) { + DRM_ERROR("buffer prim %d\n", vertex->prim); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + buf = dma->buflist[vertex->idx]; + buf_priv = buf->dev_private; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", vertex->idx); + return -EINVAL; + } + + buf->used = vertex->count; + buf_priv->prim = vertex->prim; + buf_priv->discard = vertex->discard; + + r128_cce_dispatch_vertex(dev, buf); + + COMMIT_RING(); + return 0; +} + +static int r128_cce_indices(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_indices_t *elts = data; + int count; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + DRM_DEBUG("pid=%d buf=%d s=%d e=%d d=%d\n", DRM_CURRENTPID, + elts->idx, elts->start, elts->end, elts->discard); + + if (elts->idx < 0 || elts->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + elts->idx, dma->buf_count - 1); + return -EINVAL; + } + if (elts->prim < 0 || + elts->prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2) { + DRM_ERROR("buffer prim %d\n", elts->prim); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + buf = dma->buflist[elts->idx]; + buf_priv = buf->dev_private; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", elts->idx); + return -EINVAL; + } + + count = (elts->end - elts->start) / sizeof(u16); + elts->start -= R128_INDEX_PRIM_OFFSET; + + if (elts->start & 0x7) { + DRM_ERROR("misaligned buffer 0x%x\n", elts->start); + return -EINVAL; + } + if (elts->start < buf->used) { + DRM_ERROR("no header 0x%x - 0x%x\n", elts->start, buf->used); + return -EINVAL; + } + + buf->used = elts->end; + buf_priv->prim = elts->prim; + buf_priv->discard = elts->discard; + + r128_cce_dispatch_indices(dev, buf, elts->start, elts->end, count); + + COMMIT_RING(); + return 0; +} + +static int r128_cce_blit(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_blit_t *blit = data; + int ret; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + DRM_DEBUG("pid=%d index=%d\n", DRM_CURRENTPID, blit->idx); + + if (blit->idx < 0 || blit->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + blit->idx, dma->buf_count - 1); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + ret = r128_cce_dispatch_blit(dev, file_priv, blit); + + COMMIT_RING(); + return ret; +} + +static int r128_cce_depth(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_depth_t *depth = data; + int ret; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + ret = -EINVAL; + switch (depth->func) { + case R128_WRITE_SPAN: + ret = r128_cce_dispatch_write_span(dev, depth); + break; + case R128_WRITE_PIXELS: + ret = r128_cce_dispatch_write_pixels(dev, depth); + break; + case R128_READ_SPAN: + ret = r128_cce_dispatch_read_span(dev, depth); + break; + case R128_READ_PIXELS: + ret = r128_cce_dispatch_read_pixels(dev, depth); + break; + } + + COMMIT_RING(); + return ret; +} + +static int r128_cce_stipple(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_stipple_t *stipple = data; + u32 mask[32]; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (DRM_COPY_FROM_USER(&mask, stipple->mask, 32 * sizeof(u32))) + return -EFAULT; + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + + r128_cce_dispatch_stipple(dev, mask); + + COMMIT_RING(); + return 0; +} + +static int r128_cce_indirect(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf; + drm_r128_buf_priv_t *buf_priv; + drm_r128_indirect_t *indirect = data; +#if 0 + RING_LOCALS; +#endif + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + DRM_DEBUG("idx=%d s=%d e=%d d=%d\n", + indirect->idx, indirect->start, indirect->end, + indirect->discard); + + if (indirect->idx < 0 || indirect->idx >= dma->buf_count) { + DRM_ERROR("buffer index %d (of %d max)\n", + indirect->idx, dma->buf_count - 1); + return -EINVAL; + } + + buf = dma->buflist[indirect->idx]; + buf_priv = buf->dev_private; + + if (buf->file_priv != file_priv) { + DRM_ERROR("process %d using buffer owned by %p\n", + DRM_CURRENTPID, buf->file_priv); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("sending pending buffer %d\n", indirect->idx); + return -EINVAL; + } + + if (indirect->start < buf->used) { + DRM_ERROR("reusing indirect: start=0x%x actual=0x%x\n", + indirect->start, buf->used); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN(dev_priv); + VB_AGE_TEST_WITH_RETURN(dev_priv); + + buf->used = indirect->end; + buf_priv->discard = indirect->discard; + +#if 0 + /* Wait for the 3D stream to idle before the indirect buffer + * containing 2D acceleration commands is processed. + */ + BEGIN_RING(2); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); +#endif + + /* Dispatch the indirect buffer full of commands from the + * X server. This is insecure and is thus only available to + * privileged clients. + */ + r128_cce_dispatch_indirect(dev, buf, indirect->start, indirect->end); + + COMMIT_RING(); + return 0; +} + +static int r128_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_getparam_t *param = data; + int value; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + DRM_DEBUG("pid=%d\n", DRM_CURRENTPID); + + switch (param->param) { + case R128_PARAM_IRQ_NR: + value = dev->irq; + break; + default: + return -EINVAL; + } + + if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return -EFAULT; + } + + return 0; +} + +void r128_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) +{ + if (dev->dev_private) { + drm_r128_private_t *dev_priv = dev->dev_private; + if (dev_priv->page_flipping) { + r128_do_cleanup_pageflip(dev); + } + } +} + +void r128_driver_lastclose(struct drm_device * dev) +{ + r128_do_cleanup_cce(dev); +} + +struct drm_ioctl_desc r128_ioctls[] = { + DRM_IOCTL_DEF(DRM_R128_INIT, r128_cce_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_R128_CCE_START, r128_cce_start, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_R128_CCE_STOP, r128_cce_stop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_R128_CCE_RESET, r128_cce_reset, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_R128_CCE_IDLE, r128_cce_idle, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_RESET, r128_engine_reset, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_FULLSCREEN, r128_fullscreen, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_SWAP, r128_cce_swap, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_FLIP, r128_cce_flip, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_CLEAR, r128_cce_clear, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_VERTEX, r128_cce_vertex, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_INDICES, r128_cce_indices, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_BLIT, r128_cce_blit, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_DEPTH, r128_cce_depth, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_STIPPLE, r128_cce_stipple, DRM_AUTH), + DRM_IOCTL_DEF(DRM_R128_INDIRECT, r128_cce_indirect, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_R128_GETPARAM, r128_getparam, DRM_AUTH), +}; + +int r128_max_ioctl = DRM_ARRAY_SIZE(r128_ioctls); -- cgit v1.2.3