[PATCH] WIP breakdown GPU into unit

Jerome Glisse jglisse at redhat.com
Mon Jul 12 12:14:26 PDT 2010


radeon GPU family have lot of similarity accross generations
and it can be isolated as unit performing one task (CP, MC,
HDP, ...). This patch try to reflect this by introducing the
unit infrastructure.

Each unit has 6 functions :
	- init one time initialization
	- fini one time finalization (cleanup + deallocation ....)
	- suspend suspend the unit stop it and put it in safe default
	- resume resume the unit (start it and test it works properly)
	- hold hold the unit activity for power management
	- unhold obvious

The main purpose for introducing this infrastructure is proper
unit reset and proper ordering in reset, initialization of the unit
the number of code path we have made the initialization and reset
order sometime tricky/confusing or different from one path to the
other.
---
 drivers/gpu/drm/radeon/Makefile      |    2 +-
 drivers/gpu/drm/radeon/r100.c        |    2 +-
 drivers/gpu/drm/radeon/radeon.h      |   70 +++-
 drivers/gpu/drm/radeon/radeon_asic.c |   20 +-
 drivers/gpu/drm/radeon/radeon_asic.h |    5 +
 drivers/gpu/drm/radeon/radeon_unit.c |  860 ++++++++++++++++++++++++++++++++++
 6 files changed, 946 insertions(+), 13 deletions(-)
 create mode 100644 drivers/gpu/drm/radeon/radeon_unit.c

diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index aebe008..56b0d6c 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -65,7 +65,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
 	rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
 	r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
 	r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \
-	evergreen.o evergreen_cs.o
+	evergreen.o evergreen_cs.o radeon_unit.o
 
 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
 radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 3970e62..cdc6eb9 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -3677,7 +3677,7 @@ static void r100_debugfs(struct radeon_device *rdev)
 		dev_warn(rdev->dev, "Failed to create r100_mc debugfs file.\n");
 }
 
-static void r100_mc_program(struct radeon_device *rdev)
+void r100_mc_program(struct radeon_device *rdev)
 {
 	struct r100_mc_save save;
 
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 55b83d7..51042ce 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -105,6 +105,70 @@ extern int radeon_hw_i2c;
 #define RADEONFB_CONN_LIMIT		4
 #define RADEON_BIOS_NUM_SCRATCH		8
 
+struct radeon_device;
+
+/* ASIC from different version often have similarities (CP is the
+ * same for r1xx to r5xx). In order to reflect this modularity we
+ * introduce the unit infrastructure which should help in asserting
+ * that we initialize & finalize GPU unit in proper order in all
+ * code path.
+ */
+struct radeon_unit_funcs;
+
+#define RADEON_UNIT_READY	(1 << 0)
+#define RADEON_UNIT_RUNNING	(1 << 1)
+
+struct radeon_unit {
+	unsigned			status;
+	int				error;
+	void				*data;
+	const struct radeon_unit_funcs	*funcs;
+};
+
+
+#define RADEON_UNIT_INIT_FATAL		(1 << 0)
+#define RADEON_UNIT_FINI_FATAL		(1 << 1)
+#define RADEON_UNIT_RESUME_FATAL	(1 << 2)
+#define RADEON_UNIT_SUSPEND_FATAL	(1 << 3)
+
+struct radeon_unit_funcs {
+	unsigned			id;
+	unsigned			flags;
+	const char			*name;
+	/**
+	 * Called once to initialize the unit RADEON_UNIT_READY is set
+	 * after successfull completion, should autoclean on error.
+	 */
+	int (*init)(struct radeon_device *rdev, struct radeon_unit *unit);
+	/**
+	 * Called once to finalize the unit RADEON_UNIT_READY is unset.
+	 */
+	int (*fini)(struct radeon_device *rdev, struct radeon_unit *unit);
+	/**
+	 * Suspend the unit (stop all work) RADEON_UNIT_RUNNING is unset
+	 * after sucessfull completion. In case of failure, fini is call.
+	 */
+	int (*suspend)(struct radeon_device *rdev, struct radeon_unit *unit);
+	/**
+	 * Resume the unit (stop all work) RADEON_UNIT_RUNNING is set
+	 * after sucessfull completion.
+	 */
+	int (*resume)(struct radeon_device *rdev, struct radeon_unit *unit);
+};
+
+unsigned radeon_unit_status(struct radeon_device *rdev, unsigned id);
+bool radeon_unit_depend_on(struct radeon_device *rdev, struct radeon_unit *unit,
+				unsigned id, unsigned status);
+int radeon_unit_init_all(struct radeon_device *rdev);
+int radeon_unit_fini_all(struct radeon_device *rdev);
+int radeon_unit_suspend_all(struct radeon_device *rdev);
+int radeon_unit_resume_all(struct radeon_device *rdev);
+int radeon_unit_init_up_to(struct radeon_device *rdev, unsigned id);
+int radeon_unit_fini_down_to(struct radeon_device *rdev, unsigned id);
+int radeon_unit_suspend_down_to(struct radeon_device *rdev, unsigned id);
+int radeon_unit_resume_up_to(struct radeon_device *rdev, unsigned id);
+
+
 /*
  * Errata workarounds.
  */
@@ -115,9 +179,6 @@ enum radeon_pll_errata {
 };
 
 
-struct radeon_device;
-
-
 /*
  * BIOS.
  */
@@ -872,6 +933,7 @@ struct radeon_asic {
 	void (*pm_finish)(struct radeon_device *rdev);
 	void (*pm_init_profile)(struct radeon_device *rdev);
 	void (*pm_get_dynpm_state)(struct radeon_device *rdev);
+	struct radeon_unit_funcs **units;
 };
 
 /*
@@ -1024,6 +1086,8 @@ struct radeon_device {
 	struct drm_device		*ddev;
 	struct pci_dev			*pdev;
 	/* ASIC */
+	unsigned			nunits;
+	struct radeon_unit		*units;
 	union radeon_asic_config	config;
 	enum radeon_family		family;
 	unsigned long			flags;
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index 646f96f..1cd3802 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -128,11 +128,13 @@ void radeon_agp_disable(struct radeon_device *rdev)
 /*
  * ASIC
  */
+extern struct radeon_unit_funcs **r100_units;
+
 static struct radeon_asic r100_asic = {
-	.init = &r100_init,
-	.fini = &r100_fini,
-	.suspend = &r100_suspend,
-	.resume = &r100_resume,
+	.init = &radeon_init_unit,
+	.fini = &radeon_fini_unit,
+	.suspend = &radeon_suspend_unit,
+	.resume = &radeon_resume_unit,
 	.vga_set_state = &r100_vga_set_state,
 	.gpu_is_lockup = &r100_gpu_is_lockup,
 	.asic_reset = &r100_asic_reset,
@@ -171,13 +173,14 @@ static struct radeon_asic r100_asic = {
 	.pm_finish = &r100_pm_finish,
 	.pm_init_profile = &r100_pm_init_profile,
 	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+	.units = &r100_units,
 };
 
 static struct radeon_asic r200_asic = {
-	.init = &r100_init,
-	.fini = &r100_fini,
-	.suspend = &r100_suspend,
-	.resume = &r100_resume,
+	.init = &radeon_init_unit,
+	.fini = &radeon_fini_unit,
+	.suspend = &radeon_suspend_unit,
+	.resume = &radeon_resume_unit,
 	.vga_set_state = &r100_vga_set_state,
 	.gpu_is_lockup = &r100_gpu_is_lockup,
 	.asic_reset = &r100_asic_reset,
@@ -215,6 +218,7 @@ static struct radeon_asic r200_asic = {
 	.pm_finish = &r100_pm_finish,
 	.pm_init_profile = &r100_pm_init_profile,
 	.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+	.units = &r100_units,
 };
 
 static struct radeon_asic r300_asic = {
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index c0bbaa6..7c08c79 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -319,4 +319,9 @@ extern void evergreen_pm_misc(struct radeon_device *rdev);
 extern void evergreen_pm_prepare(struct radeon_device *rdev);
 extern void evergreen_pm_finish(struct radeon_device *rdev);
 
+extern int radeon_init_unit(struct radeon_device *rdev);
+extern void radeon_fini_unit(struct radeon_device *rdev);
+extern int radeon_suspend_unit(struct radeon_device *rdev);
+extern int radeon_resume_unit(struct radeon_device *rdev);
+
 #endif
diff --git a/drivers/gpu/drm/radeon/radeon_unit.c b/drivers/gpu/drm/radeon/radeon_unit.c
new file mode 100644
index 0000000..d9e6617
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_unit.c
@@ -0,0 +1,860 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Jerome Glisse
+ */
+#include "drmP.h"
+#include "drm_sarea.h"
+#include "radeon.h"
+#include "radeon_drm.h"
+#include "radeon_asic.h"
+#include "r100d.h"
+
+enum radeon_unit_id {
+	/* General units */
+	RADEON_BIOS		= 0x0001,
+	RADEON_MM		= 0x0002,
+	RADEON_GEM		= 0x0003,
+	RADEON_SCRATCH		= 0x0004,
+	RADEON_SURFACE		= 0x0005,
+	RADEON_IRQ		= 0x0006,
+	RADEON_FENCE		= 0x0007,
+	RADEON_AGP		= 0x0008,
+	/* R100 ASIC units */
+	R100_ERRATA		= 0x1000,
+	R100_VGA_DISABLE	= 0x1001,
+	R100_COMMON		= 0x1002,
+	R100_CLOCK		= 0x0003,
+	R100_MC			= 0x1004,
+	R100_GART		= 0x1005,
+	R100_IRQ		= 0x1006,
+	R100_CS			= 0x1007,
+	R100_CP			= 0x1008,
+	R100_IB			= 0x1009,
+	R100_SR			= 0x1010,
+};
+
+/*
+ * VGA unit
+ */
+static int r100_unit_init_vga(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_vga_render_disable(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_vga = {
+	.id = R100_VGA_DISABLE,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "R100 VGA disable",
+	.init = r100_unit_init_vga,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * scratch registers
+ */
+static int radeon_unit_init_scratch(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_scratch_init(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_scratch = {
+	.id = RADEON_SCRATCH,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "scratch registers",
+	.init = radeon_unit_init_scratch,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * surface registers
+ */
+static int radeon_unit_init_surface(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_surface_init(rdev);
+	return 0;
+}
+
+static int radeon_unit_resume_surface(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_surface_init(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_surface = {
+	.id = RADEON_SURFACE,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "surface registers",
+	.init = radeon_unit_init_surface,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = radeon_unit_resume_surface,
+};
+
+/*
+ * BIOS
+ */
+static int radeon_unit_init_bios(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	int r;
+
+	if (!radeon_get_bios(rdev)) {
+		if (ASIC_IS_AVIVO(rdev))
+			return -EINVAL;
+	}
+	if (rdev->is_atom_bios) {
+		dev_err(rdev->dev, "Expecting combios for RS400/RS480 GPU\n");
+		return -EINVAL;
+	} else {
+		r = radeon_combios_init(rdev);
+		if (r)
+			return r;
+	}
+	/* Reset gpu before posting otherwise ATOM will enter infinite loop */
+	if (radeon_asic_reset(rdev)) {
+		dev_warn(rdev->dev,
+			"GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n",
+			RREG32(R_000E40_RBBM_STATUS),
+			RREG32(R_0007C0_CP_STAT));
+	}
+	/* check if cards are posted or not */
+	if (radeon_boot_test_post_card(rdev) == false)
+		return -EINVAL;
+	return 0;
+}
+
+static int radeon_unit_fini_bios(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_atombios_fini(rdev);
+	kfree(rdev->bios);
+	rdev->bios = NULL;
+	return 0;
+}
+
+static int radeon_unit_resume_bios(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	/* Reset gpu before posting otherwise ATOM will enter infinite loop */
+	if (radeon_asic_reset(rdev)) {
+		dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n",
+			RREG32(R_000E40_RBBM_STATUS),
+			RREG32(R_0007C0_CP_STAT));
+	}
+	/* post */
+	radeon_combios_asic_init(rdev->ddev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_bios = {
+	.id = RADEON_BIOS,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "BIOS",
+	.init = radeon_unit_init_bios,
+	.fini = radeon_unit_fini_bios,
+	.suspend = NULL,
+	.resume = radeon_unit_resume_bios,
+};
+
+/*
+ * errata registers
+ */
+extern void r100_errata(struct radeon_device *rdev);
+static int r100_unit_init_errata(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_errata(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_errata = {
+	.id = R100_ERRATA,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "R100 errata",
+	.init = r100_unit_init_errata,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * fence registers
+ */
+static int radeon_unit_init_fence(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	return radeon_fence_driver_init(rdev);
+}
+
+static int radeon_unit_fini_fence(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_fence_driver_fini(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_fence = {
+	.id = RADEON_FENCE,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "fence",
+	.init = radeon_unit_init_fence,
+	.fini = radeon_unit_fini_fence,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * IRQ
+ */
+static int radeon_unit_init_irq(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	return radeon_irq_kms_init(rdev);
+}
+
+static int radeon_unit_fini_irq(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_irq_kms_fini(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_irq = {
+	.id = RADEON_IRQ,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "IRQ",
+	.init = radeon_unit_init_irq,
+	.fini = radeon_unit_fini_irq,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * r100 IRQ
+ */
+static int r100_unit_resume_irq(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	/* Enable IRQ */
+	r100_irq_set(rdev);
+	rdev->config.r100.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+	return 0;
+}
+
+static int r100_unit_suspend_irq(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_irq_disable(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_irq = {
+	.id = R100_IRQ,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "R100 IRQ",
+	.init = NULL,
+	.fini = NULL,
+	.suspend = r100_unit_suspend_irq,
+	.resume = r100_unit_resume_irq,
+};
+
+/*
+ * GEM
+ */
+static int radeon_unit_fini_gem(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_gem_fini(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_gem = {
+	.id = RADEON_GEM,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "memory manager",
+	.init = NULL,
+	.fini = radeon_unit_fini_gem,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * memory manager
+ */
+static int radeon_unit_init_mm(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	return radeon_bo_init(rdev);
+}
+
+static int radeon_unit_fini_mm(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_bo_fini(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_mm = {
+	.id = RADEON_MM,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "memory manager",
+	.init = radeon_unit_init_mm,
+	.fini = radeon_unit_fini_mm,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * r100 gart
+ */
+static int r100_unit_init_gart(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (rdev->flags & RADEON_IS_AGP)
+		return 0;
+	return r100_pci_gart_init(rdev);
+}
+
+static int r100_unit_fini_gart(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (rdev->flags & RADEON_IS_AGP)
+		return 0;
+	r100_pci_gart_fini(rdev);
+	return 0;
+}
+
+static int r100_unit_resume_gart(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_enable_bm(rdev);
+	if (rdev->flags & RADEON_IS_AGP)
+		return 0;
+	return r100_pci_gart_enable(rdev);
+}
+
+static int r100_unit_suspend_gart(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (rdev->flags & RADEON_IS_PCI)
+		r100_pci_gart_disable(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_gart = {
+	.id = R100_GART,
+	.flags = 0,
+	.name = "R100 GART",
+	.init = r100_unit_init_gart,
+	.fini = r100_unit_fini_gart,
+	.suspend = r100_unit_suspend_gart,
+	.resume = r100_unit_resume_gart,
+};
+
+/*
+ * r100 command checker
+ */
+void r100_set_safe_registers(struct radeon_device *rdev);
+static int r100_unit_init_cs(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_set_safe_registers(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_cs = {
+	.id = R100_CS,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "R100 command checker",
+	.init = r100_unit_init_cs,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * agp registers
+ */
+static int radeon_unit_init_agp(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (!(rdev->flags & RADEON_IS_AGP))
+		return 0;
+	return radeon_agp_init(rdev);
+}
+
+static int radeon_unit_fini_agp(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (!(rdev->flags & RADEON_IS_AGP))
+		return 0;
+	radeon_agp_fini(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs radeon_unit_agp = {
+	.id = RADEON_AGP,
+	.flags = 0,
+	.name = "AGP",
+	.init = radeon_unit_init_agp,
+	.fini = radeon_unit_fini_agp,
+	.suspend = NULL,
+	.resume = NULL,
+};
+
+/*
+ * clock registers
+ */
+static int r100_unit_init_clock(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	radeon_get_clock_info(rdev->ddev);
+	return 0;
+}
+
+void r100_clock_startup(struct radeon_device *rdev);
+static int r100_unit_resume_clock(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_clock_startup(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_clock = {
+	.id = R100_CLOCK,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "GPU clock",
+	.init = r100_unit_init_clock,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = r100_unit_resume_clock,
+};
+
+/*
+ * r100 memory controller
+ */
+void r100_mc_init(struct radeon_device *rdev);
+static int r100_unit_init_mc(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_mc_init(rdev);
+	return 0;
+}
+
+void r100_mc_program(struct radeon_device *rdev);
+static int r100_unit_resume_mc(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_mc_program(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_mc = {
+	.id = R100_MC,
+	.flags = RADEON_UNIT_INIT_FATAL,
+	.name = "R100 memory controller",
+	.init = r100_unit_init_mc,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = r100_unit_resume_mc,
+};
+
+/*
+ * r100 common register
+ */
+void r100_clock_startup(struct radeon_device *rdev);
+static int r100_unit_resume_common(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_set_common_regs(rdev);
+	/* Resume clock before doing reset */
+	r100_clock_startup(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_common = {
+	.id = R100_COMMON,
+	.flags = 0,
+	.name = "R100 common registers",
+	.init = NULL,
+	.fini = NULL,
+	.suspend = NULL,
+	.resume = r100_unit_resume_common,
+};
+
+/*
+ * r100 CP
+ */
+static int r100_unit_fini_cp(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_cp_fini(rdev);
+	return 0;
+}
+
+static int r100_unit_resume_cp(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	return r100_cp_init(rdev, 1024 * 1024);
+}
+
+static int r100_unit_suspend_cp(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_cp_disable(rdev);
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_cp = {
+	.id = R100_CP,
+	.flags = 0,
+	.name = "R100 command processor",
+	.init = NULL,
+	.fini = r100_unit_fini_cp,
+	.suspend = r100_unit_suspend_cp,
+	.resume = r100_unit_resume_cp,
+};
+
+/*
+ * r100 IB
+ */
+static int r100_unit_fini_ib(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	r100_ib_fini(rdev);
+	return 0;
+}
+
+static int r100_unit_resume_ib(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (!radeon_unit_depend_on(rdev, unit, R100_CP, RADEON_UNIT_RUNNING))
+		return -EINVAL;
+	return r100_ib_init(rdev);
+}
+
+struct radeon_unit_funcs r100_unit_ib = {
+	.id = R100_IB,
+	.flags = 0,
+	.name = "R100 indirect command buffer",
+	.init = NULL,
+	.fini = r100_unit_fini_ib,
+	.suspend = NULL,
+	.resume = r100_unit_resume_ib,
+};
+
+/*
+ * r100 suspend/resume
+ */
+static int r100_unit_resume_sr(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	/* Make sur GART are not working */
+	if (rdev->flags & RADEON_IS_PCI)
+		r100_pci_gart_disable(rdev);
+	/* Resume clock before doing reset */
+	r100_clock_startup(rdev);
+	/* Reset gpu before posting otherwise ATOM will enter infinite loop */
+	if (radeon_asic_reset(rdev)) {
+		dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n",
+			RREG32(R_000E40_RBBM_STATUS),
+			RREG32(R_0007C0_CP_STAT));
+	}
+	/* post */
+	radeon_combios_asic_init(rdev->ddev);
+	return 0;
+}
+
+static int r100_unit_suspend_sr(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	return 0;
+}
+
+struct radeon_unit_funcs r100_unit_sr = {
+	.id = R100_SR,
+	.flags = 0,
+	.name = "R100 suspend/resume",
+	.init = NULL,
+	.fini = NULL,
+	.suspend = r100_unit_suspend_sr,
+	.resume = r100_unit_resume_sr,
+};
+
+struct radeon_unit_funcs *r100_units[] = {
+	&r100_unit_errata,
+	&r100_unit_vga,
+	&r100_unit_common,
+	&radeon_unit_scratch,
+	&radeon_unit_surface,
+	&radeon_unit_bios,
+	&r100_unit_clock,
+	&r100_unit_mc,
+	&radeon_unit_fence,
+	&radeon_unit_irq,
+	&radeon_unit_mm,
+	&radeon_unit_agp,
+	&r100_unit_gart,
+	&r100_unit_irq,
+	&radeon_unit_gem,
+	&r100_unit_cs,
+	&r100_unit_cp,
+	&r100_unit_ib,
+	NULL
+};
+
+int radeon_init_unit(struct radeon_device *rdev)
+{
+	int i, r;
+
+	/* allocate unit structure & initialize them */
+	for (i = 0; rdev->asic->units[i]; i++);
+	rdev->nunits = i;
+	rdev->units = kzalloc(sizeof(struct radeon_unit) * rdev->nunits, GFP_KERNEL);
+	if (rdev->units == NULL)
+		return -ENOMEM;
+	for (i = 0; i < rdev->nunits; i++) {
+		rdev->units[i].funcs = rdev->asic->units[i];
+	}
+	r = radeon_unit_init_all(rdev);
+	if (r)
+		return r;
+	return radeon_unit_resume_all(rdev);
+}
+
+void radeon_fini_unit(struct radeon_device *rdev)
+{
+	radeon_unit_suspend_all(rdev);
+	radeon_unit_fini_all(rdev);
+}
+
+int radeon_suspend_unit(struct radeon_device *rdev)
+{
+	return radeon_unit_suspend_all(rdev);
+	return 0;
+}
+
+int radeon_resume_unit(struct radeon_device *rdev)
+{
+	return radeon_unit_resume_all(rdev);
+	return 0;
+}
+
+/*
+ * Unit helpers
+ */
+static int radeon_unit_init(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	DRM_INFO("init unit 0x%08X\n", unit->funcs->id);
+	if (unlikely(unit->status & RADEON_UNIT_READY)) {
+		DRM_ERROR("radeon: unit %s already initialized\n", unit->funcs->name);
+		return -EINVAL;
+	}
+	if (unit->funcs->init)
+		unit->error = unit->funcs->init(rdev, unit);
+	if (unit->error) {
+		DRM_ERROR("radeon: error initializing %s\n", unit->funcs->name);
+	} else {
+		unit->status |= RADEON_UNIT_READY;
+	}
+	return 0;
+}
+
+static int radeon_unit_fini(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (unlikely(!(unit->status & RADEON_UNIT_READY))) {
+		DRM_ERROR("radeon: unit %s already finalized\n", unit->funcs->name);
+		return -EINVAL;
+	}
+	if (unlikely(unit->status & RADEON_UNIT_RUNNING)) {
+		DRM_ERROR("radeon: unit %s in use\n", unit->funcs->name);
+		return -EINVAL;
+	}
+	if (unit->funcs->fini)
+		unit->error = unit->funcs->fini(rdev, unit);
+	if (unit->error) {
+		DRM_ERROR("radeon: error finalizing %s\n", unit->funcs->name);
+	} else {
+		unit->status ^= RADEON_UNIT_READY;
+	}
+	return 0;
+}
+
+static int radeon_unit_suspend(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (unlikely(!(unit->status & RADEON_UNIT_RUNNING))) {
+		DRM_ERROR("radeon: unit %s already suspended\n", unit->funcs->name);
+		return -EINVAL;
+	}
+	if (unit->funcs->suspend)
+		unit->error = unit->funcs->suspend(rdev, unit);
+	if (unit->error) {
+		DRM_ERROR("radeon: error suspending %s\n", unit->funcs->name);
+	} else {
+		unit->status ^= RADEON_UNIT_RUNNING;
+	}
+	return 0;
+}
+
+static int radeon_unit_resume(struct radeon_device *rdev, struct radeon_unit *unit)
+{
+	if (unlikely(!(unit->status & RADEON_UNIT_READY))) {
+		DRM_ERROR("radeon: unit %s not ready\n", unit->funcs->name);
+		return -EINVAL;
+	}
+	if (unlikely(unit->status & RADEON_UNIT_RUNNING)) {
+		DRM_ERROR("radeon: unit %s already running\n", unit->funcs->name);
+		return -EINVAL;
+	}
+	if (unit->funcs->resume)
+		unit->error = unit->funcs->resume(rdev, unit);
+	if (unit->error) {
+		DRM_ERROR("radeon: error resuming %s\n", unit->funcs->name);
+	} else {
+		unit->status |= RADEON_UNIT_RUNNING;
+	}
+	return 0;
+}
+
+unsigned radeon_unit_status(struct radeon_device *rdev, unsigned id)
+{
+	int i;
+
+	for (i = 0; i < rdev->nunits; i++) {
+		if (rdev->units[i].funcs->id == id)
+			return rdev->units[i].status;
+	}
+	return 0;
+}
+
+bool radeon_unit_depend_on(struct radeon_device *rdev, struct radeon_unit *unit,
+				unsigned id, unsigned status)
+{
+	int i;
+
+	for (i = 0; i < rdev->nunits; i++) {
+		if (rdev->units[i].funcs->id == id) {
+			if (!(rdev->units[i].status & status)) {
+				DRM_ERROR("radeon: %s needs %s\n",
+					unit->funcs->name,
+					rdev->units[i].funcs->name);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+int radeon_unit_init_all(struct radeon_device *rdev)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = 0; i < rdev->nunits; i++) {
+		radeon_unit_init(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_INIT_FATAL)) {
+			return units[i].error;
+		}
+	}
+	return 0;
+}
+
+int radeon_unit_fini_all(struct radeon_device *rdev)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = rdev->nunits - 1; i >= 0; i--) {
+		radeon_unit_fini(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_FINI_FATAL)) {
+			return units[i].error;
+		}
+	}
+	return 0;
+}
+
+int radeon_unit_suspend_all(struct radeon_device *rdev)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = rdev->nunits - 1; i >= 0; i--) {
+		radeon_unit_suspend(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_SUSPEND_FATAL)) {
+			return units[i].error;
+		}
+	}
+	return 0;
+}
+
+int radeon_unit_resume_all(struct radeon_device *rdev)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = 0; i < rdev->nunits; i++) {
+		radeon_unit_resume(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_RESUME_FATAL)) {
+			return units[i].error;
+		}
+	}
+	return 0;
+}
+
+int radeon_unit_init_up_to(struct radeon_device *rdev, unsigned id)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = 0; i < rdev->nunits; i++) {
+		radeon_unit_init(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_RESUME_FATAL)) {
+			return units[i].error;
+		}
+		if (units[i].funcs->id == id)
+			return 0;
+	}
+	return 0;
+}
+
+int radeon_unit_fini_down_to(struct radeon_device *rdev, unsigned id)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = rdev->nunits - 1; i >= 0; i--) {
+		radeon_unit_fini(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_FINI_FATAL)) {
+			return units[i].error;
+		}
+		if (units[i].funcs->id == id)
+			return 0;
+	}
+	return 0;
+}
+
+int radeon_unit_suspend_down_to(struct radeon_device *rdev, unsigned id)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = rdev->nunits - 1; i >= 0; i--) {
+		radeon_unit_suspend(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_SUSPEND_FATAL)) {
+			return units[i].error;
+		}
+		if (units[i].funcs->id == id)
+			return 0;
+	}
+	return 0;
+}
+
+int radeon_unit_resume_up_to(struct radeon_device *rdev, unsigned id)
+{
+	struct radeon_unit *units = rdev->units;
+	int i;
+
+	for (i = 0; i < rdev->nunits; i++) {
+		radeon_unit_resume(rdev, &units[i]);
+		if (units[i].error && (units[i].funcs->flags & RADEON_UNIT_RESUME_FATAL)) {
+			return units[i].error;
+		}
+		if (units[i].funcs->id == id)
+			return 0;
+	}
+	return 0;
+}
+
-- 
1.7.0.1


--------------000203050201060207040109--


More information about the dri-devel mailing list