diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index ba3fb04bb691..32ffa66a8b54 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -371,6 +371,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
 
 source "drivers/gpu/drm/sprd/Kconfig"
 
+source "drivers/gpu/drm/apu/Kconfig"
+
 config DRM_HYPERV
 	tristate "DRM Support for Hyper-V synthetic video device"
 	depends on DRM && PCI && MMU && HYPERV
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index a33257d2bc7f..7cd8c0f3936a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -191,6 +191,7 @@ obj-$(CONFIG_DRM_MCDE) += mcde/
 obj-$(CONFIG_DRM_TIDSS) += tidss/
 obj-y			+= xlnx/
 obj-y			+= gud/
+obj-$(CONFIG_DRM_APU) += apu/
 obj-$(CONFIG_DRM_HYPERV) += hyperv/
 obj-y			+= solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
diff --git a/drivers/gpu/drm/apu/Kconfig b/drivers/gpu/drm/apu/Kconfig
new file mode 100644
index 000000000000..226dcf072115
--- /dev/null
+++ b/drivers/gpu/drm/apu/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+config DRM_APU
+	tristate "APU (AI Processor Unit)"
+	select DRM_GEM_DMA_HELPER
+	select DRM_KMS_HELPER
+	help
+	  This provides a DRM driver that provides some facilities to
+	  communicate with an AI Processor Unit (APU).
+	  The driver intends to provide a common infrastructure that may be
+	  used to support many different APU.
diff --git a/drivers/gpu/drm/apu/Makefile b/drivers/gpu/drm/apu/Makefile
new file mode 100644
index 000000000000..ad85b88a8b52
--- /dev/null
+++ b/drivers/gpu/drm/apu/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+drm_apu-y += apu_drv.o
+
+obj-$(CONFIG_DRM_APU) += drm_apu.o
diff --git a/drivers/gpu/drm/apu/apu_drv.c b/drivers/gpu/drm/apu/apu_drv.c
new file mode 100644
index 000000000000..b420b13a9ffd
--- /dev/null
+++ b/drivers/gpu/drm/apu/apu_drv.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright 2020 BayLibre SAS
+
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <drm/apu_drm.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_probe_helper.h>
+
+#include "apu_internal.h"
+
+static LIST_HEAD(apu_devices);
+
+static int ioctl_apu_state(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv);
+
+static const struct drm_ioctl_desc ioctls[] = {
+	DRM_IOCTL_DEF_DRV(APU_STATE, ioctl_apu_state,
+			  DRM_RENDER_ALLOW),
+};
+
+DEFINE_DRM_GEM_DMA_FOPS(apu_drm_ops);
+
+static struct drm_driver apu_drm_driver = {
+	.driver_features = DRIVER_GEM | DRIVER_SYNCOBJ,
+	.name = "drm_apu",
+	.desc = "APU DRM driver",
+	.date = "20210319",
+	.major = 1,
+	.minor = 0,
+	.patchlevel = 0,
+	.ioctls = ioctls,
+	.num_ioctls = ARRAY_SIZE(ioctls),
+	.fops = &apu_drm_ops,
+	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_dma_dumb_create),
+};
+
+/**
+ * apu_dev_alloc() - Allocate a new APU device
+ *
+ * @dev: Pointer to the device instance.
+
+ * This allocate an APU device.
+ * The APU describe a hardware accelerator that may have one or more
+ * core (or unit).
+ *
+ * Returns: A pointer or NULL in case of failure.
+ */
+struct apu_drm *apu_dev_alloc(struct device *dev)
+{
+	struct drm_device *drm;
+	struct apu_drm *apu;
+
+	apu = devm_drm_dev_alloc(dev, &apu_drm_driver, typeof(*apu), base);
+	if (IS_ERR(apu))
+		return NULL;
+	INIT_LIST_HEAD(&apu->cores);
+
+	apu->dev = dev;
+	ida_init(&apu->ida);
+	drm = &apu->base;
+	drm->dev_private = apu;
+
+	dev_set_drvdata(dev, drm);
+
+	return apu;
+}
+EXPORT_SYMBOL_GPL(apu_dev_alloc);
+
+/**
+ * apu_dev_register() - Register the APU to DRM
+ *
+ * @apu: Pointer to APU device
+ *
+ * Register an APU device to DRM.
+ * On success, this creates everything required to use the APU.
+ * Note that at this step, the cores (or units) have not been
+ * registered so we can't yet perform any operations.
+ *
+ * Returns: Zero on success, non-zero value on failure.
+ */
+int apu_dev_register(struct apu_drm *apu)
+{
+	struct drm_device *drm = &apu->base;
+	int ret;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret)
+		return ret;
+
+	list_add(&apu->node, &apu_devices);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(apu_dev_register);
+
+/**
+ * apu_dev_unregister() - Unregister the APU
+ *
+ * @apu: Pointer to APU device
+ *
+ * This undo what has been done by apu_dev_register();
+ */
+void apu_dev_unregister(struct apu_drm *apu)
+{
+	struct drm_device *drm = &apu->base;
+
+	list_del(&apu->node);
+	drm_dev_unregister(drm);
+}
+EXPORT_SYMBOL_GPL(apu_dev_unregister);
+
+/**
+ * apu_core_alloc() - Allocate an APU core
+ *
+ * @apu: Pointer to APU device
+ * @ops: The operation callbacks to use for this core
+ * @priv:
+ *
+ * Allocate an APU core. This represents a computing unit that could
+ * execute a job. The APU may be composed of different units that doesn't
+ * accept same kind of jobs so we may to use differents callbacks for each
+ * core.
+ *
+ * Returns: A pointer or NULL in case of failure.
+ */
+struct apu_core *apu_core_alloc(struct apu_drm *apu, struct apu_core_ops *ops,
+				void *priv)
+{
+	struct apu_core *core;
+
+	if (!ops || !ops->is_ready)
+		return NULL;
+
+	core = devm_kzalloc(apu->dev, sizeof(*core), GFP_KERNEL);
+	if (!core)
+		return NULL;
+
+	core->device_id = ida_alloc(&apu->ida, GFP_KERNEL);
+	if (core->device_id < 0)
+		return NULL;
+
+	core->apu = apu;
+	core->priv = priv;
+	core->ops = ops;
+
+	list_add(&core->node, &apu->cores);
+
+	return core;
+}
+EXPORT_SYMBOL_GPL(apu_core_alloc);
+
+/**
+ * apu_core_free() - Free an APU core allocated using apu_core_alloc()
+ *
+ * @core: The APU core to release
+ */
+void apu_core_free(struct apu_core *core)
+{
+	ida_free(&core->apu->ida, core->device_id);
+	list_del(&core->node);
+}
+EXPORT_SYMBOL_GPL(apu_core_free);
+
+/**
+ * apu_core_register() - Register a core to APU device
+ *
+ * @dev: Pointer to APU device
+ * @core: Pointer to APU core to register
+ * @priv: Private data attached to this core
+ *
+ * Register an APU core and make it available for computing.
+ * On success, userspace can start using this core.
+ *
+ * Returns: Zero on success, non-zero value on failure.
+ */
+int apu_core_register(struct device *dev, struct apu_core *core, void *priv)
+{
+	int ret;
+
+	core->dev_priv = priv;
+	core->dev = dev;
+
+	if (core->ops->register_core) {
+		ret = core->ops->register_core(core);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(apu_core_register);
+
+/**
+ * apu_core_remove() - Remove a core from the APU device
+ *
+ * @core: Pointer to APU core to remove
+ */
+void apu_core_remove(struct apu_core *core)
+{
+	core->dev_priv = NULL;
+}
+EXPORT_SYMBOL_GPL(apu_core_remove);
+
+/**
+ * apu_find_core_by_priv() - Find a core allocated by apu_core_alloc()
+ *
+ * @priv: The pointer used to allocate the core
+ *
+ * All core allocated using apu_core_alloc() is registered to a list.
+ * This goes through the list to find the core using the @priv field.
+ *
+ * Returns: A pointer or NULL if no core has been found.
+ */
+struct apu_core *apu_find_core_by_priv(void *priv)
+{
+	struct apu_drm *apu;
+	struct apu_core *core;
+
+	list_for_each_entry(apu, &apu_devices, node) {
+		list_for_each_entry(core, &apu->cores, node) {
+			if (core->priv == priv)
+				return core;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(apu_find_core_by_priv);
+
+static struct apu_core *get_apu_core(struct apu_drm *apu, int device_id)
+{
+	struct apu_core *core;
+
+	list_for_each_entry(core, &apu->cores, node) {
+		if (core->device_id == device_id)
+			return core;
+	}
+
+	return NULL;
+}
+
+static void apu_core_update_state(struct apu_core *core)
+{
+	if (!core->ops->is_ready(core))
+		core->flags &= ~APU_ONLINE;
+}
+
+static int ioctl_apu_state(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct apu_drm *apu = dev->dev_private;
+	struct drm_apu_state *args = data;
+	struct apu_core *core;
+
+	args->flags = 0;
+
+	core = get_apu_core(apu, args->device);
+	if (!core)
+		return -ENODEV;
+
+	apu_core_update_state(core);
+	args->flags |= core->flags;
+
+	return 0;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexandre Bailon");
diff --git a/drivers/gpu/drm/apu/apu_internal.h b/drivers/gpu/drm/apu/apu_internal.h
new file mode 100644
index 000000000000..58d93a16c68f
--- /dev/null
+++ b/drivers/gpu/drm/apu/apu_internal.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __APU_INTERNAL_H__
+#define __APU_INTERNAL_H__
+
+#include <drm/drm_drv.h>
+
+struct apu_core {
+	int device_id;
+	struct device *dev;
+	struct apu_core_ops *ops;
+	struct apu_drm *apu;
+
+	struct list_head node;
+	void *priv;
+	void *dev_priv;
+
+	u32 flags;
+};
+
+struct apu_drm {
+	struct drm_device base;
+	struct device *dev;
+
+	struct list_head cores;
+	struct list_head node;
+
+	struct ida ida;
+};
+
+/**
+ * @apu_core_ops: Provides platform specific callbacks
+ */
+struct apu_core_ops {
+	/**
+	 * @register_core:
+	 *
+	 * Optional. Platform specific APU core registration.
+	 */
+	int (*register_core)(struct apu_core *core);
+
+	/**
+	 * @is_ready:
+	 *
+	 * Implements platform specific code to test if APU is ready to receive
+	 * commands.
+	 * Basically, an APU core may be running but not be ready to handle
+	 * commands. This allows checking if APU is ready and start executing
+	 * requests.
+	 *
+	 * Returns:
+	 *
+	 * One if the APU is ready or zero.
+	 */
+	int (*is_ready)(struct apu_core *core);
+};
+
+struct apu_drm *apu_dev_alloc(struct device *dev);
+int apu_dev_register(struct apu_drm *apu);
+void apu_dev_unregister(struct apu_drm *apu);
+
+struct apu_core *apu_core_alloc(struct apu_drm *apu, struct apu_core_ops *ops,
+				void *priv);
+void apu_core_free(struct apu_core *core);
+int apu_core_register(struct device *dev, struct apu_core *core, void *priv);
+void apu_core_remove(struct apu_core *core);
+struct apu_core *apu_find_core_by_priv(void *priv);
+
+#endif /* __APU_INTERNAL_H__ */
diff --git a/include/uapi/drm/apu_drm.h b/include/uapi/drm/apu_drm.h
new file mode 100644
index 000000000000..d50c63d1b813
--- /dev/null
+++ b/include/uapi/drm/apu_drm.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note*/
+
+#ifndef __UAPI_APU_DRM_H__
+#define __UAPI_APU_DRM_H__
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define APU_ONLINE		BIT(0)
+
+struct drm_apu_state {
+	__u32 device;
+	__u32 flags;
+};
+
+#define DRM_APU_STATE			0x00
+#define DRM_APU_NUM_IOCTLS		0x01
+
+#define DRM_IOCTL_APU_STATE		DRM_IOWR(DRM_COMMAND_BASE + DRM_APU_STATE, struct drm_apu_state)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __UAPI_APU_DRM_H__ */
