18c2ecf20Sopenharmony_ci/* r128_cce.c -- ATI Rage 128 driver -*- linux-c -*-
28c2ecf20Sopenharmony_ci * Created: Wed Apr  5 19:24:19 2000 by kevin@precisioninsight.com
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci/*
58c2ecf20Sopenharmony_ci * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
68c2ecf20Sopenharmony_ci * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
78c2ecf20Sopenharmony_ci * All Rights Reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
108c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
118c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
128c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
138c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
148c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
178c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
188c2ecf20Sopenharmony_ci * Software.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
218c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
228c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
238c2ecf20Sopenharmony_ci * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
248c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
258c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
268c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * Authors:
298c2ecf20Sopenharmony_ci *    Gareth Hughes <gareth@valinux.com>
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <linux/delay.h>
338c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
348c2ecf20Sopenharmony_ci#include <linux/firmware.h>
358c2ecf20Sopenharmony_ci#include <linux/module.h>
368c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
378c2ecf20Sopenharmony_ci#include <linux/slab.h>
388c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <drm/drm_agpsupport.h>
418c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
428c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
438c2ecf20Sopenharmony_ci#include <drm/drm_irq.h>
448c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
458c2ecf20Sopenharmony_ci#include <drm/r128_drm.h>
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#include "r128_drv.h"
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define R128_FIFO_DEBUG		0
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define FIRMWARE_NAME		"r128/r128_cce.bin"
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_NAME);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int R128_READ_PLL(struct drm_device *dev, int addr)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f);
608c2ecf20Sopenharmony_ci	return R128_READ(R128_CLOCK_CNTL_DATA);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#if R128_FIFO_DEBUG
648c2ecf20Sopenharmony_cistatic void r128_status(drm_r128_private_t *dev_priv)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	printk("GUI_STAT           = 0x%08x\n",
678c2ecf20Sopenharmony_ci	       (unsigned int)R128_READ(R128_GUI_STAT));
688c2ecf20Sopenharmony_ci	printk("PM4_STAT           = 0x%08x\n",
698c2ecf20Sopenharmony_ci	       (unsigned int)R128_READ(R128_PM4_STAT));
708c2ecf20Sopenharmony_ci	printk("PM4_BUFFER_DL_WPTR = 0x%08x\n",
718c2ecf20Sopenharmony_ci	       (unsigned int)R128_READ(R128_PM4_BUFFER_DL_WPTR));
728c2ecf20Sopenharmony_ci	printk("PM4_BUFFER_DL_RPTR = 0x%08x\n",
738c2ecf20Sopenharmony_ci	       (unsigned int)R128_READ(R128_PM4_BUFFER_DL_RPTR));
748c2ecf20Sopenharmony_ci	printk("PM4_MICRO_CNTL     = 0x%08x\n",
758c2ecf20Sopenharmony_ci	       (unsigned int)R128_READ(R128_PM4_MICRO_CNTL));
768c2ecf20Sopenharmony_ci	printk("PM4_BUFFER_CNTL    = 0x%08x\n",
778c2ecf20Sopenharmony_ci	       (unsigned int)R128_READ(R128_PM4_BUFFER_CNTL));
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci#endif
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* ================================================================
828c2ecf20Sopenharmony_ci * Engine, FIFO control
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int r128_do_pixcache_flush(drm_r128_private_t *dev_priv)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	u32 tmp;
888c2ecf20Sopenharmony_ci	int i;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	tmp = R128_READ(R128_PC_NGUI_CTLSTAT) | R128_PC_FLUSH_ALL;
918c2ecf20Sopenharmony_ci	R128_WRITE(R128_PC_NGUI_CTLSTAT, tmp);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	for (i = 0; i < dev_priv->usec_timeout; i++) {
948c2ecf20Sopenharmony_ci		if (!(R128_READ(R128_PC_NGUI_CTLSTAT) & R128_PC_BUSY))
958c2ecf20Sopenharmony_ci			return 0;
968c2ecf20Sopenharmony_ci		udelay(1);
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#if R128_FIFO_DEBUG
1008c2ecf20Sopenharmony_ci	DRM_ERROR("failed!\n");
1018c2ecf20Sopenharmony_ci#endif
1028c2ecf20Sopenharmony_ci	return -EBUSY;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int r128_do_wait_for_fifo(drm_r128_private_t *dev_priv, int entries)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	int i;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	for (i = 0; i < dev_priv->usec_timeout; i++) {
1108c2ecf20Sopenharmony_ci		int slots = R128_READ(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK;
1118c2ecf20Sopenharmony_ci		if (slots >= entries)
1128c2ecf20Sopenharmony_ci			return 0;
1138c2ecf20Sopenharmony_ci		udelay(1);
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci#if R128_FIFO_DEBUG
1178c2ecf20Sopenharmony_ci	DRM_ERROR("failed!\n");
1188c2ecf20Sopenharmony_ci#endif
1198c2ecf20Sopenharmony_ci	return -EBUSY;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int r128_do_wait_for_idle(drm_r128_private_t *dev_priv)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int i, ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = r128_do_wait_for_fifo(dev_priv, 64);
1278c2ecf20Sopenharmony_ci	if (ret)
1288c2ecf20Sopenharmony_ci		return ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	for (i = 0; i < dev_priv->usec_timeout; i++) {
1318c2ecf20Sopenharmony_ci		if (!(R128_READ(R128_GUI_STAT) & R128_GUI_ACTIVE)) {
1328c2ecf20Sopenharmony_ci			r128_do_pixcache_flush(dev_priv);
1338c2ecf20Sopenharmony_ci			return 0;
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci		udelay(1);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci#if R128_FIFO_DEBUG
1398c2ecf20Sopenharmony_ci	DRM_ERROR("failed!\n");
1408c2ecf20Sopenharmony_ci#endif
1418c2ecf20Sopenharmony_ci	return -EBUSY;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* ================================================================
1458c2ecf20Sopenharmony_ci * CCE control, initialization
1468c2ecf20Sopenharmony_ci */
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* Load the microcode for the CCE */
1498c2ecf20Sopenharmony_cistatic int r128_cce_load_microcode(drm_r128_private_t *dev_priv)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct platform_device *pdev;
1528c2ecf20Sopenharmony_ci	const struct firmware *fw;
1538c2ecf20Sopenharmony_ci	const __be32 *fw_data;
1548c2ecf20Sopenharmony_ci	int rc, i;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	pdev = platform_device_register_simple("r128_cce", 0, NULL, 0);
1598c2ecf20Sopenharmony_ci	if (IS_ERR(pdev)) {
1608c2ecf20Sopenharmony_ci		pr_err("r128_cce: Failed to register firmware\n");
1618c2ecf20Sopenharmony_ci		return PTR_ERR(pdev);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	rc = request_firmware(&fw, FIRMWARE_NAME, &pdev->dev);
1648c2ecf20Sopenharmony_ci	platform_device_unregister(pdev);
1658c2ecf20Sopenharmony_ci	if (rc) {
1668c2ecf20Sopenharmony_ci		pr_err("r128_cce: Failed to load firmware \"%s\"\n",
1678c2ecf20Sopenharmony_ci		       FIRMWARE_NAME);
1688c2ecf20Sopenharmony_ci		return rc;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (fw->size != 256 * 8) {
1728c2ecf20Sopenharmony_ci		pr_err("r128_cce: Bogus length %zu in firmware \"%s\"\n",
1738c2ecf20Sopenharmony_ci		       fw->size, FIRMWARE_NAME);
1748c2ecf20Sopenharmony_ci		rc = -EINVAL;
1758c2ecf20Sopenharmony_ci		goto out_release;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	r128_do_wait_for_idle(dev_priv);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	fw_data = (const __be32 *)fw->data;
1818c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_MICROCODE_ADDR, 0);
1828c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++) {
1838c2ecf20Sopenharmony_ci		R128_WRITE(R128_PM4_MICROCODE_DATAH,
1848c2ecf20Sopenharmony_ci			   be32_to_cpup(&fw_data[i * 2]));
1858c2ecf20Sopenharmony_ci		R128_WRITE(R128_PM4_MICROCODE_DATAL,
1868c2ecf20Sopenharmony_ci			   be32_to_cpup(&fw_data[i * 2 + 1]));
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciout_release:
1908c2ecf20Sopenharmony_ci	release_firmware(fw);
1918c2ecf20Sopenharmony_ci	return rc;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/* Flush any pending commands to the CCE.  This should only be used just
1958c2ecf20Sopenharmony_ci * prior to a wait for idle, as it informs the engine that the command
1968c2ecf20Sopenharmony_ci * stream is ending.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_cistatic void r128_do_cce_flush(drm_r128_private_t *dev_priv)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	u32 tmp;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	tmp = R128_READ(R128_PM4_BUFFER_DL_WPTR) | R128_PM4_BUFFER_DL_DONE;
2038c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_DL_WPTR, tmp);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* Wait for the CCE to go idle.
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_ciint r128_do_cce_idle(drm_r128_private_t *dev_priv)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int i;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	for (i = 0; i < dev_priv->usec_timeout; i++) {
2138c2ecf20Sopenharmony_ci		if (GET_RING_HEAD(dev_priv) == dev_priv->ring.tail) {
2148c2ecf20Sopenharmony_ci			int pm4stat = R128_READ(R128_PM4_STAT);
2158c2ecf20Sopenharmony_ci			if (((pm4stat & R128_PM4_FIFOCNT_MASK) >=
2168c2ecf20Sopenharmony_ci			     dev_priv->cce_fifo_size) &&
2178c2ecf20Sopenharmony_ci			    !(pm4stat & (R128_PM4_BUSY |
2188c2ecf20Sopenharmony_ci					 R128_PM4_GUI_ACTIVE))) {
2198c2ecf20Sopenharmony_ci				return r128_do_pixcache_flush(dev_priv);
2208c2ecf20Sopenharmony_ci			}
2218c2ecf20Sopenharmony_ci		}
2228c2ecf20Sopenharmony_ci		udelay(1);
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci#if R128_FIFO_DEBUG
2268c2ecf20Sopenharmony_ci	DRM_ERROR("failed!\n");
2278c2ecf20Sopenharmony_ci	r128_status(dev_priv);
2288c2ecf20Sopenharmony_ci#endif
2298c2ecf20Sopenharmony_ci	return -EBUSY;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/* Start the Concurrent Command Engine.
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic void r128_do_cce_start(drm_r128_private_t *dev_priv)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	r128_do_wait_for_idle(dev_priv);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_CNTL,
2398c2ecf20Sopenharmony_ci		   dev_priv->cce_mode | dev_priv->ring.size_l2qw
2408c2ecf20Sopenharmony_ci		   | R128_PM4_BUFFER_CNTL_NOUPDATE);
2418c2ecf20Sopenharmony_ci	R128_READ(R128_PM4_BUFFER_ADDR);	/* as per the sample code */
2428c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	dev_priv->cce_running = 1;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci/* Reset the Concurrent Command Engine.  This will not flush any pending
2488c2ecf20Sopenharmony_ci * commands, so you must wait for the CCE command stream to complete
2498c2ecf20Sopenharmony_ci * before calling this routine.
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_cistatic void r128_do_cce_reset(drm_r128_private_t *dev_priv)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_DL_WPTR, 0);
2548c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_DL_RPTR, 0);
2558c2ecf20Sopenharmony_ci	dev_priv->ring.tail = 0;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/* Stop the Concurrent Command Engine.  This will not flush any pending
2598c2ecf20Sopenharmony_ci * commands, so you must flush the command stream and wait for the CCE
2608c2ecf20Sopenharmony_ci * to go idle before calling this routine.
2618c2ecf20Sopenharmony_ci */
2628c2ecf20Sopenharmony_cistatic void r128_do_cce_stop(drm_r128_private_t *dev_priv)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_MICRO_CNTL, 0);
2658c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_CNTL,
2668c2ecf20Sopenharmony_ci		   R128_PM4_NONPM4 | R128_PM4_BUFFER_CNTL_NOUPDATE);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	dev_priv->cce_running = 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/* Reset the engine.  This will stop the CCE if it is running.
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_cistatic int r128_do_engine_reset(struct drm_device *dev)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
2768c2ecf20Sopenharmony_ci	u32 clock_cntl_index, mclk_cntl, gen_reset_cntl;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	r128_do_pixcache_flush(dev_priv);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	clock_cntl_index = R128_READ(R128_CLOCK_CNTL_INDEX);
2818c2ecf20Sopenharmony_ci	mclk_cntl = R128_READ_PLL(dev, R128_MCLK_CNTL);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	R128_WRITE_PLL(R128_MCLK_CNTL,
2848c2ecf20Sopenharmony_ci		       mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	gen_reset_cntl = R128_READ(R128_GEN_RESET_CNTL);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Taken from the sample code - do not change */
2898c2ecf20Sopenharmony_ci	R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl | R128_SOFT_RESET_GUI);
2908c2ecf20Sopenharmony_ci	R128_READ(R128_GEN_RESET_CNTL);
2918c2ecf20Sopenharmony_ci	R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl & ~R128_SOFT_RESET_GUI);
2928c2ecf20Sopenharmony_ci	R128_READ(R128_GEN_RESET_CNTL);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	R128_WRITE_PLL(R128_MCLK_CNTL, mclk_cntl);
2958c2ecf20Sopenharmony_ci	R128_WRITE(R128_CLOCK_CNTL_INDEX, clock_cntl_index);
2968c2ecf20Sopenharmony_ci	R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Reset the CCE ring */
2998c2ecf20Sopenharmony_ci	r128_do_cce_reset(dev_priv);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* The CCE is no longer running after an engine reset */
3028c2ecf20Sopenharmony_ci	dev_priv->cce_running = 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Reset any pending vertex, indirect buffers */
3058c2ecf20Sopenharmony_ci	r128_freelist_reset(dev);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic void r128_cce_init_ring_buffer(struct drm_device *dev,
3118c2ecf20Sopenharmony_ci				      drm_r128_private_t *dev_priv)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	u32 ring_start;
3148c2ecf20Sopenharmony_ci	u32 tmp;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* The manual (p. 2) says this address is in "VM space".  This
3198c2ecf20Sopenharmony_ci	 * means it's an offset from the start of AGP space.
3208c2ecf20Sopenharmony_ci	 */
3218c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
3228c2ecf20Sopenharmony_ci	if (!dev_priv->is_pci)
3238c2ecf20Sopenharmony_ci		ring_start = dev_priv->cce_ring->offset - dev->agp->base;
3248c2ecf20Sopenharmony_ci	else
3258c2ecf20Sopenharmony_ci#endif
3268c2ecf20Sopenharmony_ci		ring_start = dev_priv->cce_ring->offset -
3278c2ecf20Sopenharmony_ci		    (unsigned long)dev->sg->virtual;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_OFFSET, ring_start | R128_AGP_OFFSET);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_DL_WPTR, 0);
3328c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_DL_RPTR, 0);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* Set watermark control */
3358c2ecf20Sopenharmony_ci	R128_WRITE(R128_PM4_BUFFER_WM_CNTL,
3368c2ecf20Sopenharmony_ci		   ((R128_WATERMARK_L / 4) << R128_WMA_SHIFT)
3378c2ecf20Sopenharmony_ci		   | ((R128_WATERMARK_M / 4) << R128_WMB_SHIFT)
3388c2ecf20Sopenharmony_ci		   | ((R128_WATERMARK_N / 4) << R128_WMC_SHIFT)
3398c2ecf20Sopenharmony_ci		   | ((R128_WATERMARK_K / 64) << R128_WB_WM_SHIFT));
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* Force read.  Why?  Because it's in the examples... */
3428c2ecf20Sopenharmony_ci	R128_READ(R128_PM4_BUFFER_ADDR);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Turn on bus mastering */
3458c2ecf20Sopenharmony_ci	tmp = R128_READ(R128_BUS_CNTL) & ~R128_BUS_MASTER_DIS;
3468c2ecf20Sopenharmony_ci	R128_WRITE(R128_BUS_CNTL, tmp);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv;
3528c2ecf20Sopenharmony_ci	int rc;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (dev->dev_private) {
3578c2ecf20Sopenharmony_ci		DRM_DEBUG("called when already initialized\n");
3588c2ecf20Sopenharmony_ci		return -EINVAL;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	dev_priv = kzalloc(sizeof(drm_r128_private_t), GFP_KERNEL);
3628c2ecf20Sopenharmony_ci	if (dev_priv == NULL)
3638c2ecf20Sopenharmony_ci		return -ENOMEM;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	dev_priv->is_pci = init->is_pci;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (dev_priv->is_pci && !dev->sg) {
3688c2ecf20Sopenharmony_ci		DRM_ERROR("PCI GART memory not allocated!\n");
3698c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
3708c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
3718c2ecf20Sopenharmony_ci		return -EINVAL;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	dev_priv->usec_timeout = init->usec_timeout;
3758c2ecf20Sopenharmony_ci	if (dev_priv->usec_timeout < 1 ||
3768c2ecf20Sopenharmony_ci	    dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT) {
3778c2ecf20Sopenharmony_ci		DRM_DEBUG("TIMEOUT problem!\n");
3788c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
3798c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
3808c2ecf20Sopenharmony_ci		return -EINVAL;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	dev_priv->cce_mode = init->cce_mode;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	/* GH: Simple idle check.
3868c2ecf20Sopenharmony_ci	 */
3878c2ecf20Sopenharmony_ci	atomic_set(&dev_priv->idle_count, 0);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* We don't support anything other than bus-mastering ring mode,
3908c2ecf20Sopenharmony_ci	 * but the ring can be in either AGP or PCI space for the ring
3918c2ecf20Sopenharmony_ci	 * read pointer.
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	if ((init->cce_mode != R128_PM4_192BM) &&
3948c2ecf20Sopenharmony_ci	    (init->cce_mode != R128_PM4_128BM_64INDBM) &&
3958c2ecf20Sopenharmony_ci	    (init->cce_mode != R128_PM4_64BM_128INDBM) &&
3968c2ecf20Sopenharmony_ci	    (init->cce_mode != R128_PM4_64BM_64VCBM_64INDBM)) {
3978c2ecf20Sopenharmony_ci		DRM_DEBUG("Bad cce_mode!\n");
3988c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
3998c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
4008c2ecf20Sopenharmony_ci		return -EINVAL;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	switch (init->cce_mode) {
4048c2ecf20Sopenharmony_ci	case R128_PM4_NONPM4:
4058c2ecf20Sopenharmony_ci		dev_priv->cce_fifo_size = 0;
4068c2ecf20Sopenharmony_ci		break;
4078c2ecf20Sopenharmony_ci	case R128_PM4_192PIO:
4088c2ecf20Sopenharmony_ci	case R128_PM4_192BM:
4098c2ecf20Sopenharmony_ci		dev_priv->cce_fifo_size = 192;
4108c2ecf20Sopenharmony_ci		break;
4118c2ecf20Sopenharmony_ci	case R128_PM4_128PIO_64INDBM:
4128c2ecf20Sopenharmony_ci	case R128_PM4_128BM_64INDBM:
4138c2ecf20Sopenharmony_ci		dev_priv->cce_fifo_size = 128;
4148c2ecf20Sopenharmony_ci		break;
4158c2ecf20Sopenharmony_ci	case R128_PM4_64PIO_128INDBM:
4168c2ecf20Sopenharmony_ci	case R128_PM4_64BM_128INDBM:
4178c2ecf20Sopenharmony_ci	case R128_PM4_64PIO_64VCBM_64INDBM:
4188c2ecf20Sopenharmony_ci	case R128_PM4_64BM_64VCBM_64INDBM:
4198c2ecf20Sopenharmony_ci	case R128_PM4_64PIO_64VCPIO_64INDPIO:
4208c2ecf20Sopenharmony_ci		dev_priv->cce_fifo_size = 64;
4218c2ecf20Sopenharmony_ci		break;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	switch (init->fb_bpp) {
4258c2ecf20Sopenharmony_ci	case 16:
4268c2ecf20Sopenharmony_ci		dev_priv->color_fmt = R128_DATATYPE_RGB565;
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	case 32:
4298c2ecf20Sopenharmony_ci	default:
4308c2ecf20Sopenharmony_ci		dev_priv->color_fmt = R128_DATATYPE_ARGB8888;
4318c2ecf20Sopenharmony_ci		break;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	dev_priv->front_offset = init->front_offset;
4348c2ecf20Sopenharmony_ci	dev_priv->front_pitch = init->front_pitch;
4358c2ecf20Sopenharmony_ci	dev_priv->back_offset = init->back_offset;
4368c2ecf20Sopenharmony_ci	dev_priv->back_pitch = init->back_pitch;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	switch (init->depth_bpp) {
4398c2ecf20Sopenharmony_ci	case 16:
4408c2ecf20Sopenharmony_ci		dev_priv->depth_fmt = R128_DATATYPE_RGB565;
4418c2ecf20Sopenharmony_ci		break;
4428c2ecf20Sopenharmony_ci	case 24:
4438c2ecf20Sopenharmony_ci	case 32:
4448c2ecf20Sopenharmony_ci	default:
4458c2ecf20Sopenharmony_ci		dev_priv->depth_fmt = R128_DATATYPE_ARGB8888;
4468c2ecf20Sopenharmony_ci		break;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	dev_priv->depth_offset = init->depth_offset;
4498c2ecf20Sopenharmony_ci	dev_priv->depth_pitch = init->depth_pitch;
4508c2ecf20Sopenharmony_ci	dev_priv->span_offset = init->span_offset;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	dev_priv->front_pitch_offset_c = (((dev_priv->front_pitch / 8) << 21) |
4538c2ecf20Sopenharmony_ci					  (dev_priv->front_offset >> 5));
4548c2ecf20Sopenharmony_ci	dev_priv->back_pitch_offset_c = (((dev_priv->back_pitch / 8) << 21) |
4558c2ecf20Sopenharmony_ci					 (dev_priv->back_offset >> 5));
4568c2ecf20Sopenharmony_ci	dev_priv->depth_pitch_offset_c = (((dev_priv->depth_pitch / 8) << 21) |
4578c2ecf20Sopenharmony_ci					  (dev_priv->depth_offset >> 5) |
4588c2ecf20Sopenharmony_ci					  R128_DST_TILE);
4598c2ecf20Sopenharmony_ci	dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch / 8) << 21) |
4608c2ecf20Sopenharmony_ci					 (dev_priv->span_offset >> 5));
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	dev_priv->sarea = drm_legacy_getsarea(dev);
4638c2ecf20Sopenharmony_ci	if (!dev_priv->sarea) {
4648c2ecf20Sopenharmony_ci		DRM_ERROR("could not find sarea!\n");
4658c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
4668c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
4678c2ecf20Sopenharmony_ci		return -EINVAL;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset);
4718c2ecf20Sopenharmony_ci	if (!dev_priv->mmio) {
4728c2ecf20Sopenharmony_ci		DRM_ERROR("could not find mmio region!\n");
4738c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
4748c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
4758c2ecf20Sopenharmony_ci		return -EINVAL;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	dev_priv->cce_ring = drm_legacy_findmap(dev, init->ring_offset);
4788c2ecf20Sopenharmony_ci	if (!dev_priv->cce_ring) {
4798c2ecf20Sopenharmony_ci		DRM_ERROR("could not find cce ring region!\n");
4808c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
4818c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
4828c2ecf20Sopenharmony_ci		return -EINVAL;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci	dev_priv->ring_rptr = drm_legacy_findmap(dev, init->ring_rptr_offset);
4858c2ecf20Sopenharmony_ci	if (!dev_priv->ring_rptr) {
4868c2ecf20Sopenharmony_ci		DRM_ERROR("could not find ring read pointer!\n");
4878c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
4888c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
4898c2ecf20Sopenharmony_ci		return -EINVAL;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci	dev->agp_buffer_token = init->buffers_offset;
4928c2ecf20Sopenharmony_ci	dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset);
4938c2ecf20Sopenharmony_ci	if (!dev->agp_buffer_map) {
4948c2ecf20Sopenharmony_ci		DRM_ERROR("could not find dma buffer region!\n");
4958c2ecf20Sopenharmony_ci		dev->dev_private = (void *)dev_priv;
4968c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
4978c2ecf20Sopenharmony_ci		return -EINVAL;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (!dev_priv->is_pci) {
5018c2ecf20Sopenharmony_ci		dev_priv->agp_textures =
5028c2ecf20Sopenharmony_ci		    drm_legacy_findmap(dev, init->agp_textures_offset);
5038c2ecf20Sopenharmony_ci		if (!dev_priv->agp_textures) {
5048c2ecf20Sopenharmony_ci			DRM_ERROR("could not find agp texture region!\n");
5058c2ecf20Sopenharmony_ci			dev->dev_private = (void *)dev_priv;
5068c2ecf20Sopenharmony_ci			r128_do_cleanup_cce(dev);
5078c2ecf20Sopenharmony_ci			return -EINVAL;
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	dev_priv->sarea_priv =
5128c2ecf20Sopenharmony_ci	    (drm_r128_sarea_t *) ((u8 *) dev_priv->sarea->handle +
5138c2ecf20Sopenharmony_ci				  init->sarea_priv_offset);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
5168c2ecf20Sopenharmony_ci	if (!dev_priv->is_pci) {
5178c2ecf20Sopenharmony_ci		drm_legacy_ioremap_wc(dev_priv->cce_ring, dev);
5188c2ecf20Sopenharmony_ci		drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev);
5198c2ecf20Sopenharmony_ci		drm_legacy_ioremap_wc(dev->agp_buffer_map, dev);
5208c2ecf20Sopenharmony_ci		if (!dev_priv->cce_ring->handle ||
5218c2ecf20Sopenharmony_ci		    !dev_priv->ring_rptr->handle ||
5228c2ecf20Sopenharmony_ci		    !dev->agp_buffer_map->handle) {
5238c2ecf20Sopenharmony_ci			DRM_ERROR("Could not ioremap agp regions!\n");
5248c2ecf20Sopenharmony_ci			dev->dev_private = (void *)dev_priv;
5258c2ecf20Sopenharmony_ci			r128_do_cleanup_cce(dev);
5268c2ecf20Sopenharmony_ci			return -ENOMEM;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci	} else
5298c2ecf20Sopenharmony_ci#endif
5308c2ecf20Sopenharmony_ci	{
5318c2ecf20Sopenharmony_ci		dev_priv->cce_ring->handle =
5328c2ecf20Sopenharmony_ci			(void *)(unsigned long)dev_priv->cce_ring->offset;
5338c2ecf20Sopenharmony_ci		dev_priv->ring_rptr->handle =
5348c2ecf20Sopenharmony_ci			(void *)(unsigned long)dev_priv->ring_rptr->offset;
5358c2ecf20Sopenharmony_ci		dev->agp_buffer_map->handle =
5368c2ecf20Sopenharmony_ci			(void *)(unsigned long)dev->agp_buffer_map->offset;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
5408c2ecf20Sopenharmony_ci	if (!dev_priv->is_pci)
5418c2ecf20Sopenharmony_ci		dev_priv->cce_buffers_offset = dev->agp->base;
5428c2ecf20Sopenharmony_ci	else
5438c2ecf20Sopenharmony_ci#endif
5448c2ecf20Sopenharmony_ci		dev_priv->cce_buffers_offset = (unsigned long)dev->sg->virtual;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	dev_priv->ring.start = (u32 *) dev_priv->cce_ring->handle;
5478c2ecf20Sopenharmony_ci	dev_priv->ring.end = ((u32 *) dev_priv->cce_ring->handle
5488c2ecf20Sopenharmony_ci			      + init->ring_size / sizeof(u32));
5498c2ecf20Sopenharmony_ci	dev_priv->ring.size = init->ring_size;
5508c2ecf20Sopenharmony_ci	dev_priv->ring.size_l2qw = order_base_2(init->ring_size / 8);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	dev_priv->ring.high_mark = 128;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	dev_priv->sarea_priv->last_frame = 0;
5578c2ecf20Sopenharmony_ci	R128_WRITE(R128_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	dev_priv->sarea_priv->last_dispatch = 0;
5608c2ecf20Sopenharmony_ci	R128_WRITE(R128_LAST_DISPATCH_REG, dev_priv->sarea_priv->last_dispatch);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
5638c2ecf20Sopenharmony_ci	if (dev_priv->is_pci) {
5648c2ecf20Sopenharmony_ci#endif
5658c2ecf20Sopenharmony_ci		dev_priv->gart_info.table_mask = DMA_BIT_MASK(32);
5668c2ecf20Sopenharmony_ci		dev_priv->gart_info.gart_table_location = DRM_ATI_GART_MAIN;
5678c2ecf20Sopenharmony_ci		dev_priv->gart_info.table_size = R128_PCIGART_TABLE_SIZE;
5688c2ecf20Sopenharmony_ci		dev_priv->gart_info.addr = NULL;
5698c2ecf20Sopenharmony_ci		dev_priv->gart_info.bus_addr = 0;
5708c2ecf20Sopenharmony_ci		dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI;
5718c2ecf20Sopenharmony_ci		rc = drm_ati_pcigart_init(dev, &dev_priv->gart_info);
5728c2ecf20Sopenharmony_ci		if (rc) {
5738c2ecf20Sopenharmony_ci			DRM_ERROR("failed to init PCI GART!\n");
5748c2ecf20Sopenharmony_ci			dev->dev_private = (void *)dev_priv;
5758c2ecf20Sopenharmony_ci			r128_do_cleanup_cce(dev);
5768c2ecf20Sopenharmony_ci			return rc;
5778c2ecf20Sopenharmony_ci		}
5788c2ecf20Sopenharmony_ci		R128_WRITE(R128_PCI_GART_PAGE, dev_priv->gart_info.bus_addr);
5798c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci#endif
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	r128_cce_init_ring_buffer(dev, dev_priv);
5848c2ecf20Sopenharmony_ci	rc = r128_cce_load_microcode(dev_priv);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	dev->dev_private = (void *)dev_priv;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	r128_do_engine_reset(dev);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (rc) {
5918c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to load firmware!\n");
5928c2ecf20Sopenharmony_ci		r128_do_cleanup_cce(dev);
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return rc;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ciint r128_do_cleanup_cce(struct drm_device *dev)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Make sure interrupts are disabled here because the uninstall ioctl
6028c2ecf20Sopenharmony_ci	 * may not have been called from userspace and after dev_private
6038c2ecf20Sopenharmony_ci	 * is freed, it's too late.
6048c2ecf20Sopenharmony_ci	 */
6058c2ecf20Sopenharmony_ci	if (dev->irq_enabled)
6068c2ecf20Sopenharmony_ci		drm_irq_uninstall(dev);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (dev->dev_private) {
6098c2ecf20Sopenharmony_ci		drm_r128_private_t *dev_priv = dev->dev_private;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
6128c2ecf20Sopenharmony_ci		if (!dev_priv->is_pci) {
6138c2ecf20Sopenharmony_ci			if (dev_priv->cce_ring != NULL)
6148c2ecf20Sopenharmony_ci				drm_legacy_ioremapfree(dev_priv->cce_ring, dev);
6158c2ecf20Sopenharmony_ci			if (dev_priv->ring_rptr != NULL)
6168c2ecf20Sopenharmony_ci				drm_legacy_ioremapfree(dev_priv->ring_rptr, dev);
6178c2ecf20Sopenharmony_ci			if (dev->agp_buffer_map != NULL) {
6188c2ecf20Sopenharmony_ci				drm_legacy_ioremapfree(dev->agp_buffer_map, dev);
6198c2ecf20Sopenharmony_ci				dev->agp_buffer_map = NULL;
6208c2ecf20Sopenharmony_ci			}
6218c2ecf20Sopenharmony_ci		} else
6228c2ecf20Sopenharmony_ci#endif
6238c2ecf20Sopenharmony_ci		{
6248c2ecf20Sopenharmony_ci			if (dev_priv->gart_info.bus_addr)
6258c2ecf20Sopenharmony_ci				if (!drm_ati_pcigart_cleanup(dev,
6268c2ecf20Sopenharmony_ci							&dev_priv->gart_info))
6278c2ecf20Sopenharmony_ci					DRM_ERROR
6288c2ecf20Sopenharmony_ci					    ("failed to cleanup PCI GART!\n");
6298c2ecf20Sopenharmony_ci		}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		kfree(dev->dev_private);
6328c2ecf20Sopenharmony_ci		dev->dev_private = NULL;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return 0;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ciint r128_cce_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	drm_r128_init_t *init = data;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	LOCK_TEST_WITH_RETURN(dev, file_priv);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	switch (init->func) {
6478c2ecf20Sopenharmony_ci	case R128_INIT_CCE:
6488c2ecf20Sopenharmony_ci		return r128_do_init_cce(dev, init);
6498c2ecf20Sopenharmony_ci	case R128_CLEANUP_CCE:
6508c2ecf20Sopenharmony_ci		return r128_do_cleanup_cce(dev);
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	return -EINVAL;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ciint r128_cce_start(struct drm_device *dev, void *data, struct drm_file *file_priv)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
6598c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	LOCK_TEST_WITH_RETURN(dev, file_priv);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	DEV_INIT_TEST_WITH_RETURN(dev_priv);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (dev_priv->cce_running || dev_priv->cce_mode == R128_PM4_NONPM4) {
6668c2ecf20Sopenharmony_ci		DRM_DEBUG("while CCE running\n");
6678c2ecf20Sopenharmony_ci		return 0;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	r128_do_cce_start(dev_priv);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	return 0;
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci/* Stop the CCE.  The engine must have been idled before calling this
6768c2ecf20Sopenharmony_ci * routine.
6778c2ecf20Sopenharmony_ci */
6788c2ecf20Sopenharmony_ciint r128_cce_stop(struct drm_device *dev, void *data, struct drm_file *file_priv)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
6818c2ecf20Sopenharmony_ci	drm_r128_cce_stop_t *stop = data;
6828c2ecf20Sopenharmony_ci	int ret;
6838c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	LOCK_TEST_WITH_RETURN(dev, file_priv);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	DEV_INIT_TEST_WITH_RETURN(dev_priv);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/* Flush any pending CCE commands.  This ensures any outstanding
6908c2ecf20Sopenharmony_ci	 * commands are exectuted by the engine before we turn it off.
6918c2ecf20Sopenharmony_ci	 */
6928c2ecf20Sopenharmony_ci	if (stop->flush)
6938c2ecf20Sopenharmony_ci		r128_do_cce_flush(dev_priv);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* If we fail to make the engine go idle, we return an error
6968c2ecf20Sopenharmony_ci	 * code so that the DRM ioctl wrapper can try again.
6978c2ecf20Sopenharmony_ci	 */
6988c2ecf20Sopenharmony_ci	if (stop->idle) {
6998c2ecf20Sopenharmony_ci		ret = r128_do_cce_idle(dev_priv);
7008c2ecf20Sopenharmony_ci		if (ret)
7018c2ecf20Sopenharmony_ci			return ret;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	/* Finally, we can turn off the CCE.  If the engine isn't idle,
7058c2ecf20Sopenharmony_ci	 * we will get some dropped triangles as they won't be fully
7068c2ecf20Sopenharmony_ci	 * rendered before the CCE is shut down.
7078c2ecf20Sopenharmony_ci	 */
7088c2ecf20Sopenharmony_ci	r128_do_cce_stop(dev_priv);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	/* Reset the engine */
7118c2ecf20Sopenharmony_ci	r128_do_engine_reset(dev);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	return 0;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci/* Just reset the CCE ring.  Called as part of an X Server engine reset.
7178c2ecf20Sopenharmony_ci */
7188c2ecf20Sopenharmony_ciint r128_cce_reset(struct drm_device *dev, void *data, struct drm_file *file_priv)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
7218c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	LOCK_TEST_WITH_RETURN(dev, file_priv);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	DEV_INIT_TEST_WITH_RETURN(dev_priv);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	r128_do_cce_reset(dev_priv);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* The CCE is no longer running after an engine reset */
7308c2ecf20Sopenharmony_ci	dev_priv->cce_running = 0;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	return 0;
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ciint r128_cce_idle(struct drm_device *dev, void *data, struct drm_file *file_priv)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
7388c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	LOCK_TEST_WITH_RETURN(dev, file_priv);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	DEV_INIT_TEST_WITH_RETURN(dev_priv);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (dev_priv->cce_running)
7458c2ecf20Sopenharmony_ci		r128_do_cce_flush(dev_priv);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	return r128_do_cce_idle(dev_priv);
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ciint r128_engine_reset(struct drm_device *dev, void *data, struct drm_file *file_priv)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	DRM_DEBUG("\n");
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	LOCK_TEST_WITH_RETURN(dev, file_priv);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	DEV_INIT_TEST_WITH_RETURN(dev->dev_private);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	return r128_do_engine_reset(dev);
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ciint r128_fullscreen(struct drm_device *dev, void *data, struct drm_file *file_priv)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	return -EINVAL;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci/* ================================================================
7678c2ecf20Sopenharmony_ci * Freelist management
7688c2ecf20Sopenharmony_ci */
7698c2ecf20Sopenharmony_ci#define R128_BUFFER_USED	0xffffffff
7708c2ecf20Sopenharmony_ci#define R128_BUFFER_FREE	0
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci#if 0
7738c2ecf20Sopenharmony_cistatic int r128_freelist_init(struct drm_device *dev)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
7768c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
7778c2ecf20Sopenharmony_ci	struct drm_buf *buf;
7788c2ecf20Sopenharmony_ci	drm_r128_buf_priv_t *buf_priv;
7798c2ecf20Sopenharmony_ci	drm_r128_freelist_t *entry;
7808c2ecf20Sopenharmony_ci	int i;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	dev_priv->head = kzalloc(sizeof(drm_r128_freelist_t), GFP_KERNEL);
7838c2ecf20Sopenharmony_ci	if (dev_priv->head == NULL)
7848c2ecf20Sopenharmony_ci		return -ENOMEM;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	dev_priv->head->age = R128_BUFFER_USED;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	for (i = 0; i < dma->buf_count; i++) {
7898c2ecf20Sopenharmony_ci		buf = dma->buflist[i];
7908c2ecf20Sopenharmony_ci		buf_priv = buf->dev_private;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci		entry = kmalloc(sizeof(drm_r128_freelist_t), GFP_KERNEL);
7938c2ecf20Sopenharmony_ci		if (!entry)
7948c2ecf20Sopenharmony_ci			return -ENOMEM;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci		entry->age = R128_BUFFER_FREE;
7978c2ecf20Sopenharmony_ci		entry->buf = buf;
7988c2ecf20Sopenharmony_ci		entry->prev = dev_priv->head;
7998c2ecf20Sopenharmony_ci		entry->next = dev_priv->head->next;
8008c2ecf20Sopenharmony_ci		if (!entry->next)
8018c2ecf20Sopenharmony_ci			dev_priv->tail = entry;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci		buf_priv->discard = 0;
8048c2ecf20Sopenharmony_ci		buf_priv->dispatched = 0;
8058c2ecf20Sopenharmony_ci		buf_priv->list_entry = entry;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci		dev_priv->head->next = entry;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci		if (dev_priv->head->next)
8108c2ecf20Sopenharmony_ci			dev_priv->head->next->prev = entry;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	return 0;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci#endif
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic struct drm_buf *r128_freelist_get(struct drm_device * dev)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
8218c2ecf20Sopenharmony_ci	drm_r128_private_t *dev_priv = dev->dev_private;
8228c2ecf20Sopenharmony_ci	drm_r128_buf_priv_t *buf_priv;
8238c2ecf20Sopenharmony_ci	struct drm_buf *buf;
8248c2ecf20Sopenharmony_ci	int i, t;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* FIXME: Optimize -- use freelist code */
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	for (i = 0; i < dma->buf_count; i++) {
8298c2ecf20Sopenharmony_ci		buf = dma->buflist[i];
8308c2ecf20Sopenharmony_ci		buf_priv = buf->dev_private;
8318c2ecf20Sopenharmony_ci		if (!buf->file_priv)
8328c2ecf20Sopenharmony_ci			return buf;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	for (t = 0; t < dev_priv->usec_timeout; t++) {
8368c2ecf20Sopenharmony_ci		u32 done_age = R128_READ(R128_LAST_DISPATCH_REG);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		for (i = 0; i < dma->buf_count; i++) {
8398c2ecf20Sopenharmony_ci			buf = dma->buflist[i];
8408c2ecf20Sopenharmony_ci			buf_priv = buf->dev_private;
8418c2ecf20Sopenharmony_ci			if (buf->pending && buf_priv->age <= done_age) {
8428c2ecf20Sopenharmony_ci				/* The buffer has been processed, so it
8438c2ecf20Sopenharmony_ci				 * can now be used.
8448c2ecf20Sopenharmony_ci				 */
8458c2ecf20Sopenharmony_ci				buf->pending = 0;
8468c2ecf20Sopenharmony_ci				return buf;
8478c2ecf20Sopenharmony_ci			}
8488c2ecf20Sopenharmony_ci		}
8498c2ecf20Sopenharmony_ci		udelay(1);
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	DRM_DEBUG("returning NULL!\n");
8538c2ecf20Sopenharmony_ci	return NULL;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_civoid r128_freelist_reset(struct drm_device *dev)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
8598c2ecf20Sopenharmony_ci	int i;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	for (i = 0; i < dma->buf_count; i++) {
8628c2ecf20Sopenharmony_ci		struct drm_buf *buf = dma->buflist[i];
8638c2ecf20Sopenharmony_ci		drm_r128_buf_priv_t *buf_priv = buf->dev_private;
8648c2ecf20Sopenharmony_ci		buf_priv->age = 0;
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci/* ================================================================
8698c2ecf20Sopenharmony_ci * CCE command submission
8708c2ecf20Sopenharmony_ci */
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ciint r128_wait_ring(drm_r128_private_t *dev_priv, int n)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	drm_r128_ring_buffer_t *ring = &dev_priv->ring;
8758c2ecf20Sopenharmony_ci	int i;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	for (i = 0; i < dev_priv->usec_timeout; i++) {
8788c2ecf20Sopenharmony_ci		r128_update_ring_snapshot(dev_priv);
8798c2ecf20Sopenharmony_ci		if (ring->space >= n)
8808c2ecf20Sopenharmony_ci			return 0;
8818c2ecf20Sopenharmony_ci		udelay(1);
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/* FIXME: This is being ignored... */
8858c2ecf20Sopenharmony_ci	DRM_ERROR("failed!\n");
8868c2ecf20Sopenharmony_ci	return -EBUSY;
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic int r128_cce_get_buffers(struct drm_device *dev,
8908c2ecf20Sopenharmony_ci				struct drm_file *file_priv,
8918c2ecf20Sopenharmony_ci				struct drm_dma *d)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	int i;
8948c2ecf20Sopenharmony_ci	struct drm_buf *buf;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	for (i = d->granted_count; i < d->request_count; i++) {
8978c2ecf20Sopenharmony_ci		buf = r128_freelist_get(dev);
8988c2ecf20Sopenharmony_ci		if (!buf)
8998c2ecf20Sopenharmony_ci			return -EAGAIN;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci		buf->file_priv = file_priv;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci		if (copy_to_user(&d->request_indices[i], &buf->idx,
9048c2ecf20Sopenharmony_ci				     sizeof(buf->idx)))
9058c2ecf20Sopenharmony_ci			return -EFAULT;
9068c2ecf20Sopenharmony_ci		if (copy_to_user(&d->request_sizes[i], &buf->total,
9078c2ecf20Sopenharmony_ci				     sizeof(buf->total)))
9088c2ecf20Sopenharmony_ci			return -EFAULT;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		d->granted_count++;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci	return 0;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ciint r128_cce_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv)
9168c2ecf20Sopenharmony_ci{
9178c2ecf20Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
9188c2ecf20Sopenharmony_ci	int ret = 0;
9198c2ecf20Sopenharmony_ci	struct drm_dma *d = data;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	LOCK_TEST_WITH_RETURN(dev, file_priv);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	/* Please don't send us buffers.
9248c2ecf20Sopenharmony_ci	 */
9258c2ecf20Sopenharmony_ci	if (d->send_count != 0) {
9268c2ecf20Sopenharmony_ci		DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n",
9278c2ecf20Sopenharmony_ci			  task_pid_nr(current), d->send_count);
9288c2ecf20Sopenharmony_ci		return -EINVAL;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	/* We'll send you buffers.
9328c2ecf20Sopenharmony_ci	 */
9338c2ecf20Sopenharmony_ci	if (d->request_count < 0 || d->request_count > dma->buf_count) {
9348c2ecf20Sopenharmony_ci		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
9358c2ecf20Sopenharmony_ci			  task_pid_nr(current), d->request_count, dma->buf_count);
9368c2ecf20Sopenharmony_ci		return -EINVAL;
9378c2ecf20Sopenharmony_ci	}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	d->granted_count = 0;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	if (d->request_count)
9428c2ecf20Sopenharmony_ci		ret = r128_cce_get_buffers(dev, file_priv, d);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	return ret;
9458c2ecf20Sopenharmony_ci}
946