summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/r128
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
commit5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch)
treea94efe259b9009378be6d90eb30d2b019d95c194 /drivers/gpu/drm/r128
parentInitial commit. (diff)
downloadlinux-upstream.tar.xz
linux-upstream.zip
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--drivers/gpu/drm/r128/Makefile10
-rw-r--r--drivers/gpu/drm/r128/ati_pcigart.c210
-rw-r--r--drivers/gpu/drm/r128/ati_pcigart.h31
-rw-r--r--drivers/gpu/drm/r128/r128_cce.c945
-rw-r--r--drivers/gpu/drm/r128/r128_drv.c114
-rw-r--r--drivers/gpu/drm/r128/r128_drv.h544
-rw-r--r--drivers/gpu/drm/r128/r128_ioc32.c199
-rw-r--r--drivers/gpu/drm/r128/r128_irq.c118
-rw-r--r--drivers/gpu/drm/r128/r128_state.c1640
9 files changed, 3811 insertions, 0 deletions
diff --git a/drivers/gpu/drm/r128/Makefile b/drivers/gpu/drm/r128/Makefile
new file mode 100644
index 000000000..c07a06953
--- /dev/null
+++ b/drivers/gpu/drm/r128/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+r128-y := r128_drv.o r128_cce.o r128_state.o r128_irq.o ati_pcigart.o
+
+r128-$(CONFIG_COMPAT) += r128_ioc32.o
+
+obj-$(CONFIG_DRM_R128) += r128.o
diff --git a/drivers/gpu/drm/r128/ati_pcigart.c b/drivers/gpu/drm/r128/ati_pcigart.c
new file mode 100644
index 000000000..3e76ae5a1
--- /dev/null
+++ b/drivers/gpu/drm/r128/ati_pcigart.c
@@ -0,0 +1,210 @@
+/**
+ * \file ati_pcigart.c
+ * ATI PCI GART support
+ *
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Wed Dec 13 21:52:19 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.
+ */
+
+#include <linux/export.h>
+#include <linux/pci.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_legacy.h>
+#include <drm/drm_print.h>
+
+#include "ati_pcigart.h"
+
+# define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */
+
+static int drm_ati_alloc_pcigart_table(struct drm_device *dev,
+ struct drm_ati_pcigart_info *gart_info)
+{
+ gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size,
+ PAGE_SIZE);
+ if (gart_info->table_handle == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void drm_ati_free_pcigart_table(struct drm_device *dev,
+ struct drm_ati_pcigart_info *gart_info)
+{
+ drm_pci_free(dev, gart_info->table_handle);
+ gart_info->table_handle = NULL;
+}
+
+int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
+{
+ struct drm_sg_mem *entry = dev->sg;
+ unsigned long pages;
+ int i;
+ int max_pages;
+
+ /* we need to support large memory configurations */
+ if (!entry) {
+ DRM_ERROR("no scatter/gather memory!\n");
+ return 0;
+ }
+
+ if (gart_info->bus_addr) {
+
+ max_pages = (gart_info->table_size / sizeof(u32));
+ pages = (entry->pages <= max_pages)
+ ? entry->pages : max_pages;
+
+ for (i = 0; i < pages; i++) {
+ if (!entry->busaddr[i])
+ break;
+ pci_unmap_page(dev->pdev, entry->busaddr[i],
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ }
+
+ if (gart_info->gart_table_location == DRM_ATI_GART_MAIN)
+ gart_info->bus_addr = 0;
+ }
+
+ if (gart_info->gart_table_location == DRM_ATI_GART_MAIN &&
+ gart_info->table_handle) {
+ drm_ati_free_pcigart_table(dev, gart_info);
+ }
+
+ return 1;
+}
+
+int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
+{
+ struct drm_local_map *map = &gart_info->mapping;
+ struct drm_sg_mem *entry = dev->sg;
+ void *address = NULL;
+ unsigned long pages;
+ u32 *pci_gart = NULL, page_base, gart_idx;
+ dma_addr_t bus_address = 0;
+ int i, j, ret = -ENOMEM;
+ int max_ati_pages, max_real_pages;
+
+ if (!entry) {
+ DRM_ERROR("no scatter/gather memory!\n");
+ goto done;
+ }
+
+ if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
+ DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n");
+
+ if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) {
+ DRM_ERROR("fail to set dma mask to 0x%Lx\n",
+ (unsigned long long)gart_info->table_mask);
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = drm_ati_alloc_pcigart_table(dev, gart_info);
+ if (ret) {
+ DRM_ERROR("cannot allocate PCI GART page!\n");
+ goto done;
+ }
+
+ pci_gart = gart_info->table_handle->vaddr;
+ address = gart_info->table_handle->vaddr;
+ bus_address = gart_info->table_handle->busaddr;
+ } else {
+ address = gart_info->addr;
+ bus_address = gart_info->bus_addr;
+ DRM_DEBUG("PCI: Gart Table: VRAM %08LX mapped at %08lX\n",
+ (unsigned long long)bus_address,
+ (unsigned long)address);
+ }
+
+
+ max_ati_pages = (gart_info->table_size / sizeof(u32));
+ max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE);
+ pages = (entry->pages <= max_real_pages)
+ ? entry->pages : max_real_pages;
+
+ if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
+ memset(pci_gart, 0, max_ati_pages * sizeof(u32));
+ } else {
+ memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u32));
+ }
+
+ gart_idx = 0;
+ for (i = 0; i < pages; i++) {
+ /* we need to support large memory configurations */
+ entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i],
+ 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+ if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
+ DRM_ERROR("unable to map PCIGART pages!\n");
+ drm_ati_pcigart_cleanup(dev, gart_info);
+ address = NULL;
+ bus_address = 0;
+ ret = -ENOMEM;
+ goto done;
+ }
+ page_base = (u32) entry->busaddr[i];
+
+ for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) {
+ u32 offset;
+ u32 val;
+
+ switch(gart_info->gart_reg_if) {
+ case DRM_ATI_GART_IGP:
+ val = page_base | 0xc;
+ break;
+ case DRM_ATI_GART_PCIE:
+ val = (page_base >> 8) | 0xc;
+ break;
+ default:
+ case DRM_ATI_GART_PCI:
+ val = page_base;
+ break;
+ }
+ if (gart_info->gart_table_location ==
+ DRM_ATI_GART_MAIN) {
+ pci_gart[gart_idx] = cpu_to_le32(val);
+ } else {
+ offset = gart_idx * sizeof(u32);
+ writel(val, (void __iomem *)map->handle + offset);
+ }
+ gart_idx++;
+ page_base += ATI_PCIGART_PAGE_SIZE;
+ }
+ }
+ ret = 0;
+
+#if defined(__i386__) || defined(__x86_64__)
+ wbinvd();
+#else
+ mb();
+#endif
+
+ done:
+ gart_info->addr = address;
+ gart_info->bus_addr = bus_address;
+ return ret;
+}
diff --git a/drivers/gpu/drm/r128/ati_pcigart.h b/drivers/gpu/drm/r128/ati_pcigart.h
new file mode 100644
index 000000000..a728a1364
--- /dev/null
+++ b/drivers/gpu/drm/r128/ati_pcigart.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef DRM_ATI_PCIGART_H
+#define DRM_ATI_PCIGART_H
+
+#include <drm/drm_legacy.h>
+
+/* location of GART table */
+#define DRM_ATI_GART_MAIN 1
+#define DRM_ATI_GART_FB 2
+
+#define DRM_ATI_GART_PCI 1
+#define DRM_ATI_GART_PCIE 2
+#define DRM_ATI_GART_IGP 3
+
+struct drm_ati_pcigart_info {
+ int gart_table_location;
+ int gart_reg_if;
+ void *addr;
+ dma_addr_t bus_addr;
+ dma_addr_t table_mask;
+ struct drm_dma_handle *table_handle;
+ struct drm_local_map mapping;
+ int table_size;
+};
+
+extern int drm_ati_pcigart_init(struct drm_device *dev,
+ struct drm_ati_pcigart_info * gart_info);
+extern int drm_ati_pcigart_cleanup(struct drm_device *dev,
+ struct drm_ati_pcigart_info * gart_info);
+
+#endif
diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c
new file mode 100644
index 000000000..138af3248
--- /dev/null
+++ b/drivers/gpu/drm/r128/r128_cce.c
@@ -0,0 +1,945 @@
+/* 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 <gareth@valinux.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <drm/drm_agpsupport.h>
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <drm/drm_irq.h>
+#include <drm/drm_print.h>
+#include <drm/r128_drm.h>
+
+#include "r128_drv.h"
+
+#define R128_FIFO_DEBUG 0
+
+#define FIRMWARE_NAME "r128/r128_cce.bin"
+
+MODULE_FIRMWARE(FIRMWARE_NAME);
+
+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;
+ 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;
+ 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;
+ }
+ udelay(1);
+ }
+
+#if R128_FIFO_DEBUG
+ DRM_ERROR("failed!\n");
+#endif
+ return -EBUSY;
+}
+
+/* ================================================================
+ * CCE control, initialization
+ */
+
+/* Load the microcode for the CCE */
+static int r128_cce_load_microcode(drm_r128_private_t *dev_priv)
+{
+ struct platform_device *pdev;
+ const struct firmware *fw;
+ const __be32 *fw_data;
+ int rc, i;
+
+ DRM_DEBUG("\n");
+
+ pdev = platform_device_register_simple("r128_cce", 0, NULL, 0);
+ if (IS_ERR(pdev)) {
+ pr_err("r128_cce: Failed to register firmware\n");
+ return PTR_ERR(pdev);
+ }
+ rc = request_firmware(&fw, FIRMWARE_NAME, &pdev->dev);
+ platform_device_unregister(pdev);
+ if (rc) {
+ pr_err("r128_cce: Failed to load firmware \"%s\"\n",
+ FIRMWARE_NAME);
+ return rc;
+ }
+
+ if (fw->size != 256 * 8) {
+ pr_err("r128_cce: Bogus length %zu in firmware \"%s\"\n",
+ fw->size, FIRMWARE_NAME);
+ rc = -EINVAL;
+ goto out_release;
+ }
+
+ r128_do_wait_for_idle(dev_priv);
+
+ fw_data = (const __be32 *)fw->data;
+ R128_WRITE(R128_PM4_MICROCODE_ADDR, 0);
+ for (i = 0; i < 256; i++) {
+ R128_WRITE(R128_PM4_MICROCODE_DATAH,
+ be32_to_cpup(&fw_data[i * 2]));
+ R128_WRITE(R128_PM4_MICROCODE_DATAL,
+ be32_to_cpup(&fw_data[i * 2 + 1]));
+ }
+
+out_release:
+ release_firmware(fw);
+ return rc;
+}
+
+/* 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);
+ }
+ }
+ 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 IS_ENABLED(CONFIG_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;
+ int rc;
+
+ DRM_DEBUG("\n");
+
+ if (dev->dev_private) {
+ DRM_DEBUG("called when already initialized\n");
+ return -EINVAL;
+ }
+
+ dev_priv = kzalloc(sizeof(drm_r128_private_t), GFP_KERNEL);
+ if (dev_priv == NULL)
+ return -ENOMEM;
+
+ 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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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_legacy_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 IS_ENABLED(CONFIG_AGP)
+ if (!dev_priv->is_pci) {
+ drm_legacy_ioremap_wc(dev_priv->cce_ring, dev);
+ drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev);
+ drm_legacy_ioremap_wc(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 *)(unsigned long)dev_priv->cce_ring->offset;
+ dev_priv->ring_rptr->handle =
+ (void *)(unsigned long)dev_priv->ring_rptr->offset;
+ dev->agp_buffer_map->handle =
+ (void *)(unsigned long)dev->agp_buffer_map->offset;
+ }
+
+#if IS_ENABLED(CONFIG_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 = order_base_2(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 IS_ENABLED(CONFIG_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;
+ rc = drm_ati_pcigart_init(dev, &dev_priv->gart_info);
+ if (rc) {
+ DRM_ERROR("failed to init PCI GART!\n");
+ dev->dev_private = (void *)dev_priv;
+ r128_do_cleanup_cce(dev);
+ return rc;
+ }
+ R128_WRITE(R128_PCI_GART_PAGE, dev_priv->gart_info.bus_addr);
+#if IS_ENABLED(CONFIG_AGP)
+ }
+#endif
+
+ r128_cce_init_ring_buffer(dev, dev_priv);
+ rc = r128_cce_load_microcode(dev_priv);
+
+ dev->dev_private = (void *)dev_priv;
+
+ r128_do_engine_reset(dev);
+
+ if (rc) {
+ DRM_ERROR("Failed to load firmware!\n");
+ r128_do_cleanup_cce(dev);
+ }
+
+ return rc;
+}
+
+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 IS_ENABLED(CONFIG_AGP)
+ if (!dev_priv->is_pci) {
+ if (dev_priv->cce_ring != NULL)
+ drm_legacy_ioremapfree(dev_priv->cce_ring, dev);
+ if (dev_priv->ring_rptr != NULL)
+ drm_legacy_ioremapfree(dev_priv->ring_rptr, dev);
+ if (dev->agp_buffer_map != NULL) {
+ drm_legacy_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");
+ }
+
+ kfree(dev->dev_private);
+ 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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ 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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev->dev_private);
+
+ 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 = kzalloc(sizeof(drm_r128_freelist_t), GFP_KERNEL);
+ if (dev_priv->head == NULL)
+ return -ENOMEM;
+
+ 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 = kmalloc(sizeof(drm_r128_freelist_t), GFP_KERNEL);
+ 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;
+ }
+ }
+ 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;
+ 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 (copy_to_user(&d->request_indices[i], &buf->idx,
+ sizeof(buf->idx)))
+ return -EFAULT;
+ if (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",
+ task_pid_nr(current), 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",
+ task_pid_nr(current), 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 000000000..b7a5f162e
--- /dev/null
+++ b/drivers/gpu/drm/r128/r128_drv.c
@@ -0,0 +1,114 @@
+/* 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 <faith@valinux.com>
+ * Gareth Hughes <gareth@valinux.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_pciids.h>
+#include <drm/drm_vblank.h>
+#include <drm/r128_drm.h>
+
+#include "r128_drv.h"
+
+static struct pci_device_id pciidlist[] = {
+ r128_PCI_IDS
+};
+
+static const struct file_operations r128_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_legacy_mmap,
+ .poll = drm_poll,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = r128_compat_ioctl,
+#endif
+ .llseek = noop_llseek,
+};
+
+static struct drm_driver driver = {
+ .driver_features =
+ DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_LEGACY |
+ DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ,
+ .dev_priv_size = sizeof(drm_r128_buf_priv_t),
+ .load = r128_driver_load,
+ .preclose = r128_driver_preclose,
+ .lastclose = r128_driver_lastclose,
+ .get_vblank_counter = r128_get_vblank_counter,
+ .enable_vblank = r128_enable_vblank,
+ .disable_vblank = r128_disable_vblank,
+ .irq_preinstall = r128_driver_irq_preinstall,
+ .irq_postinstall = r128_driver_irq_postinstall,
+ .irq_uninstall = r128_driver_irq_uninstall,
+ .irq_handler = r128_driver_irq_handler,
+ .ioctls = r128_ioctls,
+ .dma_ioctl = r128_cce_buffers,
+ .fops = &r128_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+int r128_driver_load(struct drm_device *dev, unsigned long flags)
+{
+ pci_set_master(dev->pdev);
+ return drm_vblank_init(dev, 1);
+}
+
+static struct pci_driver r128_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pciidlist,
+};
+
+static int __init r128_init(void)
+{
+ driver.num_ioctls = r128_max_ioctl;
+
+ return drm_legacy_pci_init(&driver, &r128_pci_driver);
+}
+
+static void __exit r128_exit(void)
+{
+ drm_legacy_pci_exit(&driver, &r128_pci_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 000000000..8b256123c
--- /dev/null
+++ b/drivers/gpu/drm/r128/r128_drv.h
@@ -0,0 +1,544 @@
+/* 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 <faith@valinux.com>
+ * Kevin E. Martin <martin@valinux.com>
+ * Gareth Hughes <gareth@valinux.com>
+ * Michel D�zer <daenzerm@student.ethz.ch>
+ */
+
+#ifndef __R128_DRV_H__
+#define __R128_DRV_H__
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+
+#include <drm/drm_ioctl.h>
+#include <drm/drm_legacy.h>
+#include <drm/r128_drm.h>
+
+#include "ati_pcigart.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;
+
+ atomic_t vbl_received;
+
+ 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 const 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 int r128_cce_stipple(struct drm_device *dev, void *data, struct drm_file *file_priv);
+extern int r128_cce_depth(struct drm_device *dev, void *data, struct drm_file *file_priv);
+extern int r128_getparam(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_enable_vblank(struct drm_device *dev, unsigned int pipe);
+extern void r128_disable_vblank(struct drm_device *dev, unsigned int pipe);
+extern u32 r128_get_vblank_counter(struct drm_device *dev, unsigned int pipe);
+extern irqreturn_t r128_driver_irq_handler(int irq, void *arg);
+extern void r128_driver_irq_preinstall(struct drm_device *dev);
+extern int 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 int r128_driver_load(struct drm_device *dev, unsigned long flags);
+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) readl(((void __iomem *)dev_priv->mmio->handle) + (reg))
+#define R128_WRITE(reg, val) writel(val, ((void __iomem *)dev_priv->mmio->handle) + (reg))
+#define R128_READ8(reg) readb(((void __iomem *)dev_priv->mmio->handle) + (reg))
+#define R128_WRITE8(reg, val) writeb(val, ((void __iomem *)dev_priv->mmio->handle) + (reg))
+
+#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 DEV_INIT_TEST_WITH_RETURN(_dev_priv) \
+do { \
+ if (!_dev_priv) { \
+ DRM_ERROR("called with no initialization\n"); \
+ return -EINVAL; \
+ } \
+} while (0)
+
+#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; \
+ 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); \
+ mb(); \
+ 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 000000000..6ac71755c
--- /dev/null
+++ b/drivers/gpu/drm/r128/r128_ioc32.c
@@ -0,0 +1,199 @@
+/**
+ * \file r128_ioc32.c
+ *
+ * 32-bit ioctl compatibility routines for the R128 DRM.
+ *
+ * \author Dave Airlie <airlied@linux.ie> 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 <linux/compat.h>
+
+#include <drm/r128_drm.h>
+
+#include "r128_drv.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 init;
+
+ if (copy_from_user(&init32, (void __user *)arg, sizeof(init32)))
+ return -EFAULT;
+
+ init.func = init32.func;
+ init.sarea_priv_offset = init32.sarea_priv_offset;
+ init.is_pci = init32.is_pci;
+ init.cce_mode = init32.cce_mode;
+ init.cce_secure = init32.cce_secure;
+ init.ring_size = init32.ring_size;
+ init.usec_timeout = init32.usec_timeout;
+ init.fb_bpp = init32.fb_bpp;
+ init.front_offset = init32.front_offset;
+ init.front_pitch = init32.front_pitch;
+ init.back_offset = init32.back_offset;
+ init.back_pitch = init32.back_pitch;
+ init.depth_bpp = init32.depth_bpp;
+ init.depth_offset = init32.depth_offset;
+ init.depth_pitch = init32.depth_pitch;
+ init.span_offset = init32.span_offset;
+ init.fb_offset = init32.fb_offset;
+ init.mmio_offset = init32.mmio_offset;
+ init.ring_offset = init32.ring_offset;
+ init.ring_rptr_offset = init32.ring_rptr_offset;
+ init.buffers_offset = init32.buffers_offset;
+ init.agp_textures_offset = init32.agp_textures_offset;
+
+ return drm_ioctl_kernel(file, r128_cce_init, &init,
+ DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY);
+}
+
+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 depth;
+
+ if (copy_from_user(&depth32, (void __user *)arg, sizeof(depth32)))
+ return -EFAULT;
+
+ depth.func = depth32.func;
+ depth.n = depth32.n;
+ depth.x = compat_ptr(depth32.x);
+ depth.y = compat_ptr(depth32.y);
+ depth.buffer = compat_ptr(depth32.buffer);
+ depth.mask = compat_ptr(depth32.mask);
+
+ return drm_ioctl_kernel(file, r128_cce_depth, &depth, DRM_AUTH);
+}
+
+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 stipple;
+
+ if (copy_from_user(&stipple32, (void __user *)arg, sizeof(stipple32)))
+ return -EFAULT;
+
+ stipple.mask = compat_ptr(stipple32.mask);
+
+ return drm_ioctl_kernel(file, r128_cce_stipple, &stipple, DRM_AUTH);
+}
+
+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 getparam;
+
+ if (copy_from_user(&getparam32, (void __user *)arg, sizeof(getparam32)))
+ return -EFAULT;
+
+ getparam.param = getparam32.param;
+ getparam.value = compat_ptr(getparam32.value);
+
+ return drm_ioctl_kernel(file, r128_getparam, &getparam, DRM_AUTH);
+}
+
+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<n>.
+ *
+ * \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 + ARRAY_SIZE(r128_compat_ioctls))
+ fn = r128_compat_ioctls[nr - DRM_COMMAND_BASE];
+
+ if (fn != NULL)
+ ret = (*fn) (filp, cmd, arg);
+ else
+ ret = drm_ioctl(filp, cmd, arg);
+
+ 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 000000000..d84e9c96e
--- /dev/null
+++ b/drivers/gpu/drm/r128/r128_irq.c
@@ -0,0 +1,118 @@
+/* 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 <keith@tungstengraphics.com>
+ * Eric Anholt <anholt@FreeBSD.org>
+ */
+
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <drm/r128_drm.h>
+
+#include "r128_drv.h"
+
+u32 r128_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
+{
+ const drm_r128_private_t *dev_priv = dev->dev_private;
+
+ if (pipe != 0)
+ return 0;
+
+ return atomic_read(&dev_priv->vbl_received);
+}
+
+irqreturn_t r128_driver_irq_handler(int irq, void *arg)
+{
+ 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_priv->vbl_received);
+ drm_handle_vblank(dev, 0);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+int r128_enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+ drm_r128_private_t *dev_priv = dev->dev_private;
+
+ if (pipe != 0) {
+ DRM_ERROR("%s: bad crtc %u\n", __func__, pipe);
+ return -EINVAL;
+ }
+
+ R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN);
+ return 0;
+}
+
+void r128_disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+ if (pipe != 0)
+ DRM_ERROR("%s: bad crtc %u\n", __func__, pipe);
+
+ /*
+ * FIXME: implement proper interrupt disable by using the vblank
+ * counter register (if available)
+ *
+ * R128_WRITE(R128_GEN_INT_CNTL,
+ * R128_READ(R128_GEN_INT_CNTL) & ~R128_CRTC_VBLANK_INT_EN);
+ */
+}
+
+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);
+}
+
+int r128_driver_irq_postinstall(struct drm_device *dev)
+{
+ return 0;
+}
+
+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 000000000..9d74c9d91
--- /dev/null
+++ b/drivers/gpu/drm/r128/r128_state.c
@@ -0,0 +1,1640 @@
+/* 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 <gareth@valinux.com>
+ */
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <drm/drm_print.h>
+#include <drm/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",
+ task_pid_nr(current), 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 (copy_from_user(&x, depth->x, sizeof(x)))
+ return -EFAULT;
+ if (copy_from_user(&y, depth->y, sizeof(y)))
+ return -EFAULT;
+
+ buffer_size = depth->n * sizeof(u32);
+ buffer = memdup_user(depth->buffer, buffer_size);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
+
+ mask_size = depth->n;
+ if (depth->mask) {
+ mask = memdup_user(depth->mask, mask_size);
+ if (IS_ERR(mask)) {
+ kfree(buffer);
+ return PTR_ERR(mask);
+ }
+
+ 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();
+ }
+ }
+
+ kfree(mask);
+ } 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();
+ }
+ }
+
+ kfree(buffer);
+
+ 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 = memdup_user(depth->x, xbuf_size);
+ if (IS_ERR(x))
+ return PTR_ERR(x);
+ y = memdup_user(depth->y, ybuf_size);
+ if (IS_ERR(y)) {
+ kfree(x);
+ return PTR_ERR(y);
+ }
+ buffer_size = depth->n * sizeof(u32);
+ buffer = memdup_user(depth->buffer, buffer_size);
+ if (IS_ERR(buffer)) {
+ kfree(x);
+ kfree(y);
+ return PTR_ERR(buffer);
+ }
+
+ if (depth->mask) {
+ mask_size = depth->n;
+ mask = memdup_user(depth->mask, mask_size);
+ if (IS_ERR(mask)) {
+ kfree(x);
+ kfree(y);
+ kfree(buffer);
+ return PTR_ERR(mask);
+ }
+
+ 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();
+ }
+ }
+
+ kfree(mask);
+ } 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();
+ }
+ }
+
+ kfree(x);
+ kfree(y);
+ kfree(buffer);
+
+ 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 (copy_from_user(&x, depth->x, sizeof(x)))
+ return -EFAULT;
+ if (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 = kmalloc(xbuf_size, GFP_KERNEL);
+ if (x == NULL)
+ return -ENOMEM;
+ y = kmalloc(ybuf_size, GFP_KERNEL);
+ if (y == NULL) {
+ kfree(x);
+ return -ENOMEM;
+ }
+ if (copy_from_user(x, depth->x, xbuf_size)) {
+ kfree(x);
+ kfree(y);
+ return -EFAULT;
+ }
+ if (copy_from_user(y, depth->y, ybuf_size)) {
+ kfree(x);
+ kfree(y);
+ 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();
+ }
+
+ kfree(x);
+ kfree(y);
+
+ 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;
+ drm_r128_clear_t *clear = data;
+ DRM_DEBUG("\n");
+
+ LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ RING_SPACE_TEST_WITH_RETURN(dev_priv);
+
+ sarea_priv = dev_priv->sarea_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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ DRM_DEBUG("pid=%d index=%d count=%d discard=%d\n",
+ task_pid_nr(current), 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",
+ task_pid_nr(current), 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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ DRM_DEBUG("pid=%d buf=%d s=%d e=%d d=%d\n", task_pid_nr(current),
+ 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",
+ task_pid_nr(current), 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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ DRM_DEBUG("pid=%d index=%d\n", task_pid_nr(current), 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;
+}
+
+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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_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;
+}
+
+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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ if (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);
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ 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",
+ task_pid_nr(current), 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;
+}
+
+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;
+
+ DEV_INIT_TEST_WITH_RETURN(dev_priv);
+
+ DRM_DEBUG("pid=%d\n", task_pid_nr(current));
+
+ switch (param->param) {
+ case R128_PARAM_IRQ_NR:
+ value = dev->pdev->irq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (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);
+}
+
+const struct drm_ioctl_desc r128_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(R128_INIT, r128_cce_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(R128_CCE_START, r128_cce_start, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(R128_CCE_STOP, r128_cce_stop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(R128_CCE_RESET, r128_cce_reset, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(R128_CCE_IDLE, r128_cce_idle, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_RESET, r128_engine_reset, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_FULLSCREEN, r128_fullscreen, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_SWAP, r128_cce_swap, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_FLIP, r128_cce_flip, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_CLEAR, r128_cce_clear, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_VERTEX, r128_cce_vertex, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_INDICES, r128_cce_indices, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_BLIT, r128_cce_blit, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_DEPTH, r128_cce_depth, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_STIPPLE, r128_cce_stipple, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(R128_INDIRECT, r128_cce_indirect, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(R128_GETPARAM, r128_getparam, DRM_AUTH),
+};
+
+int r128_max_ioctl = ARRAY_SIZE(r128_ioctls);