[01/12] drm/amdgpu: add amd_gnb_bus support
diff mbox

Message ID 1438871112-25946-2-git-send-email-alexander.deucher@amd.com
State New
Headers show

Commit Message

Alex Deucher Aug. 6, 2015, 2:25 p.m. UTC
From: Chunming Zhou <david1.zhou@amd.com>

This is used by the incoming ACP driver.  The DMA
engine for the i2s audio codec is part of the GPU.

This exposes an amd gnb bus for the i2s codec to
hang off of.

Reviewed-by: Jammy Zhou <Jammy.Zhou@amd.com>
Signed-off-by: Chunming Zhou <david1.zhou@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
---
 drivers/gpu/drm/Kconfig                       |   3 +
 drivers/gpu/drm/Makefile                      |   1 +
 drivers/gpu/drm/amd/bus/Kconfig               |   7 +
 drivers/gpu/drm/amd/bus/Makefile              |   4 +
 drivers/gpu/drm/amd/bus/amd_gnb_bus.c         | 266 ++++++++++++++++++++++++++
 drivers/gpu/drm/amd/include/bus/amd_gnb_bus.h |  78 ++++++++
 6 files changed, 359 insertions(+)
 create mode 100644 drivers/gpu/drm/amd/bus/Kconfig
 create mode 100644 drivers/gpu/drm/amd/bus/Makefile
 create mode 100644 drivers/gpu/drm/amd/bus/amd_gnb_bus.c
 create mode 100644 drivers/gpu/drm/amd/include/bus/amd_gnb_bus.h

Comments

Lars-Peter Clausen Aug. 6, 2015, 7:36 p.m. UTC | #1
> @@ -134,6 +136,7 @@ config DRM_AMDGPU
>  	select HWMON
>  	select BACKLIGHT_CLASS_DEVICE
>  	select INTERVAL_TREE
> +	select DRM_AMD_GNB_BUS

Here you select the symbol.

[...]

> +config DRM_AMD_GNB_BUS
> +	tristate "AMD GNB bus - used for GNB IPs such as ACP and ISP"

Here you make it user selectable. Use either or having both doesn't work too
well.

> +
> +endmenu
> diff --git a/drivers/gpu/drm/amd/bus/Makefile b/drivers/gpu/drm/amd/bus/Makefile
> new file mode 100644
> index 0000000..c41ffc9
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/bus/Makefile
> @@ -0,0 +1,4 @@
> +#
> +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/amd/include/bus/
> +
> +obj-$(CONFIG_DRM_AMD_GNB_BUS) := amd_gnb_bus.o
> diff --git a/drivers/gpu/drm/amd/bus/amd_gnb_bus.c b/drivers/gpu/drm/amd/bus/amd_gnb_bus.c
> new file mode 100644
> index 0000000..071b16c
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/bus/amd_gnb_bus.c
> @@ -0,0 +1,266 @@
[..]
> +#ifdef CONFIG_PM_SLEEP
> +static int amd_gnb_bus_legacy_suspend(struct device *dev, pm_message_t mesg)
> +{
> +	struct amd_gnb_bus_dev *amd_gnb_bus_dev = to_amd_gnb_bus_device(dev);
> +	struct amd_gnb_bus_driver *driver;
> +
> +	if (!amd_gnb_bus_dev || !dev->driver)
> +		return 0;
> +	driver = to_amd_gnb_bus_driver(dev->driver);
> +	if (!driver->suspend)
> +		return 0;
> +	return driver->suspend(amd_gnb_bus_dev, mesg);
> +}
> +
> +static int amd_gnb_bus_legacy_resume(struct device *dev)
> +{
> +	struct amd_gnb_bus_dev *amd_gnb_bus_dev = to_amd_gnb_bus_device(dev);
> +	struct amd_gnb_bus_driver *driver;
> +
> +	if (!amd_gnb_bus_dev || !dev->driver)
> +		return 0;
> +	driver = to_amd_gnb_bus_driver(dev->driver);
> +	if (!driver->resume)
> +		return 0;
> +	return driver->resume(amd_gnb_bus_dev);
> +}
[...]


Preferably don't add support for legacy suspend/resume to new subsystems.
Support for this is supposed to be removed from the kernel. Just use
dev_pm_ops and then you can drop all of the above since the PM core does the
right thing on its own for dev_pm_ops. No need to have support at the bus
level if there is nothing special to do at the bus level.

[...]
Mark Brown Aug. 7, 2015, 10:25 a.m. UTC | #2
On Thu, Aug 06, 2015 at 10:25:02AM -0400, Alex Deucher wrote:
> From: Chunming Zhou <david1.zhou@amd.com>
> 
> This is used by the incoming ACP driver.  The DMA
> engine for the i2s audio codec is part of the GPU.
> 
> This exposes an amd gnb bus for the i2s codec to
> hang off of.

Could you be more specific about what an "amd gnd bus" is please?

> +enum amd_gnb_bus_ip {
> +	AMD_GNB_IP_ACP_DMA,
> +	AMD_GNB_IP_ACP_I2S,
> +	AMD_GNB_IP_ACP_PCM,
> +	AMD_GNB_IP_ISP,
> +	AMD_GNB_IP_NUM
> +};
> +
> +struct amd_gnb_bus_dev {
> +	struct device dev; /* generic device interface */
> +	enum amd_gnb_bus_ip ip;
> +	/* private data can be acp_handle/isp_handle etc.*/
> +	void *private_data;
> +};

Looking at the code I'm not seeing too much bus specific except for the
above which looks like the sort of device we usually represent as a MFD
(with the MFD providing resource distribution and arbitration between
various component devices which fit into the subsystem).  Why code a new
bus for this device?
Alex Deucher Aug. 7, 2015, 2:17 p.m. UTC | #3
On Fri, Aug 7, 2015 at 6:25 AM, Mark Brown <broonie@kernel.org> wrote:
> On Thu, Aug 06, 2015 at 10:25:02AM -0400, Alex Deucher wrote:
>> From: Chunming Zhou <david1.zhou@amd.com>
>>
>> This is used by the incoming ACP driver.  The DMA
>> engine for the i2s audio codec is part of the GPU.
>>
>> This exposes an amd gnb bus for the i2s codec to
>> hang off of.
>
> Could you be more specific about what an "amd gnd bus" is please?

It's bus to hang hw blocks of the GPU on that are controlled by other
subsystems.

>
>> +enum amd_gnb_bus_ip {
>> +     AMD_GNB_IP_ACP_DMA,
>> +     AMD_GNB_IP_ACP_I2S,
>> +     AMD_GNB_IP_ACP_PCM,
>> +     AMD_GNB_IP_ISP,
>> +     AMD_GNB_IP_NUM
>> +};
>> +
>> +struct amd_gnb_bus_dev {
>> +     struct device dev; /* generic device interface */
>> +     enum amd_gnb_bus_ip ip;
>> +     /* private data can be acp_handle/isp_handle etc.*/
>> +     void *private_data;
>> +};
>
> Looking at the code I'm not seeing too much bus specific except for the
> above which looks like the sort of device we usually represent as a MFD
> (with the MFD providing resource distribution and arbitration between
> various component devices which fit into the subsystem).  Why code a new
> bus for this device?

Adding Felix who did worked on the design for this.  The idea is that
there are hw blocks on the GPU that are controlled by drivers that are
part of other subsystems.  Those drivers need access to resources
(e.g., the MMIO aperture) controlled by the GPU driver.  I guess this
is a MFD of sorts.  If this is not the preferred way to handle this
type of device, what is?  Can you point me to another driver that
handles this differently?

Alex
Felix Kuehling Aug. 7, 2015, 4:16 p.m. UTC | #4
Hi,

To elaborate more on Alex's explanation ...

AMD SOCs have audio (and in the future potentially also camera image
signal processors) IPs built into the GNB (graphics north bridge). These
IPs are programmed through MMIO registers in the graphics MMIO aperture.
They send events to the host through the graphics IRQ. And they use
memory that is accessed through the graphics memory controller.
Therefore the GPU driver must be involved in programming these IPs.

However, these functions are not exposed to user mode by the graphics
driver subsystem in Linux. Audio is handled by ALSA and camera ISPs are
handled by V4L2. We want to represent these IPs as separate devices in
the device hierarchy, so that ALSA and V4L2 drivers can discover and
bind to them using the standard mechanisms.

Therefore we created this "virtual" GNB bus that allows us to create
devices in a sensible place in the device hierarchy, as child devices of
the GNB. The enum amd_gnb_bus_ip serves as device ID on this bus. The
struct amd_gnb_bus_dev represents the device. Private_data is specific
to the type of device. It contains high-level interfaces for ALSA
drivers to talk to the audio IP and V4L2 driver to talk to the ISP IP.
Any direct HW access, memory management and IRQ handling is done inside
the GPU driver.

Regards,
  Felix

On 15-08-07 10:17 AM, Alex Deucher wrote:
> On Fri, Aug 7, 2015 at 6:25 AM, Mark Brown <broonie@kernel.org> wrote:
>> On Thu, Aug 06, 2015 at 10:25:02AM -0400, Alex Deucher wrote:
>>> From: Chunming Zhou <david1.zhou@amd.com>
>>>
>>> This is used by the incoming ACP driver.  The DMA
>>> engine for the i2s audio codec is part of the GPU.
>>>
>>> This exposes an amd gnb bus for the i2s codec to
>>> hang off of.
>> Could you be more specific about what an "amd gnd bus" is please?
> It's bus to hang hw blocks of the GPU on that are controlled by other
> subsystems.
>
>>> +enum amd_gnb_bus_ip {
>>> +     AMD_GNB_IP_ACP_DMA,
>>> +     AMD_GNB_IP_ACP_I2S,
>>> +     AMD_GNB_IP_ACP_PCM,
>>> +     AMD_GNB_IP_ISP,
>>> +     AMD_GNB_IP_NUM
>>> +};
>>> +
>>> +struct amd_gnb_bus_dev {
>>> +     struct device dev; /* generic device interface */
>>> +     enum amd_gnb_bus_ip ip;
>>> +     /* private data can be acp_handle/isp_handle etc.*/
>>> +     void *private_data;
>>> +};
>> Looking at the code I'm not seeing too much bus specific except for the
>> above which looks like the sort of device we usually represent as a MFD
>> (with the MFD providing resource distribution and arbitration between
>> various component devices which fit into the subsystem).  Why code a new
>> bus for this device?
> Adding Felix who did worked on the design for this.  The idea is that
> there are hw blocks on the GPU that are controlled by drivers that are
> part of other subsystems.  Those drivers need access to resources
> (e.g., the MMIO aperture) controlled by the GPU driver.  I guess this
> is a MFD of sorts.  If this is not the preferred way to handle this
> type of device, what is?  Can you point me to another driver that
> handles this differently?
>
> Alex
Mark Brown Aug. 7, 2015, 5:20 p.m. UTC | #5
On Fri, Aug 07, 2015 at 10:17:36AM -0400, Alex Deucher wrote:
> On Fri, Aug 7, 2015 at 6:25 AM, Mark Brown <broonie@kernel.org> wrote:

> > Looking at the code I'm not seeing too much bus specific except for the
> > above which looks like the sort of device we usually represent as a MFD
> > (with the MFD providing resource distribution and arbitration between
> > various component devices which fit into the subsystem).  Why code a new
> > bus for this device?

> Adding Felix who did worked on the design for this.  The idea is that
> there are hw blocks on the GPU that are controlled by drivers that are
> part of other subsystems.  Those drivers need access to resources
> (e.g., the MMIO aperture) controlled by the GPU driver.  I guess this
> is a MFD of sorts.  If this is not the preferred way to handle this
> type of device, what is?  Can you point me to another driver that
> handles this differently?

Yeah, this sounds like a MFD - see drivers/mfd and take a look at how
the drivers there deal with this.
Mark Brown Aug. 7, 2015, 6:24 p.m. UTC | #6
On Fri, Aug 07, 2015 at 12:16:03PM -0400, Felix Kuehling wrote:
> Hi,
> 
> To elaborate more on Alex's explanation ...

Please don't top post, reply in line deleting any unneeded context so
people have context for what's being discussed.

> Therefore we created this "virtual" GNB bus that allows us to create
> devices in a sensible place in the device hierarchy, as child devices of
> the GNB. The enum amd_gnb_bus_ip serves as device ID on this bus. The

Like I say this just sounds like exactly the sort of thing we handle
with an MFD, it's a very common pattern.
Felix Kuehling Aug. 7, 2015, 8:03 p.m. UTC | #7
On 15-08-07 02:24 PM, Mark Brown wrote:
> Like I say this just sounds like exactly the sort of thing we handle
> with an MFD, it's a very common pattern. 

OK, the MFD documentation in Documentation/devicetree/bindings/mfd/
seemed to imply a dependency on a devicetree. It took me a moment to
realize that's just the most common scenario.

In our case we don't have a devicetree, we're talking about a PC
platform. The GPU (GNB) is a PCIe device. Does the following sound like
proper use of the MFD framework for our case?

 1. GPU driver gets initialized, detects a GPU with audio co-processor (ACP)
 2. GPU driver registers mfd_cell for the ACP device using
    mfd_add_hotplug_devices
      * It's not really hot-plug, but the mem_base, irq_base, irq_domain
        parameters don't make sense for us
 3. Platform_data in the MFD cell contains audio driver-specific data,
    function pointers, etc. for the audio driver to use
 4. Audio driver binds to platform device created by
    mfd_add_hotplug_devices based on driver name

Or do we have to convert our GPU device to be an MFD cell itself, a peer
of the ACP cell?

Thanks,
  Felix
Mark Brown Aug. 10, 2015, 11:56 a.m. UTC | #8
On Fri, Aug 07, 2015 at 04:03:08PM -0400, Felix Kuehling wrote:

>  1. GPU driver gets initialized, detects a GPU with audio co-processor (ACP)
>  2. GPU driver registers mfd_cell for the ACP device using
>     mfd_add_hotplug_devices
>       * It's not really hot-plug, but the mem_base, irq_base, irq_domain
>         parameters don't make sense for us

All those should be optional...

>  3. Platform_data in the MFD cell contains audio driver-specific data,
>     function pointers, etc. for the audio driver to use

Note that a MFD knows that its parent is the core device so it can just
look at the driver data of the parent unless things vary per child.

>  4. Audio driver binds to platform device created by
>     mfd_add_hotplug_devices based on driver name

> Or do we have to convert our GPU device to be an MFD cell itself, a peer
> of the ACP cell?

If they're all part of the same block of hardware that'd be more normal,
but it all depends on what the code looks like and what the relevant
maintainers think.

Patch
diff mbox

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index c46ca31..8c3cc9e 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -120,6 +120,8 @@  config DRM_RADEON
 
 source "drivers/gpu/drm/radeon/Kconfig"
 
+source "drivers/gpu/drm/amd/bus/Kconfig"
+
 config DRM_AMDGPU
 	tristate "AMD GPU"
 	depends on DRM && PCI
@@ -134,6 +136,7 @@  config DRM_AMDGPU
 	select HWMON
 	select BACKLIGHT_CLASS_DEVICE
 	select INTERVAL_TREE
+	select DRM_AMD_GNB_BUS
 	help
 	  Choose this option if you have a recent AMD Radeon graphics card.
 
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 5713d05..5380477 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -37,6 +37,7 @@  obj-$(CONFIG_DRM_TDFX)	+= tdfx/
 obj-$(CONFIG_DRM_R128)	+= r128/
 obj-$(CONFIG_HSA_AMD) += amd/amdkfd/
 obj-$(CONFIG_DRM_RADEON)+= radeon/
+obj-$(CONFIG_DRM_AMD_GNB_BUS)   += amd/bus/
 obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/
 obj-$(CONFIG_DRM_MGA)	+= mga/
 obj-$(CONFIG_DRM_I810)	+= i810/
diff --git a/drivers/gpu/drm/amd/bus/Kconfig b/drivers/gpu/drm/amd/bus/Kconfig
new file mode 100644
index 0000000..f101dd8
--- /dev/null
+++ b/drivers/gpu/drm/amd/bus/Kconfig
@@ -0,0 +1,7 @@ 
+menu "AMD GNB BUS"
+	visible if 0
+
+config DRM_AMD_GNB_BUS
+	tristate "AMD GNB bus - used for GNB IPs such as ACP and ISP"
+
+endmenu
diff --git a/drivers/gpu/drm/amd/bus/Makefile b/drivers/gpu/drm/amd/bus/Makefile
new file mode 100644
index 0000000..c41ffc9
--- /dev/null
+++ b/drivers/gpu/drm/amd/bus/Makefile
@@ -0,0 +1,4 @@ 
+#
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/amd/include/bus/
+
+obj-$(CONFIG_DRM_AMD_GNB_BUS) := amd_gnb_bus.o
diff --git a/drivers/gpu/drm/amd/bus/amd_gnb_bus.c b/drivers/gpu/drm/amd/bus/amd_gnb_bus.c
new file mode 100644
index 0000000..071b16c
--- /dev/null
+++ b/drivers/gpu/drm/amd/bus/amd_gnb_bus.c
@@ -0,0 +1,266 @@ 
+/*
+ * Copyright 2014 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/module.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include "amd_gnb_bus.h"
+
+#define to_amd_gnb_bus_device(x) container_of((x), struct amd_gnb_bus_dev, dev)
+#define to_amd_gnb_bus_driver(drv) (container_of((drv),			\
+						struct amd_gnb_bus_driver, \
+						driver))
+
+static int amd_gnb_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct amd_gnb_bus_dev *amd_gnb_bus_dev = to_amd_gnb_bus_device(dev);
+	struct amd_gnb_bus_driver *amd_gnb_bus_driver =
+		to_amd_gnb_bus_driver(drv);
+
+	return amd_gnb_bus_dev->ip == amd_gnb_bus_driver->ip ? 1 : 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int amd_gnb_bus_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+	struct amd_gnb_bus_dev *amd_gnb_bus_dev = to_amd_gnb_bus_device(dev);
+	struct amd_gnb_bus_driver *driver;
+
+	if (!amd_gnb_bus_dev || !dev->driver)
+		return 0;
+	driver = to_amd_gnb_bus_driver(dev->driver);
+	if (!driver->suspend)
+		return 0;
+	return driver->suspend(amd_gnb_bus_dev, mesg);
+}
+
+static int amd_gnb_bus_legacy_resume(struct device *dev)
+{
+	struct amd_gnb_bus_dev *amd_gnb_bus_dev = to_amd_gnb_bus_device(dev);
+	struct amd_gnb_bus_driver *driver;
+
+	if (!amd_gnb_bus_dev || !dev->driver)
+		return 0;
+	driver = to_amd_gnb_bus_driver(dev->driver);
+	if (!driver->resume)
+		return 0;
+	return driver->resume(amd_gnb_bus_dev);
+}
+
+static int amd_gnb_bus_device_pm_suspend(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_suspend(dev);
+	else
+		return amd_gnb_bus_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int amd_gnb_bus_device_pm_resume(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_resume(dev);
+	else
+		return amd_gnb_bus_legacy_resume(dev);
+}
+
+static int amd_gnb_bus_device_pm_freeze(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_freeze(dev);
+	else
+		return amd_gnb_bus_legacy_suspend(dev, PMSG_FREEZE);
+}
+
+static int amd_gnb_bus_device_pm_thaw(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_thaw(dev);
+	else
+		return amd_gnb_bus_legacy_resume(dev);
+}
+
+static int amd_gnb_bus_device_pm_poweroff(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_poweroff(dev);
+	else
+		return amd_gnb_bus_legacy_suspend(dev, PMSG_HIBERNATE);
+}
+
+static int amd_gnb_bus_device_pm_restore(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_restore(dev);
+	else
+		return amd_gnb_bus_legacy_resume(dev);
+}
+#else /* !CONFIG_PM_SLEEP */
+#define amd_gnb_bus_device_pm_suspend	NULL
+#define amd_gnb_bus_device_pm_resume	NULL
+#define amd_gnb_bus_device_pm_freeze	NULL
+#define amd_gnb_bus_device_pm_thaw	NULL
+#define amd_gnb_bus_device_pm_poweroff	NULL
+#define amd_gnb_bus_device_pm_restore	NULL
+#endif /* !CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops amd_gnb_bus_device_pm_ops = {
+	.suspend = amd_gnb_bus_device_pm_suspend,
+	.resume = amd_gnb_bus_device_pm_resume,
+	.freeze = amd_gnb_bus_device_pm_freeze,
+	.thaw = amd_gnb_bus_device_pm_thaw,
+	.poweroff = amd_gnb_bus_device_pm_poweroff,
+	.restore = amd_gnb_bus_device_pm_restore,
+	SET_RUNTIME_PM_OPS(
+		pm_generic_runtime_suspend,
+		pm_generic_runtime_resume,
+		pm_runtime_idle
+	)
+};
+
+/* The bus should only be registered by the first amd_gnb, but further
+ * socs can add devices to the bus. */
+struct bus_type amd_gnb_bus_type = {
+	.name  = "amd_gnb",
+	.match = amd_gnb_bus_match,
+	.pm    = &amd_gnb_bus_device_pm_ops,
+};
+EXPORT_SYMBOL(amd_gnb_bus_type);
+
+static int amd_gnb_bus_drv_probe(struct device *_dev)
+{
+	struct amd_gnb_bus_driver *drv = to_amd_gnb_bus_driver(_dev->driver);
+	struct amd_gnb_bus_dev *dev = to_amd_gnb_bus_device(_dev);
+
+	return drv->probe(dev);
+}
+
+static int amd_gnb_bus_drv_remove(struct device *_dev)
+{
+	struct amd_gnb_bus_driver *drv = to_amd_gnb_bus_driver(_dev->driver);
+	struct amd_gnb_bus_dev *dev = to_amd_gnb_bus_device(_dev);
+
+	return drv->remove(dev);
+}
+
+static void amd_gnb_bus_drv_shutdown(struct device *_dev)
+{
+	struct amd_gnb_bus_driver *drv = to_amd_gnb_bus_driver(_dev->driver);
+	struct amd_gnb_bus_dev *dev = to_amd_gnb_bus_device(_dev);
+
+	drv->shutdown(dev);
+}
+
+int amd_gnb_bus_register_driver(struct amd_gnb_bus_driver *drv,
+			       struct module *owner,
+			       const char *mod_name)
+{
+	/* initialize common driver fields */
+	drv->driver.name = drv->name;
+	drv->driver.bus = &amd_gnb_bus_type;
+	drv->driver.owner = owner;
+	drv->driver.mod_name = mod_name;
+
+	if (drv->probe)
+		drv->driver.probe = amd_gnb_bus_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = amd_gnb_bus_drv_remove;
+	if (drv->shutdown)
+		drv->driver.shutdown = amd_gnb_bus_drv_shutdown;
+
+	/* register with core */
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(amd_gnb_bus_register_driver);
+
+void amd_gnb_bus_unregister_driver(struct amd_gnb_bus_driver *drv)
+{
+	/* register with core */
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(amd_gnb_bus_unregister_driver);
+
+int amd_gnb_bus_register_device(struct amd_gnb_bus_dev *dev)
+{
+	dev->dev.bus = &amd_gnb_bus_type;
+	return device_add(&dev->dev);
+}
+EXPORT_SYMBOL(amd_gnb_bus_register_device);
+
+void amd_gnb_bus_unregister_device(struct amd_gnb_bus_dev *dev)
+{
+	if (dev)
+		device_del(&dev->dev);
+}
+EXPORT_SYMBOL(amd_gnb_bus_unregister_device);
+
+int amd_gnb_bus_device_init(struct amd_gnb_bus_dev *bus_dev,
+			    enum amd_gnb_bus_ip ip,
+			    char *dev_name,
+			    void *handle,
+			    struct device *parent)
+{
+	device_initialize(&bus_dev->dev);
+	bus_dev->dev.init_name = dev_name;
+	bus_dev->ip = ip;
+	bus_dev->private_data = handle;
+	bus_dev->dev.parent = parent;
+	return amd_gnb_bus_register_device(bus_dev);
+}
+EXPORT_SYMBOL(amd_gnb_bus_device_init);
+
+static int __init amd_gnb_bus_init(void)
+{
+	int ret = 0;
+	/* does this need to be thread safe? */
+	ret = bus_register(&amd_gnb_bus_type);
+	if (ret)
+		pr_err("%s: bus register failed\n", __func__);
+	else
+		pr_info("%s: initialization is successful\n", __func__);
+
+	return ret;
+}
+
+static void __exit amd_gnb_bus_exit(void)
+{
+	bus_unregister(&amd_gnb_bus_type);
+}
+
+module_init(amd_gnb_bus_init);
+module_exit(amd_gnb_bus_exit);
+
+MODULE_AUTHOR("AMD");
+MODULE_DESCRIPTION("AMD GPU bus");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/amd/include/bus/amd_gnb_bus.h b/drivers/gpu/drm/amd/include/bus/amd_gnb_bus.h
new file mode 100644
index 0000000..3e90077
--- /dev/null
+++ b/drivers/gpu/drm/amd/include/bus/amd_gnb_bus.h
@@ -0,0 +1,78 @@ 
+/*
+ * Copyright 2014 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_GNB_BUS_H__
+#define __AMD_GNB_BUS_H__
+
+#include <linux/types.h>
+
+enum amd_gnb_bus_ip {
+	AMD_GNB_IP_ACP_DMA,
+	AMD_GNB_IP_ACP_I2S,
+	AMD_GNB_IP_ACP_PCM,
+	AMD_GNB_IP_ISP,
+	AMD_GNB_IP_NUM
+};
+
+struct amd_gnb_bus_dev {
+	struct device dev; /* generic device interface */
+	enum amd_gnb_bus_ip ip;
+	/* private data can be acp_handle/isp_handle etc.*/
+	void *private_data;
+};
+
+struct amd_gnb_bus_driver {
+	const char *name;
+	enum amd_gnb_bus_ip ip;
+	int (*probe)(struct amd_gnb_bus_dev *dev); /* New device inserted */
+	int (*remove)(struct amd_gnb_bus_dev *dev); /* Device removed */
+	int (*suspend)(struct amd_gnb_bus_dev *dev, pm_message_t state);
+	int (*resume)(struct amd_gnb_bus_dev *dev);
+	void (*shutdown)(struct amd_gnb_bus_dev *dev);
+	struct device_driver driver;  /* generic device driver interface */
+};
+
+#define amd_gnb_to_acp_device(x) container_of((x), \
+					     struct amd_gnb_bus_dev_acp, \
+					     base)
+#define amd_gnb_to_isp_device(x) container_of((x), \
+					     struct amd_gnb_bus_dev_isp, \
+					     base)
+#define amd_gnb_parent_to_pci_device(x) container_of((x)->dev.parent, \
+						     struct pci_dev,  \
+						     dev)
+
+int amd_gnb_bus_register_device(struct amd_gnb_bus_dev *dev);
+void amd_gnb_bus_unregister_device(struct amd_gnb_bus_dev *dev);
+int amd_gnb_bus_device_init(struct amd_gnb_bus_dev *bus_dev,
+			    enum amd_gnb_bus_ip ip,
+			    char *dev_name,
+			    void *handle,
+			    struct device *parent);
+int amd_gnb_bus_register_driver(struct amd_gnb_bus_driver *drv,
+			       struct module *owner,
+			       const char *mod_name);
+void amd_gnb_bus_unregister_driver(struct amd_gnb_bus_driver *drv);
+
+#endif