diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
index 91e72bd900c1..90c6239bd77d 100644
--- a/drivers/gpu/drm/loongson/Makefile
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -9,6 +9,7 @@ loongson-y := \
 	lsdc_gfxpll.o \
 	lsdc_i2c.o \
 	lsdc_irq.o \
+	lsdc_output.o \
 	lsdc_output_7a1000.o \
 	lsdc_output_7a2000.o \
 	lsdc_plane.o \
@@ -17,6 +18,7 @@ loongson-y := \
 	lsdc_ttm.o
 
 loongson-y += loongson_device.o \
+	      loongson_drv.o \
 	      loongson_module.o
 
 obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c
index 9986c8a2a255..51de71a9b692 100644
--- a/drivers/gpu/drm/loongson/loongson_device.c
+++ b/drivers/gpu/drm/loongson/loongson_device.c
@@ -4,6 +4,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/platform_device.h>
 
 #include "lsdc_drv.h"
 
@@ -100,3 +101,32 @@ lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip_id)
 {
 	return __chip_id_desc_table[chip_id];
 }
+
+static void loongson_device_postfini(void *data)
+{
+	struct platform_device *proxy = data;
+
+	platform_device_unregister(proxy);
+}
+
+int loongson_device_preinit(struct device *parent)
+{
+	struct platform_device *proxy;
+	int ret;
+
+	proxy = platform_device_register_resndata(parent,
+						  "loongson.drm.proxy",
+						  PLATFORM_DEVID_NONE,
+						  NULL, 0,
+						  NULL, 0);
+	if (IS_ERR(proxy)) {
+		dev_err(parent, "failed to register loongson proxy device\n");
+		return PTR_ERR(proxy);
+	}
+
+	ret = devm_add_action_or_reset(parent, loongson_device_postfini, proxy);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
new file mode 100644
index 000000000000..1d7735cd1b89
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Authors:
+ *      Sui Jingfeng <sui.jingfeng@linux.dev>
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_aperture.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_ttm.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_probe_helper.h>
+
+#include "loongson_drv.h"
+#include "loongson_module.h"
+#include "lsdc_drv.h"
+#include "lsdc_gem.h"
+#include "lsdc_output.h"
+#include "lsdc_ttm.h"
+
+DEFINE_DRM_GEM_FOPS(loongson_gem_fops);
+
+static const struct drm_driver loongson_drm_driver = {
+	.driver_features = DRIVER_MODESET | DRIVER_RENDER | DRIVER_GEM |
+			   DRIVER_ATOMIC,
+	.fops = &loongson_gem_fops,
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+	.debugfs_init = loongson_debugfs_init,
+	.dumb_create = lsdc_dumb_create,
+	.dumb_map_offset = lsdc_dumb_map_offset,
+	.gem_prime_import_sg_table = lsdc_prime_import_sg_table,
+};
+
+/*
+ * The GPU and display controller in the LS7A1000/LS7A2000/LS2K2000 are
+ * separated PCIE devices. They are two devices, not one. Bar 2 of the GPU
+ * device contains the base address and size of the VRAM, both the GPU and
+ * the DC could access the on-board VRAM.
+ */
+static int loongson_drm_get_dedicated_vram(struct drm_device *drm)
+{
+	struct loongson_drm *ldrm = to_loongson_drm(drm);
+	struct pci_dev *pdev_gpu;
+	resource_size_t base, size;
+
+	/*
+	 * The GPU has 00:06.0 as its BDF, this is true at least for
+	 * LS7A1000, LS7A2000 and LS2K2000.
+	 */
+	pdev_gpu = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(6, 0));
+	if (!pdev_gpu) {
+		drm_err(drm, "No GPU device, then no VRAM\n");
+		return -ENODEV;
+	}
+
+	base = pci_resource_start(pdev_gpu, 2);
+	size = pci_resource_len(pdev_gpu, 2);
+
+	pci_dev_put(pdev_gpu);
+
+	ldrm->vram_base = base;
+	ldrm->vram_size = size;
+
+	drm_info(drm, "Dedicated vram start: 0x%llx, size: %uMiB\n",
+		 (u64)base, (u32)(size >> 20));
+
+	return (size > SZ_1M) ? 0 : -ENODEV;
+}
+
+static int loongson_drm_master_bind(struct device *dev)
+{
+	struct loongson_drm *ldrm = dev_get_drvdata(dev);
+	struct drm_device *ddev = &ldrm->ddev;
+	int ret;
+
+	loongson_drm_get_dedicated_vram(ddev);
+
+	loongson_gfxpll_preinit(ddev);
+
+	ret = drmm_mode_config_init(ddev);
+	if (ret)
+		return ret;
+
+	ret = component_bind_all(dev, ddev);
+	if (ret) {
+		dev_err(dev, "master bind all failed: %d\n", ret);
+		return ret;
+	}
+
+	drm_mode_config_reset(ddev);
+
+	lsdc_gem_init(ddev);
+
+	ret = lsdc_ttm_init(ddev);
+	if (ret) {
+		drm_err(ddev, "Memory manager init failed: %d\n", ret);
+		return ret;
+	}
+
+	drmm_kms_helper_poll_init(ddev);
+
+	ret = drm_dev_register(ddev, 0);
+	if (ret)
+		return ret;
+
+	drm_fbdev_ttm_setup(ddev, 32);
+
+	return 0;
+}
+
+static void loongson_drm_master_unbind(struct device *dev)
+{
+	struct loongson_drm *ldrm = dev_get_drvdata(dev);
+	struct drm_device *ddev = &ldrm->ddev;
+
+	drm_atomic_helper_shutdown(ddev);
+
+	drm_dev_unregister(ddev);
+
+	component_unbind_all(dev, ddev);
+}
+
+static const struct component_master_ops loongson_drm_master_ops = {
+	.bind = loongson_drm_master_bind,
+	.unbind = loongson_drm_master_unbind,
+};
+
+static int loongson_drm_freeze(struct drm_device *ddev)
+{
+	struct loongson_drm *ldrm = to_loongson_drm(ddev);
+	struct lsdc_bo *lbo;
+	int ret;
+
+	/* unpin all of buffers in the VRAM */
+	mutex_lock(&ldrm->gem.mutex);
+	list_for_each_entry(lbo, &ldrm->gem.objects, list) {
+		struct ttm_buffer_object *tbo = &lbo->tbo;
+		struct ttm_resource *resource = tbo->resource;
+		unsigned int pin_count = tbo->pin_count;
+
+		drm_dbg(ddev, "bo[%p], size: %zuKiB, type: %s, pin count: %u\n",
+			lbo, lsdc_bo_size(lbo) >> 10,
+			lsdc_mem_type_to_str(resource->mem_type), pin_count);
+
+		if (!pin_count)
+			continue;
+
+		if (resource->mem_type == TTM_PL_VRAM) {
+			ret = lsdc_bo_reserve(lbo);
+			if (unlikely(ret)) {
+				drm_err(ddev, "bo reserve failed: %d\n", ret);
+				continue;
+			}
+
+			do {
+				lsdc_bo_unpin(lbo);
+				--pin_count;
+			} while (pin_count);
+
+			lsdc_bo_unreserve(lbo);
+		}
+	}
+	mutex_unlock(&ldrm->gem.mutex);
+
+	lsdc_bo_evict_vram(ddev);
+
+	ret = drm_mode_config_helper_suspend(ddev);
+	if (unlikely(ret)) {
+		drm_err(ddev, "Freeze error: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int loongson_drm_pm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct loongson_drm *ldrm = platform_get_drvdata(pdev);
+
+	return loongson_drm_freeze(&ldrm->ddev);
+}
+
+static int loongson_drm_pm_resume(struct platform_device *pdev)
+{
+	struct loongson_drm *ldrm = platform_get_drvdata(pdev);
+
+	return drm_mode_config_helper_resume(&ldrm->ddev);
+}
+
+static void loongson_add_pci_match(struct device *master,
+				   struct device_driver *driver,
+				   struct component_match **matchptr)
+{
+	struct pci_driver *pdriver = to_pci_driver(driver);
+	const struct pci_device_id *id_table = pdriver->id_table;
+	unsigned int i = 0;
+	struct pci_dev *pdev;
+
+	while (id_table[i].vendor == PCI_VENDOR_ID_LOONGSON) {
+		pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON,
+				      id_table[i].device,
+				      NULL);
+		if (!pdev) {
+			++i;
+			continue;
+		}
+
+		component_match_add(master, matchptr, component_compare_dev,
+				    &pdev->dev);
+		pci_dev_put(pdev);
+		return;
+	}
+}
+
+static void loongson_add_platform_match(struct device *master,
+					struct device_driver *driver,
+					struct component_match **matchptr)
+{
+	struct device *dev = NULL;
+
+	while ((dev = platform_find_device_by_driver(dev, driver))) {
+		component_match_add(master, matchptr, component_compare_dev, dev);
+		put_device(dev);
+	}
+}
+
+static void loongson_matches_add(struct device *master,
+				 struct component_match **pptr)
+{
+	const struct loongson_driver_info *ldi;
+
+	ldi = loongson_get_driver_info_array(NULL);
+	while (ldi->driver) {
+		switch (ldi->type) {
+		case LOONGSON_DRIVER_FLAG_PCI:
+			loongson_add_pci_match(master, ldi->driver, pptr);
+			break;
+		case LOONGSON_DRIVER_FLAG_PLATFORM:
+			loongson_add_platform_match(master, ldi->driver, pptr);
+			break;
+		default:
+			break;
+		}
+
+		++ldi;
+	}
+}
+
+static int loongson_drm_driver_probe(struct platform_device *pdev)
+{
+	struct lsdc_device *lsdc = dev_get_drvdata(pdev->dev.parent);
+	struct component_match *matches = NULL;
+	struct loongson_drm *ldrm;
+
+	ldrm = devm_drm_dev_alloc(pdev->dev.parent,
+				  &loongson_drm_driver,
+				  struct loongson_drm, ddev);
+	if (IS_ERR(ldrm))
+		return PTR_ERR(ldrm);
+
+	ldrm->lsdc = lsdc;
+	ldrm->gfxinfo = to_loongson_gfx(lsdc->descp);
+	platform_set_drvdata(pdev, ldrm);
+
+	loongson_matches_add(&pdev->dev, &matches);
+
+	dev_info(&pdev->dev, "probed\n");
+
+	return component_master_add_with_match(&pdev->dev,
+					       &loongson_drm_master_ops,
+					       matches);
+}
+
+static void loongson_drm_driver_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &loongson_drm_master_ops);
+}
+
+struct platform_driver loongson_drm_platform_driver = {
+	.driver = {
+		.name = "loongson.drm.proxy",
+	},
+	.probe = loongson_drm_driver_probe,
+	.remove_new = loongson_drm_driver_remove,
+	.suspend = loongson_drm_pm_suspend,
+	.resume = loongson_drm_pm_resume,
+};
diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
new file mode 100644
index 000000000000..896440c973c7
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Authors:
+ *      Sui Jingfeng <sui.jingfeng@linux.dev>
+ */
+
+#ifndef __LOONGSON_DRV_H__
+#define __LOONGSON_DRV_H__
+
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <drm/ttm/ttm_device.h>
+
+#include "loongson_module.h"
+#include "lsdc_gem.h"
+#include "lsdc_gfxpll.h"
+
+struct loongson_drm {
+	struct drm_device ddev;
+
+	struct device *dev;
+
+	/* submodules */
+	struct lsdc_device *lsdc;
+	struct loonggpu_device *loonggpu;
+
+	const struct loongson_gfx_desc *gfxinfo;
+
+	struct loongson_gfxpll gfxpll;
+
+	/* memory manager */
+	struct ttm_device bdev;
+	resource_size_t vram_base;
+	resource_size_t vram_size;
+	resource_size_t gtt_base;
+	resource_size_t gtt_size;
+
+	/* tracking pinned memory */
+	size_t vram_pinned_size;
+	size_t gtt_pinned_size;
+
+	struct loongson_gem gem;
+};
+
+static inline struct loongson_drm *
+to_loongson_drm(struct drm_device *drm)
+{
+	return container_of(drm, struct loongson_drm, ddev);
+}
+
+static inline struct lsdc_device *
+to_lsdc(struct drm_device *drm)
+{
+	return to_loongson_drm(drm)->lsdc;
+}
+
+static inline struct loonggpu_device *
+to_loongpu(struct drm_device *drm)
+{
+	return to_loongson_drm(drm)->loonggpu;
+}
+
+static inline const struct loongson_gfx_desc *
+to_loongson_gfxinfo(struct drm_device *drm)
+{
+	return to_loongson_drm(drm)->gfxinfo;
+}
+
+static inline struct loongson_gem *
+to_loongson_gem(struct drm_device *drm)
+{
+	return &(to_loongson_drm(drm)->gem);
+}
+
+static inline struct loongson_gfxpll *
+to_loongson_gfxpll(struct drm_device *drm)
+{
+	return &(to_loongson_drm(drm)->gfxpll);
+}
+
+static inline struct ttm_device *
+to_tdev(struct drm_device *drm)
+{
+	return &(to_loongson_drm(drm)->bdev);
+}
+
+static inline struct loongson_drm *
+tdev_to_ldrm(struct ttm_device *tdev)
+{
+	return container_of(tdev, struct loongson_drm, bdev);
+}
+
+static inline resource_size_t
+loongson_drm_vram_base(struct drm_device *drm)
+{
+	return to_loongson_drm(drm)->vram_base;
+}
+
+static inline resource_size_t
+loongson_drm_vram_size(struct drm_device *drm)
+{
+	return to_loongson_drm(drm)->vram_size;
+}
+
+int loongson_device_preinit(struct device *parent);
+void loongson_debugfs_init(struct drm_minor *minor);
+
+#endif
diff --git a/drivers/gpu/drm/loongson/loongson_module.c b/drivers/gpu/drm/loongson/loongson_module.c
index d2a51bd395f6..918c1cfbac6f 100644
--- a/drivers/gpu/drm/loongson/loongson_module.c
+++ b/drivers/gpu/drm/loongson/loongson_module.c
@@ -4,6 +4,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/platform_device.h>
 
 #include <video/nomodeset.h>
 
@@ -17,17 +18,92 @@ int loongson_vblank = 1;
 MODULE_PARM_DESC(vblank, "Disable/Enable hw vblank support");
 module_param_named(vblank, loongson_vblank, int, 0400);
 
+static const struct loongson_driver_info loongson_driver_array[] = {
+	{
+		.driver = &lsdc_output_platform_driver.driver,
+		.type = LOONGSON_DRIVER_FLAG_PLATFORM,
+	},
+	{
+		.driver = &lsdc_pci_driver.driver,
+		.type = LOONGSON_DRIVER_FLAG_PCI,
+	},
+	{
+		.driver = &loongson_drm_platform_driver.driver,
+		.type = LOONGSON_DRIVER_FLAG_PLATFORM | LOONGSON_DRIVER_FLAG_FAKE_MASTER,
+	},
+	{
+		.driver = NULL,
+		.type = LOONGSON_DRIVER_FLAG_UNKNOWN,
+	},
+};
+
+const struct loongson_driver_info *loongson_get_driver_info_array(int *num)
+{
+	if (num)
+		*num = ARRAY_SIZE(loongson_driver_array) - 1;
+
+	return loongson_driver_array;
+}
+
+static inline void loongson_unregister_driver(int count)
+{
+	const struct loongson_driver_info *ldi;
+	struct device_driver *driver;
+
+	while (count-- > 0) {
+		ldi = &loongson_driver_array[count];
+		driver = ldi->driver;
+		if (!driver)
+			continue;
+
+		if (ldi->type & LOONGSON_DRIVER_FLAG_PCI)
+			pci_unregister_driver(to_pci_driver(driver));
+		else if (ldi->type & LOONGSON_DRIVER_FLAG_PLATFORM)
+			platform_driver_unregister(to_platform_driver(driver));
+	}
+}
+
 static int __init loongson_module_init(void)
 {
+	const struct loongson_driver_info *ldi = loongson_driver_array;
+	int count = 0;
+	int ret;
+
 	if (!loongson_modeset || video_firmware_drivers_only())
 		return -ENODEV;
 
-	return pci_register_driver(&lsdc_pci_driver);
+	while (ldi->driver) {
+		if (ldi->type & LOONGSON_DRIVER_FLAG_PCI) {
+			ret = pci_register_driver(to_pci_driver(ldi->driver));
+			if (ret)
+				goto register_driver_err;
+			goto register_driver_ok;
+		}
+
+		if (ldi->type & LOONGSON_DRIVER_FLAG_PLATFORM) {
+			ret = platform_driver_register(to_platform_driver(ldi->driver));
+			if (ret)
+				goto register_driver_err;
+			goto register_driver_ok;
+		}
+
+register_driver_ok:
+		++count;
+		++ldi;
+	}
+
+	pr_info("loongson: total %d drivers registered\n", count);
+	return 0;
+
+register_driver_err:
+	loongson_unregister_driver(count);
+
+	return ret;
 }
 module_init(loongson_module_init);
 
 static void __exit loongson_module_exit(void)
 {
-	pci_unregister_driver(&lsdc_pci_driver);
+	loongson_unregister_driver(ARRAY_SIZE(loongson_driver_array) - 1);
 }
 module_exit(loongson_module_exit);
diff --git a/drivers/gpu/drm/loongson/loongson_module.h b/drivers/gpu/drm/loongson/loongson_module.h
index 931c17521bf0..e8f268b14fee 100644
--- a/drivers/gpu/drm/loongson/loongson_module.h
+++ b/drivers/gpu/drm/loongson/loongson_module.h
@@ -6,7 +6,38 @@
 #ifndef __LOONGSON_MODULE_H__
 #define __LOONGSON_MODULE_H__
 
+#define DRIVER_AUTHOR               "Sui Jingfeng <sui.jingfeng@linux.dev>"
+#define DRIVER_NAME                 "loongson"
+#define DRIVER_DESC                 "drm driver for loongson graphics"
+#define DRIVER_DATE                 "20220701"
+#define DRIVER_MAJOR                1
+#define DRIVER_MINOR                0
+#define DRIVER_PATCHLEVEL           0
+
+enum loongson_chip_id {
+	CHIP_LS7A1000 = 0,
+	CHIP_LS7A2000 = 1,
+	CHIP_LS_LAST,
+};
+
+enum loongson_driver_type {
+	LOONGSON_DRIVER_FLAG_UNKNOWN = 0,
+	LOONGSON_DRIVER_FLAG_FAKE_MASTER = 1,
+	LOONGSON_DRIVER_FLAG_PCI = 2,
+	LOONGSON_DRIVER_FLAG_PLATFORM = 4,
+};
+
+struct loongson_driver_info {
+	struct device_driver *driver;
+	u64 type;
+};
+
+const struct loongson_driver_info *loongson_get_driver_info_array(int *num);
+
 extern int loongson_vblank;
+
 extern struct pci_driver lsdc_pci_driver;
+extern struct platform_driver lsdc_output_platform_driver;
+extern struct platform_driver loongson_drm_platform_driver;
 
 #endif
diff --git a/drivers/gpu/drm/loongson/lsdc_benchmark.c b/drivers/gpu/drm/loongson/lsdc_benchmark.c
index b088646a2ff9..b3f2fde5d345 100644
--- a/drivers/gpu/drm/loongson/lsdc_benchmark.c
+++ b/drivers/gpu/drm/loongson/lsdc_benchmark.c
@@ -5,6 +5,7 @@
 
 #include <drm/drm_debugfs.h>
 
+#include "loongson_drv.h"
 #include "lsdc_benchmark.h"
 #include "lsdc_drv.h"
 #include "lsdc_gem.h"
@@ -60,7 +61,7 @@ static void lsdc_copy_gtt_to_gtt_cpu(struct lsdc_bo *src_bo,
 	lsdc_bo_kunmap(dst_bo);
 }
 
-static void lsdc_benchmark_copy(struct lsdc_device *ldev,
+static void lsdc_benchmark_copy(struct drm_device *ddev,
 				unsigned int size,
 				unsigned int n,
 				u32 src_domain,
@@ -68,7 +69,6 @@ static void lsdc_benchmark_copy(struct lsdc_device *ldev,
 				lsdc_copy_proc_t copy_proc,
 				struct drm_printer *p)
 {
-	struct drm_device *ddev = &ldev->base;
 	struct lsdc_bo *src_bo;
 	struct lsdc_bo *dst_bo;
 	unsigned long start_jiffies;
@@ -100,12 +100,12 @@ static void lsdc_benchmark_copy(struct lsdc_device *ldev,
 		   time, throughput);
 }
 
-int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p)
+int lsdc_show_benchmark_copy(struct drm_device *ddev, struct drm_printer *p)
 {
 	unsigned int buffer_size = 1920 * 1080 * 4;
 	unsigned int iteration = 60;
 
-	lsdc_benchmark_copy(ldev,
+	lsdc_benchmark_copy(ddev,
 			    buffer_size,
 			    iteration,
 			    LSDC_GEM_DOMAIN_GTT,
@@ -113,7 +113,7 @@ int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p)
 			    lsdc_copy_gtt_to_gtt_cpu,
 			    p);
 
-	lsdc_benchmark_copy(ldev,
+	lsdc_benchmark_copy(ddev,
 			    buffer_size,
 			    iteration,
 			    LSDC_GEM_DOMAIN_GTT,
@@ -121,7 +121,7 @@ int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p)
 			    lsdc_copy_gtt_to_vram_cpu,
 			    p);
 
-	lsdc_benchmark_copy(ldev,
+	lsdc_benchmark_copy(ddev,
 			    buffer_size,
 			    iteration,
 			    LSDC_GEM_DOMAIN_VRAM,
diff --git a/drivers/gpu/drm/loongson/lsdc_benchmark.h b/drivers/gpu/drm/loongson/lsdc_benchmark.h
index 36110278237e..432213189044 100644
--- a/drivers/gpu/drm/loongson/lsdc_benchmark.h
+++ b/drivers/gpu/drm/loongson/lsdc_benchmark.h
@@ -8,6 +8,6 @@
 
 #include "lsdc_drv.h"
 
-int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p);
+int lsdc_show_benchmark_copy(struct drm_device *ddev, struct drm_printer *p);
 
 #endif
diff --git a/drivers/gpu/drm/loongson/lsdc_crtc.c b/drivers/gpu/drm/loongson/lsdc_crtc.c
index 03958b79f251..76f5269a3efa 100644
--- a/drivers/gpu/drm/loongson/lsdc_crtc.c
+++ b/drivers/gpu/drm/loongson/lsdc_crtc.c
@@ -84,7 +84,7 @@ static void lsdc_crtc0_enable(struct lsdc_crtc *lcrtc)
 	 * something if it happens.
 	 */
 	if (val & CRTC_ANCHORED) {
-		drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);
+		drm_warn(ldev->drm, "%s stall\n", lcrtc->base.name);
 		return lsdc_crtc0_soft_reset(lcrtc);
 	}
 
@@ -112,7 +112,7 @@ static void lsdc_crtc1_enable(struct lsdc_crtc *lcrtc)
 	 */
 	val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);
 	if (val & CRTC_ANCHORED) {
-		drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);
+		drm_warn(ldev->drm, "%s stall\n", lcrtc->base.name);
 		return lsdc_crtc1_soft_reset(lcrtc);
 	}
 
diff --git a/drivers/gpu/drm/loongson/lsdc_debugfs.c b/drivers/gpu/drm/loongson/lsdc_debugfs.c
index b9c2e6b1701f..86d4711c6f8c 100644
--- a/drivers/gpu/drm/loongson/lsdc_debugfs.c
+++ b/drivers/gpu/drm/loongson/lsdc_debugfs.c
@@ -5,6 +5,7 @@
 
 #include <drm/drm_debugfs.h>
 
+#include "loongson_drv.h"
 #include "lsdc_benchmark.h"
 #include "lsdc_drv.h"
 #include "lsdc_gem.h"
@@ -16,8 +17,8 @@
 static int lsdc_identify(struct seq_file *m, void *arg)
 {
 	struct drm_info_node *node = (struct drm_info_node *)m->private;
-	struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data;
-	const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp);
+	struct drm_device *ddev = node->minor->dev;
+	const struct loongson_gfx_desc *gfx = to_loongson_gfxinfo(ddev);
 	u8 impl, rev;
 
 	loongson_cpu_get_prid(&impl, &rev);
@@ -44,9 +45,8 @@ static int lsdc_show_mm(struct seq_file *m, void *arg)
 static int lsdc_show_gfxpll_clock(struct seq_file *m, void *arg)
 {
 	struct drm_info_node *node = (struct drm_info_node *)m->private;
-	struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data;
+	struct loongson_gfxpll *gfxpll = to_loongson_gfxpll(node->minor->dev);
 	struct drm_printer printer = drm_seq_file_printer(m);
-	struct loongson_gfxpll *gfxpll = ldev->gfxpll;
 
 	gfxpll->funcs->print(gfxpll, &printer, true);
 
@@ -56,31 +56,10 @@ static int lsdc_show_gfxpll_clock(struct seq_file *m, void *arg)
 static int lsdc_show_benchmark(struct seq_file *m, void *arg)
 {
 	struct drm_info_node *node = (struct drm_info_node *)m->private;
-	struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data;
+	struct drm_device *ddev = node->minor->dev;
 	struct drm_printer printer = drm_seq_file_printer(m);
 
-	lsdc_show_benchmark_copy(ldev, &printer);
-
-	return 0;
-}
-
-static int lsdc_pdev_enable_io_mem(struct seq_file *m, void *arg)
-{
-	struct drm_info_node *node = (struct drm_info_node *)m->private;
-	struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data;
-	u16 cmd;
-
-	pci_read_config_word(ldev->dc, PCI_COMMAND, &cmd);
-
-	seq_printf(m, "PCI_COMMAND: 0x%x\n", cmd);
-
-	cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
-
-	pci_write_config_word(ldev->dc, PCI_COMMAND, cmd);
-
-	pci_read_config_word(ldev->dc, PCI_COMMAND, &cmd);
-
-	seq_printf(m, "PCI_COMMAND: 0x%x\n", cmd);
+	lsdc_show_benchmark_copy(ddev, &printer);
 
 	return 0;
 }
@@ -90,21 +69,19 @@ static struct drm_info_list lsdc_debugfs_list[] = {
 	{ "bos",         lsdc_show_buffer_object, 0, NULL },
 	{ "chips",       lsdc_identify, 0, NULL },
 	{ "clocks",      lsdc_show_gfxpll_clock, 0, NULL },
-	{ "dc_enable",   lsdc_pdev_enable_io_mem, 0, NULL },
 	{ "mm",          lsdc_show_mm, 0, NULL },
 };
 
-void lsdc_debugfs_init(struct drm_minor *minor)
+void loongson_debugfs_init(struct drm_minor *minor)
 {
 	struct drm_device *ddev = minor->dev;
-	struct lsdc_device *ldev = to_lsdc(ddev);
 	unsigned int n = ARRAY_SIZE(lsdc_debugfs_list);
 	unsigned int i;
 
 	for (i = 0; i < n; ++i)
-		lsdc_debugfs_list[i].data = ldev;
+		lsdc_debugfs_list[i].data = to_loongson_drm(ddev);
 
 	drm_debugfs_create_files(lsdc_debugfs_list, n, minor->debugfs_root, minor);
 
-	lsdc_ttm_debugfs_init(ldev);
+	lsdc_ttm_debugfs_init(ddev);
 }
diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c
index adc7344d2f80..2ebc829c7745 100644
--- a/drivers/gpu/drm/loongson/lsdc_drv.c
+++ b/drivers/gpu/drm/loongson/lsdc_drv.c
@@ -3,51 +3,29 @@
  * Copyright (C) 2023 Loongson Technology Corporation Limited
  */
 
+#include <linux/aperture.h>
+#include <linux/component.h>
 #include <linux/pci.h>
 #include <linux/vgaarb.h>
 
-#include <drm/drm_aperture.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_ioctl.h>
 #include <drm/drm_modeset_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
 
+#include "loongson_drv.h"
 #include "loongson_module.h"
 #include "lsdc_drv.h"
-#include "lsdc_gem.h"
-#include "lsdc_ttm.h"
-
-#define DRIVER_AUTHOR               "Sui Jingfeng <suijingfeng@loongson.cn>"
-#define DRIVER_NAME                 "loongson"
-#define DRIVER_DESC                 "drm driver for loongson graphics"
-#define DRIVER_DATE                 "20220701"
-#define DRIVER_MAJOR                1
-#define DRIVER_MINOR                0
-#define DRIVER_PATCHLEVEL           0
-
-DEFINE_DRM_GEM_FOPS(lsdc_gem_fops);
-
-static const struct drm_driver lsdc_drm_driver = {
-	.driver_features = DRIVER_MODESET | DRIVER_RENDER | DRIVER_GEM | DRIVER_ATOMIC,
-	.fops = &lsdc_gem_fops,
-
-	.name = DRIVER_NAME,
-	.desc = DRIVER_DESC,
-	.date = DRIVER_DATE,
-	.major = DRIVER_MAJOR,
-	.minor = DRIVER_MINOR,
-	.patchlevel = DRIVER_PATCHLEVEL,
-
-	.debugfs_init = lsdc_debugfs_init,
-	.dumb_create = lsdc_dumb_create,
-	.dumb_map_offset = lsdc_dumb_map_offset,
-	.gem_prime_import_sg_table = lsdc_prime_import_sg_table,
-};
+
+/* For multiple GPU devices co-exixt in the system */
+
+static unsigned int lsdc_vga_set_decode(struct pci_dev *pdev, bool state)
+{
+	return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
+}
 
 static const struct drm_mode_config_funcs lsdc_mode_config_funcs = {
 	.fb_create = drm_gem_fb_create,
@@ -55,43 +33,16 @@ static const struct drm_mode_config_funcs lsdc_mode_config_funcs = {
 	.atomic_commit = drm_atomic_helper_commit,
 };
 
-/* Display related */
-
 static int lsdc_modeset_init(struct lsdc_device *ldev,
 			     unsigned int num_crtc,
 			     const struct lsdc_kms_funcs *funcs,
 			     bool has_vblank)
 {
-	struct drm_device *ddev = &ldev->base;
+	struct drm_device *ddev = ldev->drm;
 	struct lsdc_display_pipe *dispipe;
 	unsigned int i;
 	int ret;
 
-	for (i = 0; i < num_crtc; i++) {
-		dispipe = &ldev->dispipe[i];
-
-		/* We need an index before crtc is initialized */
-		dispipe->index = i;
-
-		ret = funcs->create_i2c(ddev, dispipe, i);
-		if (ret)
-			return ret;
-	}
-
-	for (i = 0; i < num_crtc; i++) {
-		struct i2c_adapter *ddc = NULL;
-
-		dispipe = &ldev->dispipe[i];
-		if (dispipe->li2c)
-			ddc = &dispipe->li2c->adapter;
-
-		ret = funcs->output_init(ddev, dispipe, ddc, i);
-		if (ret)
-			return ret;
-
-		ldev->num_output++;
-	}
-
 	for (i = 0; i < num_crtc; i++) {
 		dispipe = &ldev->dispipe[i];
 
@@ -111,8 +62,6 @@ static int lsdc_modeset_init(struct lsdc_device *ldev,
 			return ret;
 	}
 
-	drm_info(ddev, "Total %u outputs\n", ldev->num_output);
-
 	return 0;
 }
 
@@ -123,12 +72,6 @@ static const struct drm_mode_config_helper_funcs lsdc_mode_config_helper_funcs =
 static int lsdc_mode_config_init(struct drm_device *ddev,
 				 const struct lsdc_desc *descp)
 {
-	int ret;
-
-	ret = drmm_mode_config_init(ddev);
-	if (ret)
-		return ret;
-
 	ddev->mode_config.funcs = &lsdc_mode_config_funcs;
 	ddev->mode_config.min_width = 1;
 	ddev->mode_config.min_height = 1;
@@ -145,123 +88,78 @@ static int lsdc_mode_config_init(struct drm_device *ddev,
 	if (descp->has_vblank_counter)
 		ddev->max_vblank_count = 0xffffffff;
 
-	return ret;
-}
-
-/*
- * The GPU and display controller in the LS7A1000/LS7A2000/LS2K2000 are
- * separated PCIE devices. They are two devices, not one. Bar 2 of the GPU
- * device contains the base address and size of the VRAM, both the GPU and
- * the DC could access the on-board VRAM.
- */
-static int lsdc_get_dedicated_vram(struct lsdc_device *ldev,
-				   struct pci_dev *pdev_dc,
-				   const struct lsdc_desc *descp)
-{
-	struct drm_device *ddev = &ldev->base;
-	struct pci_dev *pdev_gpu;
-	resource_size_t base, size;
-
-	/*
-	 * The GPU has 00:06.0 as its BDF, while the DC has 00:06.1
-	 * This is true for the LS7A1000, LS7A2000 and LS2K2000.
-	 */
-	pdev_gpu = pci_get_domain_bus_and_slot(pci_domain_nr(pdev_dc->bus),
-					       pdev_dc->bus->number,
-					       PCI_DEVFN(6, 0));
-	if (!pdev_gpu) {
-		drm_err(ddev, "No GPU device, then no VRAM\n");
-		return -ENODEV;
-	}
-
-	base = pci_resource_start(pdev_gpu, 2);
-	size = pci_resource_len(pdev_gpu, 2);
-
-	ldev->vram_base = base;
-	ldev->vram_size = size;
-	ldev->gpu = pdev_gpu;
-
-	drm_info(ddev, "Dedicated vram start: 0x%llx, size: %uMiB\n",
-		 (u64)base, (u32)(size >> 20));
-
-	return (size > SZ_1M) ? 0 : -ENODEV;
+	return 0;
 }
 
-static struct lsdc_device *
-lsdc_create_device(struct pci_dev *pdev,
-		   const struct lsdc_desc *descp,
-		   const struct drm_driver *driver)
+static int lsdc_pci_component_bind(struct device *dev,
+				   struct device *master,
+				   void *data)
 {
-	struct lsdc_device *ldev;
-	struct drm_device *ddev;
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct drm_device *drm = data;
+	struct loongson_drm *ldrm = to_loongson_drm(drm);
+	struct lsdc_device *lsdc = dev_get_drvdata(dev);
+	const struct lsdc_desc *descp = lsdc->descp;
+	int num_pipe = descp->num_of_crtc;
 	int ret;
 
-	ldev = devm_drm_dev_alloc(&pdev->dev, driver, struct lsdc_device, base);
-	if (IS_ERR(ldev))
-		return ldev;
-
-	ldev->dc = pdev;
-	ldev->descp = descp;
-
-	ddev = &ldev->base;
+	ldrm->lsdc = lsdc;
+	lsdc->drm = drm;
 
-	loongson_gfxpll_create(ddev, &ldev->gfxpll);
+	ret = aperture_remove_conflicting_devices(ldrm->vram_base,
+						  ldrm->vram_size,
+						  DRIVER_NAME);
+	if (ret)
+		return ret;
 
-	ret = lsdc_get_dedicated_vram(ldev, pdev, descp);
-	if (ret) {
-		drm_err(ddev, "Init VRAM failed: %d\n", ret);
-		return ERR_PTR(ret);
-	}
+	ret = lsdc_mode_config_init(drm, descp);
+	if (ret)
+		return ret;
 
-	ret = drm_aperture_remove_conflicting_framebuffers(ldev->vram_base,
-							   ldev->vram_size,
-							   driver);
-	if (ret) {
-		drm_err(ddev, "Remove firmware framebuffers failed: %d\n", ret);
-		return ERR_PTR(ret);
-	}
+	ret = lsdc_modeset_init(lsdc, num_pipe, descp->funcs, loongson_vblank);
+	if (ret)
+		return ret;
 
-	ret = lsdc_ttm_init(ldev);
-	if (ret) {
-		drm_err(ddev, "Memory manager init failed: %d\n", ret);
-		return ERR_PTR(ret);
+	if (loongson_vblank) {
+		ret = drm_vblank_init(drm, num_pipe);
+		if (ret)
+			return ret;
 	}
 
-	lsdc_gem_init(ddev);
-
-	/* Bar 0 of the DC device contains the MMIO register's base address */
-	ldev->reg_base = pcim_iomap(pdev, 0, 0);
-	if (!ldev->reg_base)
-		return ERR_PTR(-ENODEV);
-
-	spin_lock_init(&ldev->reglock);
-
-	ret = lsdc_mode_config_init(ddev, descp);
+	ret = devm_request_irq(dev,
+			       pdev->irq,
+			       descp->funcs->irq_handler,
+			       IRQF_SHARED,
+			       dev_name(dev),
+			       drm);
 	if (ret)
-		return ERR_PTR(ret);
+		return ret;
 
-	ret = lsdc_modeset_init(ldev, descp->num_of_crtc, descp->funcs,
-				loongson_vblank);
-	if (ret)
-		return ERR_PTR(ret);
+	dev_info(dev, "lsdc irq: %d\n", pdev->irq);
 
-	drm_mode_config_reset(ddev);
+	vga_client_register(pdev, lsdc_vga_set_decode);
 
-	return ldev;
+	return 0;
 }
 
-/* For multiple GPU driver instance co-exixt in the system */
-
-static unsigned int lsdc_vga_set_decode(struct pci_dev *pdev, bool state)
+static void lsdc_pci_component_unbind(struct device *dev,
+				      struct device *master,
+				      void *data)
 {
-	return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
+	struct pci_dev *pdev = to_pci_dev(dev);
+
+	vga_client_unregister(pdev);
 }
 
+const struct component_ops lsdc_pci_component_ops = {
+	.bind = lsdc_pci_component_bind,
+	.unbind = lsdc_pci_component_unbind,
+};
+
 static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	const struct lsdc_desc *descp;
-	struct drm_device *ddev;
-	struct lsdc_device *ldev;
+	struct lsdc_device *lsdc;
 	int ret;
 
 	descp = lsdc_device_probe(pdev, ent->driver_data);
@@ -281,134 +179,62 @@ static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	dev_info(&pdev->dev, "Found %s, revision: %u",
 		 to_loongson_gfx(descp)->model, pdev->revision);
 
-	ldev = lsdc_create_device(pdev, descp, &lsdc_drm_driver);
-	if (IS_ERR(ldev))
-		return PTR_ERR(ldev);
-
-	ddev = &ldev->base;
-
-	pci_set_drvdata(pdev, ddev);
+	lsdc = devm_kzalloc(&pdev->dev, sizeof(*lsdc), GFP_KERNEL);
+	if (!lsdc)
+		return -ENOMEM;
 
-	vga_client_register(pdev, lsdc_vga_set_decode);
+	/* Bar 0 of the DC device contains the MMIO register's base address */
+	lsdc->reg_base = pcim_iomap(pdev, 0, 0);
+	if (!lsdc->reg_base)
+		return -ENODEV;
 
-	drm_kms_helper_poll_init(ddev);
+	lsdc->descp = descp;
+	spin_lock_init(&lsdc->reglock);
 
-	if (loongson_vblank) {
-		ret = drm_vblank_init(ddev, descp->num_of_crtc);
-		if (ret)
-			return ret;
+	pci_set_drvdata(pdev, lsdc);
 
-		ret = devm_request_irq(&pdev->dev, pdev->irq,
-				       descp->funcs->irq_handler,
-				       IRQF_SHARED,
-				       dev_name(&pdev->dev), ddev);
-		if (ret) {
-			drm_err(ddev, "Failed to register interrupt: %d\n", ret);
-			return ret;
-		}
+	ret = lsdc_i2c_preinit(&pdev->dev, descp);
+	if (ret)
+		return ret;
 
-		drm_info(ddev, "registered irq: %u\n", pdev->irq);
-	}
+	ret = component_add(&pdev->dev, &lsdc_pci_component_ops);
+	if (ret)
+		return ret;
 
-	ret = drm_dev_register(ddev, 0);
+	ret = lsdc_output_preinit(&pdev->dev, descp);
 	if (ret)
 		return ret;
 
-	drm_fbdev_ttm_setup(ddev, 32);
+	ret = loongson_device_preinit(&pdev->dev);
+	if (ret)
+		return ret;
 
 	return 0;
 }
 
 static void lsdc_pci_remove(struct pci_dev *pdev)
 {
-	struct drm_device *ddev = pci_get_drvdata(pdev);
-
-	drm_dev_unregister(ddev);
-	drm_atomic_helper_shutdown(ddev);
+	component_del(&pdev->dev, &lsdc_pci_component_ops);
 }
 
 static void lsdc_pci_shutdown(struct pci_dev *pdev)
 {
-	drm_atomic_helper_shutdown(pci_get_drvdata(pdev));
-}
-
-static int lsdc_drm_freeze(struct drm_device *ddev)
-{
-	struct lsdc_device *ldev = to_lsdc(ddev);
-	struct lsdc_bo *lbo;
-	int ret;
-
-	/* unpin all of buffers in the VRAM */
-	mutex_lock(&ldev->gem.mutex);
-	list_for_each_entry(lbo, &ldev->gem.objects, list) {
-		struct ttm_buffer_object *tbo = &lbo->tbo;
-		struct ttm_resource *resource = tbo->resource;
-		unsigned int pin_count = tbo->pin_count;
-
-		drm_dbg(ddev, "bo[%p], size: %zuKiB, type: %s, pin count: %u\n",
-			lbo, lsdc_bo_size(lbo) >> 10,
-			lsdc_mem_type_to_str(resource->mem_type), pin_count);
-
-		if (!pin_count)
-			continue;
-
-		if (resource->mem_type == TTM_PL_VRAM) {
-			ret = lsdc_bo_reserve(lbo);
-			if (unlikely(ret)) {
-				drm_err(ddev, "bo reserve failed: %d\n", ret);
-				continue;
-			}
-
-			do {
-				lsdc_bo_unpin(lbo);
-				--pin_count;
-			} while (pin_count);
-
-			lsdc_bo_unreserve(lbo);
-		}
-	}
-	mutex_unlock(&ldev->gem.mutex);
 
-	lsdc_bo_evict_vram(ddev);
-
-	ret = drm_mode_config_helper_suspend(ddev);
-	if (unlikely(ret)) {
-		drm_err(ddev, "Freeze error: %d", ret);
-		return ret;
-	}
-
-	return 0;
-}
-
-static int lsdc_drm_resume(struct device *dev)
-{
-	struct pci_dev *pdev = to_pci_dev(dev);
-	struct drm_device *ddev = pci_get_drvdata(pdev);
-
-	return drm_mode_config_helper_resume(ddev);
 }
 
 static int lsdc_pm_freeze(struct device *dev)
 {
-	struct pci_dev *pdev = to_pci_dev(dev);
-	struct drm_device *ddev = pci_get_drvdata(pdev);
-
-	return lsdc_drm_freeze(ddev);
+	return 0;
 }
 
 static int lsdc_pm_thaw(struct device *dev)
 {
-	return lsdc_drm_resume(dev);
+	return 0;
 }
 
 static int lsdc_pm_suspend(struct device *dev)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
-	int error;
-
-	error = lsdc_pm_freeze(dev);
-	if (error)
-		return error;
 
 	pci_save_state(pdev);
 	/* Shut down the device */
@@ -429,7 +255,7 @@ static int lsdc_pm_resume(struct device *dev)
 	if (pcim_enable_device(pdev))
 		return -EIO;
 
-	return lsdc_pm_thaw(dev);
+	return 0;
 }
 
 static const struct dev_pm_ops lsdc_pm_ops = {
@@ -437,8 +263,6 @@ static const struct dev_pm_ops lsdc_pm_ops = {
 	.resume = lsdc_pm_resume,
 	.freeze = lsdc_pm_freeze,
 	.thaw = lsdc_pm_thaw,
-	.poweroff = lsdc_pm_freeze,
-	.restore = lsdc_pm_resume,
 };
 
 static const struct pci_device_id lsdc_pciid_list[] = {
@@ -448,7 +272,7 @@ static const struct pci_device_id lsdc_pciid_list[] = {
 };
 
 struct pci_driver lsdc_pci_driver = {
-	.name = DRIVER_NAME,
+	.name = "loongson.lsdc",
 	.id_table = lsdc_pciid_list,
 	.probe = lsdc_pci_probe,
 	.remove = lsdc_pci_remove,
diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h b/drivers/gpu/drm/loongson/lsdc_drv.h
index fbf2d760ef27..87694b8b9f41 100644
--- a/drivers/gpu/drm/loongson/lsdc_drv.h
+++ b/drivers/gpu/drm/loongson/lsdc_drv.h
@@ -14,11 +14,12 @@
 #include <drm/drm_encoder.h>
 #include <drm/drm_file.h>
 #include <drm/drm_plane.h>
-#include <drm/ttm/ttm_device.h>
+
+#include "loongson_module.h"
+#include "loongson_drv.h"
 
 #include "lsdc_i2c.h"
 #include "lsdc_irq.h"
-#include "lsdc_gfxpll.h"
 #include "lsdc_output.h"
 #include "lsdc_pixpll.h"
 #include "lsdc_regs.h"
@@ -38,12 +39,6 @@
  * display pipe 1 = crtc1 + dvo1 + encoder1 + connectro1 + cursor1 + primary1
  */
 
-enum loongson_chip_id {
-	CHIP_LS7A1000 = 0,
-	CHIP_LS7A2000 = 1,
-	CHIP_LS_LAST,
-};
-
 const struct lsdc_desc *
 lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip);
 
@@ -167,47 +162,23 @@ struct lsdc_cursor {
 	struct lsdc_device *ldev;
 };
 
-struct lsdc_output {
-	struct drm_encoder encoder;
-	struct drm_connector connector;
-};
-
-static inline struct lsdc_output *
-connector_to_lsdc_output(struct drm_connector *connector)
-{
-	return container_of(connector, struct lsdc_output, connector);
-}
-
-static inline struct lsdc_output *
-encoder_to_lsdc_output(struct drm_encoder *encoder)
-{
-	return container_of(encoder, struct lsdc_output, encoder);
-}
-
 struct lsdc_display_pipe {
 	struct lsdc_crtc crtc;
 	struct lsdc_primary primary;
 	struct lsdc_cursor cursor;
-	struct lsdc_output output;
-	struct lsdc_i2c *li2c;
+	struct lsdc_output *output;
 	unsigned int index;
 };
 
-static inline struct lsdc_display_pipe *
-output_to_display_pipe(struct lsdc_output *output)
-{
-	return container_of(output, struct lsdc_display_pipe, output);
-}
-
 struct lsdc_kms_funcs {
 	irqreturn_t (*irq_handler)(int irq, void *arg);
 
-	int (*create_i2c)(struct drm_device *ddev,
-			  struct lsdc_display_pipe *dispipe,
-			  unsigned int index);
+	int (*create_i2c)(struct device *parent,
+			  unsigned int index,
+			  struct i2c_adapter **ppadapter);
 
 	int (*output_init)(struct drm_device *ddev,
-			   struct lsdc_display_pipe *dispipe,
+			   struct lsdc_output *output,
 			   struct i2c_adapter *ddc,
 			   unsigned int index);
 
@@ -256,65 +227,31 @@ struct lsdc_crtc_state {
 	struct lsdc_pixpll_parms pparms;
 };
 
-struct lsdc_gem {
-	/* @mutex: protect objects list */
-	struct mutex mutex;
-	struct list_head objects;
-};
-
 struct lsdc_device {
-	struct drm_device base;
-	struct ttm_device bdev;
+	struct drm_device *drm;
 
 	/* @descp: features description of the DC variant */
 	const struct lsdc_desc *descp;
-	struct pci_dev *dc;
-	struct pci_dev *gpu;
-
-	struct loongson_gfxpll *gfxpll;
 
 	/* @reglock: protects concurrent access */
 	spinlock_t reglock;
-
 	void __iomem *reg_base;
-	resource_size_t vram_base;
-	resource_size_t vram_size;
-
-	resource_size_t gtt_base;
-	resource_size_t gtt_size;
 
+	struct i2c_adapter *i2c[LSDC_NUM_CRTC];
 	struct lsdc_display_pipe dispipe[LSDC_NUM_CRTC];
 
-	struct lsdc_gem gem;
-
 	u32 irq_status;
 
-	/* tracking pinned memory */
-	size_t vram_pinned_size;
-	size_t gtt_pinned_size;
-
 	/* @num_output: count the number of active display pipe */
 	unsigned int num_output;
 };
 
-static inline struct lsdc_device *tdev_to_ldev(struct ttm_device *bdev)
-{
-	return container_of(bdev, struct lsdc_device, bdev);
-}
-
-static inline struct lsdc_device *to_lsdc(struct drm_device *ddev)
-{
-	return container_of(ddev, struct lsdc_device, base);
-}
-
 static inline struct lsdc_crtc_state *
 to_lsdc_crtc_state(struct drm_crtc_state *base)
 {
 	return container_of(base, struct lsdc_crtc_state, base);
 }
 
-void lsdc_debugfs_init(struct drm_minor *minor);
-
 int ls7a1000_crtc_init(struct drm_device *ddev,
 		       struct drm_crtc *crtc,
 		       struct drm_plane *primary,
@@ -385,4 +322,10 @@ static inline void lsdc_pipe_wreg32(struct lsdc_device *ldev,
 	writel(val, ldev->reg_base + offset + pipe * CRTC_PIPE_OFFSET);
 }
 
+static inline unsigned int
+lsdc_pitch_align_requirement(struct drm_device *drm, unsigned int pitch)
+{
+	return ALIGN(pitch, to_lsdc(drm)->descp->pitch_align);
+}
+
 #endif
diff --git a/drivers/gpu/drm/loongson/lsdc_gem.c b/drivers/gpu/drm/loongson/lsdc_gem.c
index a720d8f53209..9104012c212c 100644
--- a/drivers/gpu/drm/loongson/lsdc_gem.c
+++ b/drivers/gpu/drm/loongson/lsdc_gem.c
@@ -10,10 +10,29 @@
 #include <drm/drm_gem.h>
 #include <drm/drm_prime.h>
 
+#include "loongson_drv.h"
 #include "lsdc_drv.h"
 #include "lsdc_gem.h"
 #include "lsdc_ttm.h"
 
+void lsdc_gem_list_add_lbo(struct drm_device *ddev, struct lsdc_bo *lbo)
+{
+	struct loongson_gem *lgem = to_loongson_gem(ddev);
+
+	mutex_lock(&lgem->mutex);
+	list_add_tail(&lbo->list, &lgem->objects);
+	mutex_unlock(&lgem->mutex);
+}
+
+void lsdc_gem_list_rm_lbo(struct drm_device *ddev, struct lsdc_bo *lbo)
+{
+	struct loongson_gem *lgem = to_loongson_gem(ddev);
+
+	mutex_lock(&lgem->mutex);
+	list_del(&lbo->list);
+	mutex_unlock(&lgem->mutex);
+}
+
 static int lsdc_gem_prime_pin(struct drm_gem_object *obj)
 {
 	struct lsdc_bo *lbo = gem_to_lsdc_bo(obj);
@@ -144,7 +163,6 @@ struct drm_gem_object *lsdc_gem_object_create(struct drm_device *ddev,
 					      struct sg_table *sg,
 					      struct dma_resv *resv)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
 	struct drm_gem_object *gobj;
 	struct lsdc_bo *lbo;
 	int ret;
@@ -164,9 +182,7 @@ struct drm_gem_object *lsdc_gem_object_create(struct drm_device *ddev,
 	gobj->funcs = &lsdc_gem_object_funcs;
 
 	/* tracking the BOs we created */
-	mutex_lock(&ldev->gem.mutex);
-	list_add_tail(&lbo->list, &ldev->gem.objects);
-	mutex_unlock(&ldev->gem.mutex);
+	lsdc_gem_list_add_lbo(ddev, lbo);
 
 	return gobj;
 }
@@ -197,11 +213,10 @@ lsdc_prime_import_sg_table(struct drm_device *ddev,
 	return gobj;
 }
 
-int lsdc_dumb_create(struct drm_file *file, struct drm_device *ddev,
+int lsdc_dumb_create(struct drm_file *file,
+		     struct drm_device *ddev,
 		     struct drm_mode_create_dumb *args)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
-	const struct lsdc_desc *descp = ldev->descp;
 	u32 domain = LSDC_GEM_DOMAIN_VRAM;
 	struct drm_gem_object *gobj;
 	size_t size;
@@ -215,13 +230,12 @@ int lsdc_dumb_create(struct drm_file *file, struct drm_device *ddev,
 	if (args->bpp != 32 && args->bpp != 16)
 		return -EINVAL;
 
-	pitch = args->width * args->bpp / 8;
-	pitch = ALIGN(pitch, descp->pitch_align);
+	pitch = lsdc_pitch_align_requirement(ddev, args->width * args->bpp / 8);
 	size = pitch * args->height;
 	size = ALIGN(size, PAGE_SIZE);
 
 	/* Maximum single bo size allowed is the half vram size available */
-	if (size > ldev->vram_size / 2) {
+	if (size > loongson_drm_vram_size(ddev) / 2) {
 		drm_err(ddev, "Requesting(%zuMiB) failed\n", size >> 20);
 		return -ENOMEM;
 	}
@@ -264,17 +278,17 @@ int lsdc_dumb_map_offset(struct drm_file *filp, struct drm_device *ddev,
 
 void lsdc_gem_init(struct drm_device *ddev)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
+	struct loongson_gem *lgem = to_loongson_gem(ddev);
 
-	mutex_init(&ldev->gem.mutex);
-	INIT_LIST_HEAD(&ldev->gem.objects);
+	mutex_init(&lgem->mutex);
+	INIT_LIST_HEAD(&lgem->objects);
 }
 
 int lsdc_show_buffer_object(struct seq_file *m, void *arg)
 {
 	struct drm_info_node *node = (struct drm_info_node *)m->private;
 	struct drm_device *ddev = node->minor->dev;
-	struct lsdc_device *ldev = to_lsdc(ddev);
+	struct loongson_drm *ldev = to_loongson_drm(ddev);
 	struct lsdc_bo *lbo;
 	unsigned int i;
 
diff --git a/drivers/gpu/drm/loongson/lsdc_gem.h b/drivers/gpu/drm/loongson/lsdc_gem.h
index 92cbb10e6e13..46e4c0bdf9fb 100644
--- a/drivers/gpu/drm/loongson/lsdc_gem.h
+++ b/drivers/gpu/drm/loongson/lsdc_gem.h
@@ -6,9 +6,22 @@
 #ifndef __LSDC_GEM_H__
 #define __LSDC_GEM_H__
 
+#include <linux/types.h>
+
 #include <drm/drm_device.h>
 #include <drm/drm_gem.h>
 
+struct loongson_gem {
+	/* @mutex: protect objects list */
+	struct mutex mutex;
+	struct list_head objects;
+};
+
+struct lsdc_bo;
+
+void lsdc_gem_list_add_lbo(struct drm_device *ddev, struct lsdc_bo *lbo);
+void lsdc_gem_list_rm_lbo(struct drm_device *ddev, struct lsdc_bo *lbo);
+
 struct drm_gem_object *
 lsdc_prime_import_sg_table(struct drm_device *ddev,
 			   struct dma_buf_attachment *attach,
diff --git a/drivers/gpu/drm/loongson/lsdc_gfxpll.c b/drivers/gpu/drm/loongson/lsdc_gfxpll.c
index 249c09d703ad..3ee978729fdd 100644
--- a/drivers/gpu/drm/loongson/lsdc_gfxpll.c
+++ b/drivers/gpu/drm/loongson/lsdc_gfxpll.c
@@ -10,6 +10,7 @@
 #include <drm/drm_print.h>
 
 #include "lsdc_drv.h"
+#include "lsdc_gfxpll.h"
 
 /*
  * GFX PLL is the PLL used by DC, GMC and GPU, the structure of the GFX PLL
@@ -143,8 +144,6 @@ static void loongson_gfxpll_fini(struct drm_device *ddev, void *data)
 	struct loongson_gfxpll *this = (struct loongson_gfxpll *)data;
 
 	iounmap(this->mmio);
-
-	kfree(this);
 }
 
 static int loongson_gfxpll_init(struct loongson_gfxpll * const this)
@@ -160,7 +159,7 @@ static int loongson_gfxpll_init(struct loongson_gfxpll * const this)
 
 	this->funcs->print(this, &printer, false);
 
-	return 0;
+	return drmm_add_action_or_reset(this->ddev, loongson_gfxpll_fini, this);
 }
 
 static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = {
@@ -170,30 +169,16 @@ static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = {
 	.print = loongson_gfxpll_print,
 };
 
-int loongson_gfxpll_create(struct drm_device *ddev,
-			   struct loongson_gfxpll **ppout)
+int loongson_gfxpll_preinit(struct drm_device *ddev)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
-	const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp);
-	struct loongson_gfxpll *this;
-	int ret;
-
-	this = kzalloc(sizeof(*this), GFP_KERNEL);
-	if (IS_ERR_OR_NULL(this))
-		return -ENOMEM;
+	struct loongson_drm *ldrm = to_loongson_drm(ddev);
+	const struct loongson_gfx_desc *gfxinfo = ldrm->gfxinfo;
+	struct loongson_gfxpll *this = &ldrm->gfxpll;
 
 	this->ddev = ddev;
-	this->reg_size = gfx->gfxpll.reg_size;
-	this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset;
+	this->reg_size = gfxinfo->gfxpll.reg_size;
+	this->reg_base = gfxinfo->conf_reg_base + gfxinfo->gfxpll.reg_offset;
 	this->funcs = &lsdc_gmc_gpu_funcs;
 
-	ret = this->funcs->init(this);
-	if (unlikely(ret)) {
-		kfree(this);
-		return ret;
-	}
-
-	*ppout = this;
-
-	return drmm_add_action_or_reset(ddev, loongson_gfxpll_fini, this);
+	return this->funcs->init(this);
 }
diff --git a/drivers/gpu/drm/loongson/lsdc_gfxpll.h b/drivers/gpu/drm/loongson/lsdc_gfxpll.h
index 9d59cbfc145d..470e20ec51bf 100644
--- a/drivers/gpu/drm/loongson/lsdc_gfxpll.h
+++ b/drivers/gpu/drm/loongson/lsdc_gfxpll.h
@@ -46,7 +46,6 @@ struct loongson_gfxpll {
 	struct loongson_gfxpll_parms parms;
 };
 
-int loongson_gfxpll_create(struct drm_device *ddev,
-			   struct loongson_gfxpll **ppout);
+int loongson_gfxpll_preinit(struct drm_device *ddev);
 
 #endif
diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.c b/drivers/gpu/drm/loongson/lsdc_i2c.c
index ce90c25536d2..a975e439dd13 100644
--- a/drivers/gpu/drm/loongson/lsdc_i2c.c
+++ b/drivers/gpu/drm/loongson/lsdc_i2c.c
@@ -6,7 +6,18 @@
 #include <drm/drm_managed.h>
 
 #include "lsdc_drv.h"
-#include "lsdc_output.h"
+#include "lsdc_i2c.h"
+
+struct lsdc_i2c {
+	struct i2c_adapter adapter;
+	struct i2c_algo_bit_data bit;
+	struct lsdc_device *lsdc;
+	void __iomem *dir_reg;
+	void __iomem *dat_reg;
+	/* pin bit mask */
+	u8 sda;
+	u8 scl;
+};
 
 /*
  * __lsdc_gpio_i2c_set - set the state of a gpio pin indicated by mask
@@ -15,7 +26,7 @@
  */
 static void __lsdc_gpio_i2c_set(struct lsdc_i2c * const li2c, int mask, int state)
 {
-	struct lsdc_device *ldev = to_lsdc(li2c->ddev);
+	struct lsdc_device *ldev = li2c->lsdc;
 	unsigned long flags;
 	u8 val;
 
@@ -51,7 +62,7 @@ static void __lsdc_gpio_i2c_set(struct lsdc_i2c * const li2c, int mask, int stat
  */
 static int __lsdc_gpio_i2c_get(struct lsdc_i2c * const li2c, int mask)
 {
-	struct lsdc_device *ldev = to_lsdc(li2c->ddev);
+	struct lsdc_device *ldev = li2c->lsdc;
 	unsigned long flags;
 	u8 val;
 
@@ -98,7 +109,7 @@ static int lsdc_gpio_i2c_get_scl(void *i2c)
 	return __lsdc_gpio_i2c_get(li2c, li2c->scl);
 }
 
-static void lsdc_destroy_i2c(struct drm_device *ddev, void *data)
+static void lsdc_destroy_i2c(void *data)
 {
 	struct lsdc_i2c *li2c = (struct lsdc_i2c *)data;
 
@@ -114,11 +125,10 @@ static void lsdc_destroy_i2c(struct drm_device *ddev, void *data)
  * @reg_base: gpio reg base
  * @index: output channel index, 0 for PIPE0, 1 for PIPE1
  */
-int lsdc_create_i2c_chan(struct drm_device *ddev,
-			 struct lsdc_display_pipe *dispipe,
-			 unsigned int index)
+int lsdc_create_i2c_chan(struct device *parent, unsigned int index,
+			 struct i2c_adapter **ppadapter)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
+	struct lsdc_device *ldev = dev_get_drvdata(parent);
 	struct i2c_adapter *adapter;
 	struct lsdc_i2c *li2c;
 	int ret;
@@ -127,8 +137,6 @@ int lsdc_create_i2c_chan(struct drm_device *ddev,
 	if (!li2c)
 		return -ENOMEM;
 
-	dispipe->li2c = li2c;
-
 	if (index == 0) {
 		li2c->sda = 0x01;  /* pin 0 */
 		li2c->scl = 0x02;  /* pin 1 */
@@ -139,7 +147,7 @@ int lsdc_create_i2c_chan(struct drm_device *ddev,
 		return -ENOENT;
 	}
 
-	li2c->ddev = ddev;
+	li2c->lsdc = ldev;
 	li2c->dir_reg = ldev->reg_base + LS7A_DC_GPIO_DIR_REG;
 	li2c->dat_reg = ldev->reg_base + LS7A_DC_GPIO_DAT_REG;
 
@@ -154,7 +162,7 @@ int lsdc_create_i2c_chan(struct drm_device *ddev,
 	adapter = &li2c->adapter;
 	adapter->algo_data = &li2c->bit;
 	adapter->owner = THIS_MODULE;
-	adapter->dev.parent = ddev->dev;
+	adapter->dev.parent = parent;
 	adapter->nr = -1;
 
 	snprintf(adapter->name, sizeof(adapter->name), "lsdc-i2c%u", index);
@@ -167,12 +175,29 @@ int lsdc_create_i2c_chan(struct drm_device *ddev,
 		return ret;
 	}
 
-	ret = drmm_add_action_or_reset(ddev, lsdc_destroy_i2c, li2c);
+	ret = devm_add_action_or_reset(parent, lsdc_destroy_i2c, li2c);
 	if (ret)
 		return ret;
 
-	drm_info(ddev, "%s(sda pin mask=%u, scl pin mask=%u) created\n",
-		 adapter->name, li2c->sda, li2c->scl);
+	*ppadapter = adapter;
+
+	dev_info(parent, "%s(sda pin mask=%u, scl pin mask=%u) created\n",
+		 dev_name(&adapter->dev), li2c->sda, li2c->scl);
+
+	return 0;
+}
+
+int lsdc_i2c_preinit(struct device *parent, const struct lsdc_desc *descp)
+{
+	struct lsdc_device *lsdc = dev_get_drvdata(parent);
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < descp->num_of_crtc; ++i) {
+		ret = descp->funcs->create_i2c(parent, i, &lsdc->i2c[i]);
+		if (ret)
+			return ret;
+	}
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.h b/drivers/gpu/drm/loongson/lsdc_i2c.h
index 88cd1a1817a5..4e0fa93bc187 100644
--- a/drivers/gpu/drm/loongson/lsdc_i2c.h
+++ b/drivers/gpu/drm/loongson/lsdc_i2c.h
@@ -9,21 +9,14 @@
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 
-struct lsdc_i2c {
-	struct i2c_adapter adapter;
-	struct i2c_algo_bit_data bit;
-	struct drm_device *ddev;
-	void __iomem *dir_reg;
-	void __iomem *dat_reg;
-	/* pin bit mask */
-	u8 sda;
-	u8 scl;
-};
+struct lsdc_device;
+struct lsdc_desc;
 
-struct lsdc_display_pipe;
+int lsdc_create_i2c_chan(struct device *parent,
+			 unsigned int index,
+			 struct i2c_adapter **ppadapter);
 
-int lsdc_create_i2c_chan(struct drm_device *ddev,
-			 struct lsdc_display_pipe *dispipe,
-			 unsigned int index);
+int lsdc_i2c_preinit(struct device *parent,
+		     const struct lsdc_desc *descp);
 
 #endif
diff --git a/drivers/gpu/drm/loongson/lsdc_output.c b/drivers/gpu/drm/loongson/lsdc_output.c
new file mode 100644
index 000000000000..2de1cea77590
--- /dev/null
+++ b/drivers/gpu/drm/loongson/lsdc_output.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Authors:
+ *      Sui Jingfeng <sui.jingfeng@linux.dev>
+ */
+
+#include <linux/delay.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "lsdc_drv.h"
+#include "lsdc_output.h"
+
+static struct property_entry ls7a1000_output_port_properties[LSDC_NUM_CRTC][3] = {
+	{
+		PROPERTY_ENTRY_STRING("type", "DVO"),
+		PROPERTY_ENTRY_U32("reg", 0),
+		{ },
+	},
+	{
+		PROPERTY_ENTRY_STRING("type", "DVO"),
+		PROPERTY_ENTRY_U32("reg", 1),
+		{ },
+	},
+};
+
+static struct property_entry ls7a2000_output_port_properties[LSDC_NUM_CRTC][3] = {
+	{
+		PROPERTY_ENTRY_STRING("type", "HDMI-or-VGA"),
+		PROPERTY_ENTRY_U32("reg", 0),
+		{ },
+	},
+	{
+		PROPERTY_ENTRY_STRING("type", "HDMI"),
+		PROPERTY_ENTRY_U32("reg", 1),
+		{ },
+	},
+};
+
+static struct property_entry *
+lsdc_output_get_property_entry(const struct lsdc_desc *descp,
+			       unsigned int index)
+{
+	if (index >= LSDC_NUM_CRTC)
+		return NULL;
+
+	switch (to_loongson_gfx(descp)->chip_id) {
+	case CHIP_LS7A1000:
+		return &ls7a1000_output_port_properties[index][0];
+	case CHIP_LS7A2000:
+		return &ls7a2000_output_port_properties[index][0];
+	default:
+		break;
+	};
+
+	return NULL;
+}
+
+static void lsdc_output_postfini(void *data)
+{
+	struct platform_device *agent = data;
+
+	platform_device_unregister(agent);
+}
+
+int lsdc_output_preinit(struct device *parent, const struct lsdc_desc *descp)
+{
+	unsigned int i;
+
+	for (i = 0; i < descp->num_of_crtc; ++i) {
+		struct platform_device_info devinfo = {};
+		struct platform_device *agent;
+		int ret;
+
+		devinfo.parent = parent;
+		devinfo.name = "lsdc.output.agent";
+		devinfo.id = i;
+		devinfo.properties = lsdc_output_get_property_entry(descp, i);
+
+		agent = platform_device_register_full(&devinfo);
+		if (IS_ERR(agent)) {
+			dev_err(parent, "failed to register output agent\n");
+			return PTR_ERR(agent);
+		}
+
+		ret = devm_add_action_or_reset(parent, lsdc_output_postfini, agent);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int lsdc_output_bind(struct device *dev,
+			    struct device *master,
+			    void *data)
+{
+	struct drm_device *drm = data;
+	struct lsdc_device *ldev = dev_get_drvdata(dev->parent);
+	const struct lsdc_kms_funcs *lkmsfun = ldev->descp->funcs;
+	struct lsdc_output *output = dev_get_drvdata(dev);
+	unsigned int pipe = output->pipe;
+	int ret;
+
+	ret = lkmsfun->output_init(drm, output, ldev->i2c[pipe], pipe);
+	if (ret)
+		return ret;
+
+	ldev->dispipe[pipe].output = output;
+	ldev->num_output++;
+
+	return 0;
+}
+
+static void lsdc_output_unbind(struct device *dev,
+			       struct device *master,
+			       void *data)
+{
+	struct lsdc_device *ldev = dev_get_drvdata(dev->parent);
+	struct lsdc_output *output = dev_get_drvdata(dev);
+	unsigned int pipe = output->pipe;
+
+	ldev->dispipe[pipe].output = NULL;
+}
+
+static const struct component_ops lsdc_output_component_ops = {
+	.bind = lsdc_output_bind,
+	.unbind = lsdc_output_unbind,
+};
+
+static void lsdc_output_destroy(void *data)
+{
+	struct lsdc_output *output = (struct lsdc_output *)data;
+
+	kfree(output);
+}
+
+static int lsdc_output_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device *parent = dev->parent;
+	struct lsdc_output *output;
+	const char *type;
+	int ret;
+
+	output = kzalloc(sizeof(*output), GFP_KERNEL);
+	if (!output)
+		return -ENOMEM;
+
+	ret = devm_add_action_or_reset(parent, lsdc_output_destroy, output);
+	if (ret)
+		return ret;
+
+	output->dev = dev;
+	dev_set_drvdata(dev, output);
+
+	ret = device_property_read_u32(dev, "reg", &output->pipe);
+	if (ret)
+		return ret;
+
+	ret = device_property_read_string(dev, "type", &type);
+	if (ret)
+		return ret;
+
+	dev_info(parent, "%s probed, type: %s\n", dev_name(dev), type);
+
+	return component_add(dev, &lsdc_output_component_ops);
+}
+
+static void lsdc_output_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &lsdc_output_component_ops);
+}
+
+struct platform_driver lsdc_output_platform_driver = {
+	.driver = {
+		.name = "lsdc.output.agent",
+	},
+	.probe = lsdc_output_probe,
+	.remove_new = lsdc_output_remove,
+};
diff --git a/drivers/gpu/drm/loongson/lsdc_output.h b/drivers/gpu/drm/loongson/lsdc_output.h
index 097789051a1d..795b58fd969a 100644
--- a/drivers/gpu/drm/loongson/lsdc_output.h
+++ b/drivers/gpu/drm/loongson/lsdc_output.h
@@ -6,16 +6,43 @@
 #ifndef __LSDC_OUTPUT_H__
 #define __LSDC_OUTPUT_H__
 
-#include "lsdc_drv.h"
+#include <linux/component.h>
+
+#include <drm/drm_encoder.h>
+#include <drm/drm_connector.h>
+
+struct lsdc_desc;
+
+struct lsdc_output {
+	struct device *dev;
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+	unsigned int pipe;
+};
+
+static inline struct lsdc_output *
+connector_to_lsdc_output(struct drm_connector *connector)
+{
+	return container_of(connector, struct lsdc_output, connector);
+}
+
+static inline struct lsdc_output *
+encoder_to_lsdc_output(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct lsdc_output, encoder);
+}
 
 int ls7a1000_output_init(struct drm_device *ddev,
-			 struct lsdc_display_pipe *dispipe,
+			 struct lsdc_output *output,
 			 struct i2c_adapter *ddc,
 			 unsigned int index);
 
 int ls7a2000_output_init(struct drm_device *ldev,
-			 struct lsdc_display_pipe *dispipe,
+			 struct lsdc_output *output,
 			 struct i2c_adapter *ddc,
 			 unsigned int index);
 
+int lsdc_output_preinit(struct device *parent,
+			const struct lsdc_desc *descp);
+
 #endif
diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c
index 6fc8dd1c7d9a..72e55ba29689 100644
--- a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c
+++ b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c
@@ -7,6 +7,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
 
+#include "loongson_drv.h"
 #include "lsdc_drv.h"
 #include "lsdc_output.h"
 
@@ -140,11 +141,10 @@ static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = {
 };
 
 int ls7a1000_output_init(struct drm_device *ddev,
-			 struct lsdc_display_pipe *dispipe,
+			 struct lsdc_output *output,
 			 struct i2c_adapter *ddc,
 			 unsigned int index)
 {
-	struct lsdc_output *output = &dispipe->output;
 	struct drm_encoder *encoder = &output->encoder;
 	struct drm_connector *connector = &output->connector;
 	int ret;
@@ -162,8 +162,6 @@ int ls7a1000_output_init(struct drm_device *ddev,
 	if (ret)
 		return ret;
 
-	drm_info(ddev, "display pipe-%u has a DVO\n", index);
-
 	drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers);
 
 	drm_connector_attach_encoder(connector, encoder);
diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c
index ce3dabec887e..7a1782b61938 100644
--- a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c
+++ b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c
@@ -10,6 +10,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
 
+#include "loongson_drv.h"
 #include "lsdc_drv.h"
 #include "lsdc_output.h"
 
@@ -284,8 +285,7 @@ static int ls7a2000_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
 					   struct drm_display_mode *mode)
 {
 	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
-	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
-	unsigned int index = dispipe->index;
+	unsigned int index = output->pipe;
 	struct drm_device *ddev = encoder->dev;
 	struct lsdc_device *ldev = to_lsdc(ddev);
 	struct hdmi_avi_infoframe infoframe;
@@ -335,8 +335,7 @@ static void ls7a2000_hdmi_atomic_disable(struct drm_encoder *encoder,
 					 struct drm_atomic_state *state)
 {
 	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
-	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
-	unsigned int index = dispipe->index;
+	unsigned int index = output->pipe;
 	struct drm_device *ddev = encoder->dev;
 	struct lsdc_device *ldev = to_lsdc(ddev);
 	u32 val;
@@ -360,8 +359,7 @@ static void ls7a2000_hdmi_atomic_enable(struct drm_encoder *encoder,
 	struct drm_device *ddev = encoder->dev;
 	struct lsdc_device *ldev = to_lsdc(ddev);
 	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
-	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
-	unsigned int index = dispipe->index;
+	unsigned int index = output->pipe;
 	u32 val;
 
 	/* datasheet say it should larger than 48 */
@@ -415,7 +413,7 @@ static void ls7a2000_hdmi_phy_pll_config(struct lsdc_device *ldev,
 					 int fin,
 					 unsigned int index)
 {
-	struct drm_device *ddev = &ldev->base;
+	struct drm_device *ddev = ldev->drm;
 	int count = 0;
 	u32 val;
 
@@ -482,8 +480,7 @@ static void ls7a2000_hdmi_atomic_mode_set(struct drm_encoder *encoder,
 					  struct drm_connector_state *conn_state)
 {
 	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
-	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
-	unsigned int index = dispipe->index;
+	unsigned int index = output->pipe;
 	struct drm_device *ddev = encoder->dev;
 	struct lsdc_device *ldev = to_lsdc(ddev);
 	struct drm_display_mode *mode = &crtc_state->mode;
@@ -512,11 +509,10 @@ static const struct drm_encoder_helper_funcs ls7a2000_encoder_helper_funcs = {
  * writing hdmi register do no harms.
  */
 int ls7a2000_output_init(struct drm_device *ddev,
-			 struct lsdc_display_pipe *dispipe,
+			 struct lsdc_output *output,
 			 struct i2c_adapter *ddc,
 			 unsigned int pipe)
 {
-	struct lsdc_output *output = &dispipe->output;
 	struct drm_encoder *encoder = &output->encoder;
 	struct drm_connector *connector = &output->connector;
 	int ret;
@@ -536,8 +532,6 @@ int ls7a2000_output_init(struct drm_device *ddev,
 	if (ret)
 		return ret;
 
-	drm_info(ddev, "display pipe-%u has HDMI %s\n", pipe, pipe ? "" : "and/or VGA");
-
 	drm_connector_helper_add(connector, &ls7a2000_connector_helpers);
 
 	drm_connector_attach_encoder(connector, encoder);
diff --git a/drivers/gpu/drm/loongson/lsdc_pixpll.c b/drivers/gpu/drm/loongson/lsdc_pixpll.c
index 2609a2256da4..a589a63e39ff 100644
--- a/drivers/gpu/drm/loongson/lsdc_pixpll.c
+++ b/drivers/gpu/drm/loongson/lsdc_pixpll.c
@@ -470,9 +470,7 @@ int lsdc_pixpll_init(struct lsdc_pixpll * const this,
 		     struct drm_device *ddev,
 		     unsigned int index)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
-	const struct lsdc_desc *descp = ldev->descp;
-	const struct loongson_gfx_desc *gfx = to_loongson_gfx(descp);
+	const struct loongson_gfx_desc *gfx = to_loongson_gfxinfo(ddev);
 
 	this->ddev = ddev;
 	this->reg_size = 8;
diff --git a/drivers/gpu/drm/loongson/lsdc_plane.c b/drivers/gpu/drm/loongson/lsdc_plane.c
index d227a2c1dcf1..c25e13184107 100644
--- a/drivers/gpu/drm/loongson/lsdc_plane.c
+++ b/drivers/gpu/drm/loongson/lsdc_plane.c
@@ -10,6 +10,7 @@
 #include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 
+#include "loongson_drv.h"
 #include "lsdc_drv.h"
 #include "lsdc_regs.h"
 #include "lsdc_ttm.h"
@@ -40,10 +41,9 @@ static unsigned int lsdc_get_fb_offset(struct drm_framebuffer *fb,
 
 static u64 lsdc_fb_base_addr(struct drm_framebuffer *fb)
 {
-	struct lsdc_device *ldev = to_lsdc(fb->dev);
 	struct lsdc_bo *lbo = gem_to_lsdc_bo(fb->obj[0]);
 
-	return lsdc_bo_gpu_offset(lbo) + ldev->vram_base;
+	return lsdc_bo_gpu_offset(lbo) + loongson_drm_vram_base(fb->dev);
 }
 
 static int lsdc_primary_atomic_check(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/loongson/lsdc_ttm.c b/drivers/gpu/drm/loongson/lsdc_ttm.c
index 465f622ac05d..0d1887ecf987 100644
--- a/drivers/gpu/drm/loongson/lsdc_ttm.c
+++ b/drivers/gpu/drm/loongson/lsdc_ttm.c
@@ -9,6 +9,7 @@
 #include <drm/drm_managed.h>
 #include <drm/drm_prime.h>
 
+#include "loongson_drv.h"
 #include "lsdc_drv.h"
 #include "lsdc_ttm.h"
 
@@ -219,7 +220,7 @@ static int lsdc_bo_move(struct ttm_buffer_object *tbo,
 static int lsdc_bo_reserve_io_mem(struct ttm_device *bdev,
 				  struct ttm_resource *mem)
 {
-	struct lsdc_device *ldev = tdev_to_ldev(bdev);
+	struct loongson_drm *ldrm = tdev_to_ldrm(bdev);
 
 	switch (mem->mem_type) {
 	case TTM_PL_SYSTEM:
@@ -227,7 +228,7 @@ static int lsdc_bo_reserve_io_mem(struct ttm_device *bdev,
 	case TTM_PL_TT:
 		break;
 	case TTM_PL_VRAM:
-		mem->bus.offset = (mem->start << PAGE_SHIFT) + ldev->vram_base;
+		mem->bus.offset = (mem->start << PAGE_SHIFT) + ldrm->vram_base;
 		mem->bus.is_iomem = true;
 		mem->bus.caching = ttm_write_combined;
 		break;
@@ -287,7 +288,7 @@ int lsdc_bo_pin(struct lsdc_bo *lbo, u32 domain, u64 *gpu_addr)
 {
 	struct ttm_operation_ctx ctx = { false, false };
 	struct ttm_buffer_object *tbo = &lbo->tbo;
-	struct lsdc_device *ldev = tdev_to_ldev(tbo->bdev);
+	struct loongson_drm *ldrm = tdev_to_ldrm(tbo->bdev);
 	int ret;
 
 	if (tbo->pin_count)
@@ -301,14 +302,14 @@ int lsdc_bo_pin(struct lsdc_bo *lbo, u32 domain, u64 *gpu_addr)
 
 	ret = ttm_bo_validate(tbo, &lbo->placement, &ctx);
 	if (unlikely(ret)) {
-		drm_err(&ldev->base, "%p validate failed: %d\n", lbo, ret);
+		drm_err(&ldrm->ddev, "%p validate failed: %d\n", lbo, ret);
 		return ret;
 	}
 
 	if (domain == LSDC_GEM_DOMAIN_VRAM)
-		ldev->vram_pinned_size += lsdc_bo_size(lbo);
+		ldrm->vram_pinned_size += lsdc_bo_size(lbo);
 	else if (domain == LSDC_GEM_DOMAIN_GTT)
-		ldev->gtt_pinned_size += lsdc_bo_size(lbo);
+		ldrm->gtt_pinned_size += lsdc_bo_size(lbo);
 
 bo_pinned:
 	ttm_bo_pin(tbo);
@@ -322,10 +323,10 @@ int lsdc_bo_pin(struct lsdc_bo *lbo, u32 domain, u64 *gpu_addr)
 void lsdc_bo_unpin(struct lsdc_bo *lbo)
 {
 	struct ttm_buffer_object *tbo = &lbo->tbo;
-	struct lsdc_device *ldev = tdev_to_ldev(tbo->bdev);
+	struct loongson_drm *ldrm = tdev_to_ldrm(tbo->bdev);
 
 	if (unlikely(!tbo->pin_count)) {
-		drm_dbg(&ldev->base, "%p unpin is not necessary\n", lbo);
+		drm_dbg(&ldrm->ddev, "%p unpin is not necessary\n", lbo);
 		return;
 	}
 
@@ -333,9 +334,9 @@ void lsdc_bo_unpin(struct lsdc_bo *lbo)
 
 	if (!tbo->pin_count) {
 		if (tbo->resource->mem_type == TTM_PL_VRAM)
-			ldev->vram_pinned_size -= lsdc_bo_size(lbo);
+			ldrm->vram_pinned_size -= lsdc_bo_size(lbo);
 		else if (tbo->resource->mem_type == TTM_PL_TT)
-			ldev->gtt_pinned_size -= lsdc_bo_size(lbo);
+			ldrm->gtt_pinned_size -= lsdc_bo_size(lbo);
 	}
 }
 
@@ -405,8 +406,8 @@ void lsdc_bo_clear(struct lsdc_bo *lbo)
 
 int lsdc_bo_evict_vram(struct drm_device *ddev)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
-	struct ttm_device *bdev = &ldev->bdev;
+	struct loongson_drm *ldrm = to_loongson_drm(ddev);
+	struct ttm_device *bdev = &ldrm->bdev;
 	struct ttm_resource_manager *man;
 
 	man = ttm_manager_type(bdev, TTM_PL_VRAM);
@@ -418,14 +419,12 @@ int lsdc_bo_evict_vram(struct drm_device *ddev)
 
 static void lsdc_bo_destroy(struct ttm_buffer_object *tbo)
 {
-	struct lsdc_device *ldev = tdev_to_ldev(tbo->bdev);
+	struct drm_gem_object *gobj = &tbo->base;
 	struct lsdc_bo *lbo = to_lsdc_bo(tbo);
 
-	mutex_lock(&ldev->gem.mutex);
-	list_del_init(&lbo->list);
-	mutex_unlock(&ldev->gem.mutex);
+	lsdc_gem_list_rm_lbo(gobj->dev, lbo);
 
-	drm_gem_object_release(&tbo->base);
+	drm_gem_object_release(gobj);
 
 	kfree(lbo);
 }
@@ -437,8 +436,7 @@ struct lsdc_bo *lsdc_bo_create(struct drm_device *ddev,
 			       struct sg_table *sg,
 			       struct dma_resv *resv)
 {
-	struct lsdc_device *ldev = to_lsdc(ddev);
-	struct ttm_device *bdev = &ldev->bdev;
+	struct ttm_device *bdev = to_tdev(ddev);
 	struct ttm_buffer_object *tbo;
 	struct lsdc_bo *lbo;
 	enum ttm_bo_type bo_type;
@@ -529,55 +527,57 @@ void lsdc_bo_free_kernel_pinned(struct lsdc_bo *lbo)
 
 static void lsdc_ttm_fini(struct drm_device *ddev, void *data)
 {
-	struct lsdc_device *ldev = (struct lsdc_device *)data;
+	struct loongson_drm *ldrm = data;
+	struct ttm_device *bdev = &ldrm->bdev;
 
-	ttm_range_man_fini(&ldev->bdev, TTM_PL_VRAM);
-	ttm_range_man_fini(&ldev->bdev, TTM_PL_TT);
+	ttm_range_man_fini(bdev, TTM_PL_VRAM);
+	ttm_range_man_fini(bdev, TTM_PL_TT);
 
-	ttm_device_fini(&ldev->bdev);
+	ttm_device_fini(bdev);
 
 	drm_dbg(ddev, "ttm finished\n");
 }
 
-int lsdc_ttm_init(struct lsdc_device *ldev)
+int lsdc_ttm_init(struct drm_device *ddev)
 {
-	struct drm_device *ddev = &ldev->base;
+	struct loongson_drm *ldrm = to_loongson_drm(ddev);
+	struct ttm_device *bdev = &ldrm->bdev;
 	unsigned long num_vram_pages;
 	unsigned long num_gtt_pages;
 	int ret;
 
-	ret = ttm_device_init(&ldev->bdev, &lsdc_bo_driver, ddev->dev,
+	ret = ttm_device_init(bdev, &lsdc_bo_driver, ddev->dev,
 			      ddev->anon_inode->i_mapping,
 			      ddev->vma_offset_manager, false, true);
 	if (ret)
 		return ret;
 
-	num_vram_pages = ldev->vram_size >> PAGE_SHIFT;
+	num_vram_pages = ldrm->vram_size >> PAGE_SHIFT;
 
-	ret = ttm_range_man_init(&ldev->bdev, TTM_PL_VRAM, false, num_vram_pages);
+	ret = ttm_range_man_init(bdev, TTM_PL_VRAM, false, num_vram_pages);
 	if (unlikely(ret))
 		return ret;
 
 	drm_info(ddev, "VRAM: %lu pages ready\n", num_vram_pages);
 
 	/* 512M is far enough for us now */
-	ldev->gtt_size = 512 << 20;
+	ldrm->gtt_size = 512 << 20;
 
-	num_gtt_pages = ldev->gtt_size >> PAGE_SHIFT;
+	num_gtt_pages = ldrm->gtt_size >> PAGE_SHIFT;
 
-	ret = ttm_range_man_init(&ldev->bdev, TTM_PL_TT, true, num_gtt_pages);
+	ret = ttm_range_man_init(bdev, TTM_PL_TT, true, num_gtt_pages);
 	if (unlikely(ret))
 		return ret;
 
 	drm_info(ddev, "GTT: %lu pages ready\n", num_gtt_pages);
 
-	return drmm_add_action_or_reset(ddev, lsdc_ttm_fini, ldev);
+	return drmm_add_action_or_reset(ddev, lsdc_ttm_fini, ldrm);
 }
 
-void lsdc_ttm_debugfs_init(struct lsdc_device *ldev)
+void lsdc_ttm_debugfs_init(struct drm_device *ddev)
 {
-	struct ttm_device *bdev = &ldev->bdev;
-	struct drm_device *ddev = &ldev->base;
+	struct ttm_device *bdev = to_tdev(ddev);
+
 	struct drm_minor *minor = ddev->primary;
 	struct dentry *root = minor->debugfs_root;
 	struct ttm_resource_manager *vram_man;
diff --git a/drivers/gpu/drm/loongson/lsdc_ttm.h b/drivers/gpu/drm/loongson/lsdc_ttm.h
index 843e1475064e..2825cec82172 100644
--- a/drivers/gpu/drm/loongson/lsdc_ttm.h
+++ b/drivers/gpu/drm/loongson/lsdc_ttm.h
@@ -93,7 +93,7 @@ void lsdc_bo_clear(struct lsdc_bo *lbo);
 
 int lsdc_bo_evict_vram(struct drm_device *ddev);
 
-int lsdc_ttm_init(struct lsdc_device *ldev);
-void lsdc_ttm_debugfs_init(struct lsdc_device *ldev);
+int lsdc_ttm_init(struct drm_device *ddev);
+void lsdc_ttm_debugfs_init(struct drm_device *ddev);
 
 #endif
