diff mbox

[6/8] drm/amd: add ACP driver support

Message ID 1444320760-21936-7-git-send-email-alexander.deucher@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Deucher Oct. 8, 2015, 4:12 p.m. UTC
From: Maruthi Bayyavarapu <maruthi.bayyavarapu@amd.com>

This adds the ACP (Audio CoProcessor) IP driver and wires
it up to the amdgpu driver.  The ACP block provides the DMA
engine for i2s based ALSA driver. This is required for audio
on APUs that utilize an i2s codec.

Reviewed-by: Jammy Zhou <Jammy.Zhou@amd.com>
Reviewed-by: Maruthi Bayyavarapu <maruthi.bayyavarapu@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Reviewed-by: Murali Krishna Vemuri <murali-krishna.vemuri@amd.com>
Signed-off-by: Maruthi Bayyavarapu <maruthi.bayyavarapu@amd.com>
Signed-off-by: Chunming Zhou <david1.zhou@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
---

v2: integrate i2s/az check patch
v3: s/amd_acp/amdgpu_acp/
v4: update copyright notice
v5: squash multiple patches, convert to mfd
v6: major changes as below :
    1. Pass ACP register base to DMA and dw i2s drivers
       as IORESOURCE_MEM resources.
    2. add dw i2s as a new mfd cell.
v7: specify broken out dw quirks that apply to AMD hardware

 drivers/gpu/drm/Kconfig                      |   2 +
 drivers/gpu/drm/amd/acp/Kconfig              |  10 +
 drivers/gpu/drm/amd/acp/Makefile             |   9 +
 drivers/gpu/drm/amd/acp/acp_hw.c             | 127 +++++++++++++
 drivers/gpu/drm/amd/acp/include/acp_gfx_if.h |  49 +++++
 drivers/gpu/drm/amd/amdgpu/Makefile          |  13 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu.h          |  12 ++
 drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c      | 275 +++++++++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h      |  41 ++++
 drivers/gpu/drm/amd/amdgpu/vi.c              |  12 ++
 drivers/gpu/drm/amd/include/amd_shared.h     |   1 +
 include/linux/mfd/amd_acp.h                  |  43 +++++
 12 files changed, 593 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/amd/acp/Kconfig
 create mode 100644 drivers/gpu/drm/amd/acp/Makefile
 create mode 100644 drivers/gpu/drm/amd/acp/acp_hw.c
 create mode 100644 drivers/gpu/drm/amd/acp/include/acp_gfx_if.h
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
 create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h
 create mode 100644 include/linux/mfd/amd_acp.h

Comments

Mark Brown Oct. 22, 2015, 3:15 p.m. UTC | #1
On Thu, Oct 08, 2015 at 12:12:39PM -0400, Alex Deucher wrote:

> +config DRM_AMD_ACP
> +       bool "Enable ACP IP support"
> +       default y
> +       depends on MFD_CORE
> +       help
> +	Choose this option to enable ACP IP support for AMD SOCs.

Why does this depend on rather than select MFD_CORE (as all other users
do)?  Since MFD_CORE has no help text and hence is not directly user
selectable the above means the user won't be able to enable this driver
unless they happen to enable something else which uses MFD which doesn't
seem ideal.
diff mbox

Patch

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 1a0a8df..330e9fb 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -161,6 +161,8 @@  config DRM_AMDGPU
 
 source "drivers/gpu/drm/amd/amdgpu/Kconfig"
 
+source "drivers/gpu/drm/amd/acp/Kconfig"
+
 source "drivers/gpu/drm/nouveau/Kconfig"
 
 config DRM_I810
diff --git a/drivers/gpu/drm/amd/acp/Kconfig b/drivers/gpu/drm/amd/acp/Kconfig
new file mode 100644
index 0000000..1de4fe7
--- /dev/null
+++ b/drivers/gpu/drm/amd/acp/Kconfig
@@ -0,0 +1,10 @@ 
+menu "ACP Configuration"
+
+config DRM_AMD_ACP
+       bool "Enable ACP IP support"
+       default y
+       depends on MFD_CORE
+       help
+	Choose this option to enable ACP IP support for AMD SOCs.
+
+endmenu
diff --git a/drivers/gpu/drm/amd/acp/Makefile b/drivers/gpu/drm/amd/acp/Makefile
new file mode 100644
index 0000000..c8c3303
--- /dev/null
+++ b/drivers/gpu/drm/amd/acp/Makefile
@@ -0,0 +1,9 @@ 
+#
+# Makefile for the ACP, which is a sub-component
+# of AMDSOC/AMDGPU drm driver.
+# It provides the HW control for ACP related functionalities.
+
+ccflags-y += -Idrivers/gpu/drm/amd/include/asic_reg/acp
+subdir-ccflags-y += -I$(AMDACPPATH)/ -I$(AMDACPPATH)/include
+
+AMD_ACP_FILES := $(AMDACPPATH)/acp_hw.o
diff --git a/drivers/gpu/drm/amd/acp/acp_hw.c b/drivers/gpu/drm/amd/acp/acp_hw.c
new file mode 100644
index 0000000..55220c3
--- /dev/null
+++ b/drivers/gpu/drm/amd/acp/acp_hw.c
@@ -0,0 +1,127 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, 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.
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+
+#include "acp_gfx_if.h"
+
+#define ACP_MODE_I2S	0
+#define ACP_MODE_AZ	1
+
+#define VISLANDS30_IV_SRCID_ACP 0x000000a2
+#define mmACP_AZALIA_I2S_SELECT 0x51d4
+
+static int irq_set_source(void *private_data, unsigned src_id, unsigned type,
+								int enabled)
+{
+	struct acp_irq_prv *idata = private_data;
+
+	if (src_id == VISLANDS30_IV_SRCID_ACP) {
+		idata->enable_intr(idata->acp_mmio, enabled);
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+static int irq_handler(void *private_data, unsigned src_id,
+		       const uint32_t *iv_entry)
+{
+	struct acp_irq_prv *idata = private_data;
+
+	if (src_id == VISLANDS30_IV_SRCID_ACP)
+		return idata->irq_handler(idata->dev);
+	else
+		return -1;
+}
+
+static void acp_irq_register(struct amd_acp_device *acp_dev, void *iprv)
+{
+	struct amd_acp_private *acp_prv = (struct amd_acp_private *)acp_dev;
+
+	cgs_add_irq_source(acp_prv->cgs_device, VISLANDS30_IV_SRCID_ACP, 1,
+			   irq_set_source, irq_handler, iprv);
+}
+
+static void acp_irq_get(struct amd_acp_device *acp_dev)
+{
+	struct amd_acp_private *acp_prv = (struct amd_acp_private *)acp_dev;
+
+	cgs_irq_get(acp_prv->cgs_device, VISLANDS30_IV_SRCID_ACP, 0);
+}
+
+static void acp_irq_put(struct amd_acp_device *acp_dev)
+{
+	struct amd_acp_private *acp_prv = (struct amd_acp_private *)acp_dev;
+
+	cgs_irq_put(acp_prv->cgs_device, VISLANDS30_IV_SRCID_ACP, 0);
+}
+
+void amd_acp_resume(struct amd_acp_private *acp_private)
+{
+
+}
+
+
+void amd_acp_suspend(struct amd_acp_private *acp_private)
+{
+
+}
+
+int amd_acp_hw_init(void *cgs_device,
+		    unsigned acp_version_major, unsigned acp_version_minor,
+		    struct amd_acp_private **acp_private)
+{
+	unsigned int acp_mode = ACP_MODE_I2S;
+
+	if ((acp_version_major == 2) && (acp_version_minor == 2))
+		acp_mode = cgs_read_register(cgs_device,
+					mmACP_AZALIA_I2S_SELECT);
+
+	if (acp_mode != ACP_MODE_I2S)
+		return -ENODEV;
+
+	*acp_private = kzalloc(sizeof(struct amd_acp_private), GFP_KERNEL);
+	if (*acp_private == NULL)
+		return -ENOMEM;
+
+	(*acp_private)->cgs_device = cgs_device;
+	(*acp_private)->acp_version_major = acp_version_major;
+	(*acp_private)->acp_version_minor = acp_version_minor;
+
+	(*acp_private)->public.irq_register = acp_irq_register;
+	(*acp_private)->public.irq_get = acp_irq_get;
+	(*acp_private)->public.irq_put = acp_irq_put;
+
+	return 0;
+}
+
+int amd_acp_hw_fini(struct amd_acp_private *acp_private)
+{
+	kfree(acp_private);
+	return 0;
+}
diff --git a/drivers/gpu/drm/amd/acp/include/acp_gfx_if.h b/drivers/gpu/drm/amd/acp/include/acp_gfx_if.h
new file mode 100644
index 0000000..d6988c2
--- /dev/null
+++ b/drivers/gpu/drm/amd/acp/include/acp_gfx_if.h
@@ -0,0 +1,49 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, 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.
+ *
+*/
+
+#ifndef _ACP_GFX_IF_H
+#define _ACP_GFX_IF_H
+
+#include <linux/types.h>
+#include <linux/mfd/amd_acp.h>
+#include "cgs_linux.h"
+#include "cgs_common.h"
+
+struct amd_acp_private {
+	/* The public struture is first, so that pointers can be cast
+	 * between the public and private structure */
+	struct amd_acp_device public;
+
+	/* private elements not expose through the bus interface */
+	void *cgs_device;
+	unsigned acp_version_major, acp_version_minor;
+};
+
+int amd_acp_hw_init(void *cgs_device,
+		    unsigned acp_version_major, unsigned acp_version_minor,
+		    struct amd_acp_private **apriv);
+int amd_acp_hw_fini(struct amd_acp_private *apriv);
+void amd_acp_suspend(struct amd_acp_private *acp_private);
+void amd_acp_resume(struct amd_acp_private *acp_private);
+
+#endif /* _ACP_GFX_IF_H */
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index 04c2707..44de879 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -5,7 +5,8 @@ 
 ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/amd/include/asic_reg \
 	-Idrivers/gpu/drm/amd/include \
 	-Idrivers/gpu/drm/amd/amdgpu \
-	-Idrivers/gpu/drm/amd/scheduler
+	-Idrivers/gpu/drm/amd/scheduler \
+	-Idrivers/gpu/drm/amd/acp/include
 
 amdgpu-y := amdgpu_drv.o
 
@@ -89,6 +90,16 @@  amdgpu-y += \
 	../scheduler/sched_fence.o \
 	amdgpu_sched.o
 
+# ACP componet
+ifneq ($(CONFIG_DRM_AMD_ACP),)
+amdgpu-y += amdgpu_acp.o
+
+AMDACPPATH := ../acp
+include drivers/gpu/drm/amd/acp/Makefile
+
+amdgpu-y += $(AMD_ACP_FILES)
+endif
+
 amdgpu-$(CONFIG_COMPAT) += amdgpu_ioc32.o
 amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o
 amdgpu-$(CONFIG_ACPI) += amdgpu_acpi.o
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 6647fb2..1871831 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -52,6 +52,7 @@ 
 #include "amdgpu_irq.h"
 #include "amdgpu_ucode.h"
 #include "amdgpu_gds.h"
+#include "amdgpu_acp.h"
 
 #include "gpu_scheduler.h"
 
@@ -1928,6 +1929,13 @@  void amdgpu_cgs_destroy_device(void *cgs_device);
 
 
 /*
+ * CGS
+ */
+void *amdgpu_cgs_create_device(struct amdgpu_device *adev);
+void amdgpu_cgs_destroy_device(void *cgs_device);
+
+
+/*
  * Core structure, functions and helpers.
  */
 typedef uint32_t (*amdgpu_rreg_t)(struct amdgpu_device*, uint32_t);
@@ -1948,6 +1956,10 @@  struct amdgpu_device {
 	struct pci_dev			*pdev;
 	struct rw_semaphore		exclusive_lock;
 
+#ifdef CONFIG_DRM_AMD_ACP
+	struct amdgpu_acp		acp;
+#endif
+
 	/* ASIC */
 	enum amd_asic_type		asic_type;
 	uint32_t			family;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
new file mode 100644
index 0000000..0eec03d
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
@@ -0,0 +1,275 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, 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: AMD
+ *
+ */
+
+#include <sound/designware_i2s.h>
+#include <sound/pcm.h>
+
+#include "amdgpu.h"
+#include "atom.h"
+#include "amdgpu_acp.h"
+
+#include "acp_gfx_if.h"
+
+#define ACP_DMA_REGS_END	0x146c0
+#define ACP_I2S_PLAY_REGS_START	0x14840
+#define ACP_I2S_PLAY_REGS_END	0x148b4
+#define ACP_I2S_CAP_REGS_START	0x148b8
+#define ACP_I2S_CAP_REGS_END	0x1496c
+
+#define ACP_I2S_COMP1_REG_OFFSET 0x124
+#define ACP_I2S_COMP2_REG_OFFSET 0x120
+
+static int acp_early_init(void *handle)
+{
+	return 0;
+}
+
+static int acp_sw_init(void *handle)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	adev->acp.parent = adev->dev;
+
+	adev->acp.cgs_device =
+		amdgpu_cgs_create_device(adev);
+	if (!adev->acp.cgs_device)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int acp_sw_fini(void *handle)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	if (adev->acp.cgs_device)
+		amdgpu_cgs_destroy_device(adev->acp.cgs_device);
+
+	return 0;
+}
+
+/**
+ * acp_hw_init - start and test UVD block
+ *
+ * @adev: amdgpu_device pointer
+ *
+ */
+static int acp_hw_init(void *handle)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+	int r;
+	uint64_t acp_base;
+	struct i2s_platform_data *i2s_pdata;
+
+	const struct amdgpu_ip_block_version *ip_version =
+		amdgpu_get_ip_block(adev, AMD_IP_BLOCK_TYPE_ACP);
+
+	if (!ip_version)
+		return -EINVAL;
+
+	r = amd_acp_hw_init(adev->acp.cgs_device,
+			    ip_version->major, ip_version->minor,
+			    &adev->acp.private);
+	/* -ENODEV means board uses AZ rather than ACP */
+	if (r == -ENODEV)
+		return 0;
+	else if (r)
+		return r;
+
+	r = cgs_get_pci_resource(adev->acp.cgs_device, CGS_RESOURCE_TYPE_MMIO,
+			0x5289, 0, &acp_base);
+	if (r == -ENODEV)
+		return 0;
+	else if (r)
+		return r;
+
+	adev->acp.acp_cell = kzalloc(sizeof(struct mfd_cell) * 2, GFP_KERNEL);
+
+	if (adev->acp.acp_cell == NULL)
+		return -ENOMEM;
+
+	adev->acp.acp_res = kzalloc(sizeof(struct resource) * 3, GFP_KERNEL);
+
+	if (adev->acp.acp_res == NULL) {
+		kfree(adev->acp.acp_cell);
+		return -ENOMEM;
+	}
+
+	i2s_pdata = kzalloc(sizeof(struct i2s_platform_data), GFP_KERNEL);
+	if (i2s_pdata == NULL) {
+		kfree(adev->acp.acp_res);
+		kfree(adev->acp.acp_cell);
+		return -ENOMEM;
+	}
+
+	i2s_pdata->quirks = DW_I2S_QUIRK_MULTI_DWC |
+				DW_I2S_QUIRK_COMP_REG_OFFSET;
+	i2s_pdata->cap = DWC_I2S_PLAY | DWC_I2S_RECORD;
+	i2s_pdata->snd_rates = SNDRV_PCM_RATE_8000_96000;
+	i2s_pdata->i2s_reg_comp1 = ACP_I2S_COMP1_REG_OFFSET;
+	i2s_pdata->i2s_reg_comp2 = ACP_I2S_COMP2_REG_OFFSET;
+
+	adev->acp.acp_res[0].name = "acp2x_dma";
+	adev->acp.acp_res[0].flags = IORESOURCE_MEM;
+	adev->acp.acp_res[0].start = acp_base;
+	adev->acp.acp_res[0].end = acp_base + ACP_DMA_REGS_END;
+
+	adev->acp.acp_res[1].name = "acp2x_dw_i2s_play";
+	adev->acp.acp_res[1].flags = IORESOURCE_MEM;
+	adev->acp.acp_res[1].start = acp_base + ACP_I2S_PLAY_REGS_START;
+	adev->acp.acp_res[1].end = acp_base + ACP_I2S_PLAY_REGS_END;
+
+	adev->acp.acp_res[2].name = "acp2x_dw_i2s_cap";
+	adev->acp.acp_res[2].flags = IORESOURCE_MEM;
+	adev->acp.acp_res[2].start = acp_base + ACP_I2S_CAP_REGS_START;
+	adev->acp.acp_res[2].end = acp_base + ACP_I2S_CAP_REGS_END;
+
+	adev->acp.acp_cell[0].name = "acp_audio_dma";
+	adev->acp.acp_cell[0].num_resources = 1;
+	adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0];
+	adev->acp.acp_cell[0].platform_data = adev->acp.private;
+	adev->acp.acp_cell[0].pdata_size = sizeof(struct amd_acp_private);
+
+	adev->acp.acp_cell[1].name = "designware-i2s";
+	adev->acp.acp_cell[1].num_resources = 2;
+	adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
+	adev->acp.acp_cell[1].platform_data = i2s_pdata;
+	adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);
+
+	r = mfd_add_hotplug_devices(adev->acp.parent, adev->acp.acp_cell, 2);
+
+	if (r) {
+		amd_acp_hw_fini(adev->acp.private);
+		return r;
+	}
+	return 0;
+}
+
+/**
+ * acp_hw_fini - stop the hardware block
+ *
+ * @adev: amdgpu_device pointer
+ *
+ */
+static int acp_hw_fini(void *handle)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	if (adev->acp.private) {
+		amd_acp_hw_fini(adev->acp.private);
+		mfd_remove_devices(adev->acp.parent);
+		kfree(adev->acp.acp_res);
+		kfree(adev->acp.acp_cell);
+	}
+
+	return 0;
+}
+
+static int acp_suspend(void *handle)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	if (adev->acp.private)
+		amd_acp_suspend(adev->acp.private);
+
+	return 0;
+}
+
+static int acp_resume(void *handle)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	if (adev->acp.private)
+		amd_acp_resume(adev->acp.private);
+
+	return 0;
+}
+
+static bool acp_is_idle(void *handle)
+{
+	return true;
+}
+
+static int acp_wait_for_idle(void *handle)
+{
+	return 0;
+}
+
+static int acp_soft_reset(void *handle)
+{
+	return 0;
+}
+
+static void acp_print_status(void *handle)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	dev_info(adev->dev, "ACP STATUS\n");
+}
+
+static int acp_set_clockgating_state(void *handle,
+				     enum amd_clockgating_state state)
+{
+	return 0;
+}
+
+static int acp_set_powergating_state(void *handle,
+				     enum amd_powergating_state state)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+	/* This doesn't actually powergate the ACP block.
+	 * That's done in the dpm code via the SMC.  This
+	 * just re-inits the block as necessary.  The actual
+	 * gating still happens in the dpm code.  We should
+	 * revisit this when there is a cleaner line between
+	 * the smc and the hw blocks
+	 */
+	if (state == AMD_PG_STATE_GATE) {
+		if (adev->acp.private)
+			amd_acp_suspend(adev->acp.private);
+	} else {
+		if (adev->acp.private)
+			amd_acp_resume(adev->acp.private);
+	}
+	return 0;
+}
+
+const struct amd_ip_funcs acp_ip_funcs = {
+	.early_init = acp_early_init,
+	.late_init = NULL,
+	.sw_init = acp_sw_init,
+	.sw_fini = acp_sw_fini,
+	.hw_init = acp_hw_init,
+	.hw_fini = acp_hw_fini,
+	.suspend = acp_suspend,
+	.resume = acp_resume,
+	.is_idle = acp_is_idle,
+	.wait_for_idle = acp_wait_for_idle,
+	.soft_reset = acp_soft_reset,
+	.print_status = acp_print_status,
+	.set_clockgating_state = acp_set_clockgating_state,
+	.set_powergating_state = acp_set_powergating_state,
+};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h
new file mode 100644
index 0000000..24952ed
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.h
@@ -0,0 +1,41 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, 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: AMD
+ *
+ */
+
+#ifndef __AMDGPU_ACP_H__
+#define __AMDGPU_ACP_H__
+
+#include <linux/mfd/core.h>
+
+struct amdgpu_acp {
+	struct device *parent;
+	void *cgs_device;
+	struct amd_acp_private *private;
+	struct mfd_cell *acp_cell;
+	struct resource *acp_res;
+};
+
+extern const struct amd_ip_funcs acp_ip_funcs;
+
+#endif /* __AMDGPU_ACP_H__ */
diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c
index b55ceb1..ffd8727 100644
--- a/drivers/gpu/drm/amd/amdgpu/vi.c
+++ b/drivers/gpu/drm/amd/amdgpu/vi.c
@@ -71,6 +71,9 @@ 
 #include "uvd_v5_0.h"
 #include "uvd_v6_0.h"
 #include "vce_v3_0.h"
+#if defined(CONFIG_DRM_AMD_ACP)
+#include "amdgpu_acp.h"
+#endif
 
 /*
  * Indirect registers accessor
@@ -1298,6 +1301,15 @@  static const struct amdgpu_ip_block_version cz_ip_blocks[] =
 		.rev = 0,
 		.funcs = &vce_v3_0_ip_funcs,
 	},
+#if defined(CONFIG_DRM_AMD_ACP)
+	{
+		.type = AMD_IP_BLOCK_TYPE_ACP,
+		.major = 2,
+		.minor = 2,
+		.rev = 0,
+		.funcs = &acp_ip_funcs,
+	},
+#endif
 };
 
 int vi_set_ip_blocks(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h
index 68a8eaa..54bf96a 100644
--- a/drivers/gpu/drm/amd/include/amd_shared.h
+++ b/drivers/gpu/drm/amd/include/amd_shared.h
@@ -72,6 +72,7 @@  enum amd_ip_block_type {
 	AMD_IP_BLOCK_TYPE_SDMA,
 	AMD_IP_BLOCK_TYPE_UVD,
 	AMD_IP_BLOCK_TYPE_VCE,
+	AMD_IP_BLOCK_TYPE_ACP,
 };
 
 enum amd_clockgating_state {
diff --git a/include/linux/mfd/amd_acp.h b/include/linux/mfd/amd_acp.h
new file mode 100644
index 0000000..8290870
--- /dev/null
+++ b/include/linux/mfd/amd_acp.h
@@ -0,0 +1,43 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, 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.
+ *
+*/
+
+#ifndef _AMD_ACP_H
+#define _AMD_ACP_H
+
+#include <linux/types.h>
+
+struct acp_irq_prv {
+	struct device *dev;
+	void __iomem *acp_mmio;
+	int (*irq_handler)(struct device *dev);
+	void (*enable_intr)(void __iomem *acp_mmio, int enable);
+};
+
+/* Public interface of ACP device */
+struct amd_acp_device {
+	void (*irq_register)(struct amd_acp_device *acp_dev, void *iprv);
+	void (*irq_get)(struct amd_acp_device *acp_dev);
+	void (*irq_put)(struct amd_acp_device *acp_dev);
+};
+
+#endif /* _AMD_ACP_H */