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
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