diff --git a/drivers/gpu/drm/lsdc/Makefile b/drivers/gpu/drm/lsdc/MakefileThat's the MIT license, yet you claim the driver to be licensed under
new file mode 100644
index 000000000000..342990654478
--- /dev/null
+++ b/drivers/gpu/drm/lsdc/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the lsdc drm device driver.
+#
+
+lsdc-y := \
+ lsdc_drv.o \
+ lsdc_crtc.o \
+ lsdc_irq.o \
+ lsdc_plane.o \
+ lsdc_pll.o \
+ lsdc_i2c.o \
+ lsdc_encoder.o \
+ lsdc_connector.o
+
+obj-$(CONFIG_DRM_LSDC) += lsdc.o
diff --git a/drivers/gpu/drm/lsdc/lsdc_connector.c b/drivers/gpu/drm/lsdc/lsdc_connector.c
new file mode 100644
index 000000000000..ae5fc0c90961
--- /dev/null
+++ b/drivers/gpu/drm/lsdc/lsdc_connector.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Loongson Corporation
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
the GPLv2 or later?
+You don't have a device tree binding for that driver, this is something
+/*
+ * Authors:
+ * Sui Jingfeng <suijingfeng@xxxxxxxxxxx>
+ */
+
+#include <drm/drm_print.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_connector.h>
+
+#include <video/videomode.h>
+#include <video/of_display_timing.h>
+
+#include "lsdc_drv.h"
+#include "lsdc_i2c.h"
+#include "lsdc_connector.h"
+
+
+static int lsdc_get_modes_from_edid(struct drm_connector *connector)
+{
+ struct drm_device *ddev = connector->dev;
+ struct lsdc_connector *lconn = to_lsdc_connector(connector);
+ struct edid *edid_p = (struct edid *)lconn->edid_data;
+ int num = drm_add_edid_modes(connector, edid_p);
+
+ if (num)
+ drm_connector_update_edid_property(connector, edid_p);
+
+ drm_dbg_kms(ddev, "%d modes added\n", num);
+
+ return num;
+}
+
+
+static int lsdc_get_modes_from_timings(struct drm_connector *connector)
+{
+ struct drm_device *ddev = connector->dev;
+ struct lsdc_connector *lconn = to_lsdc_connector(connector);
+ struct display_timings *disp_tim = lconn->disp_tim;
+ unsigned int num = 0;
+ unsigned int i;
+
+ for (i = 0; i < disp_tim->num_timings; i++) {
+ const struct display_timing *dt = disp_tim->timings[i];
+ struct drm_display_mode *mode;
+ struct videomode vm;
+
+ videomode_from_timing(dt, &vm);
+ mode = drm_mode_create(ddev);
+ if (!mode) {
+ drm_err(ddev, "failed to add mode %ux%u\n",
+ dt->hactive.typ, dt->vactive.typ);
+ continue;
+ }
+
+ drm_display_mode_from_videomode(&vm, mode);
+
+ mode->type |= DRM_MODE_TYPE_DRIVER;
+
+ if (i == disp_tim->native_mode)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(connector, mode);
+ num++;
+ }
+
+ drm_dbg_kms(ddev, "%d modes added\n", num);
+
+ return num;
+}
+
+
+static int lsdc_get_modes_from_ddc(struct drm_connector *connector,
+ struct i2c_adapter *ddc)
+{
+ unsigned int num = 0;
+ struct edid *edid;
+
+ edid = drm_get_edid(connector, ddc);
+ if (edid) {
+ drm_connector_update_edid_property(connector, edid);
+ num = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+ }
+
+ return num;
+}
+
+
+static int lsdc_get_modes(struct drm_connector *connector)
+{
+ struct lsdc_connector *lconn = to_lsdc_connector(connector);
+ unsigned int num = 0;
+
+ if (lconn->has_edid)
+ return lsdc_get_modes_from_edid(connector);
+
+ if (lconn->has_disp_tim)
+ return lsdc_get_modes_from_timings(connector);
+
+ if (IS_ERR_OR_NULL(lconn->ddc) == false)
+ return lsdc_get_modes_from_ddc(connector, lconn->ddc);
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_VIRTUAL) {
+ num = drm_add_modes_noedid(connector,
+ connector->dev->mode_config.max_width,
+ connector->dev->mode_config.max_height);
+
+ drm_set_preferred_mode(connector, 1024, 768);
+
+ return num;
+ }
+
+
+ /*
+ * In case we cannot retrieve the EDIDs (broken or missing i2c
+ * bus), fallback on the XGA standards, because we are for board
+ * bringup.
+ */
+ num = drm_add_modes_noedid(connector, 1920, 1200);
+
+ /* And prefer a mode pretty much anyone can handle */
+ drm_set_preferred_mode(connector, 1024, 768);
+
+ return num;
+
+}
+
+
+static enum drm_connector_status
+lsdc_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct lsdc_connector *lconn = to_lsdc_connector(connector);
+
+ if (lconn->has_edid == true)
+ return connector_status_connected;
+
+ if (lconn->has_disp_tim == true)
+ return connector_status_connected;
+
+ if (IS_ERR_OR_NULL(lconn->ddc) == false)
+ return drm_probe_ddc(lconn->ddc);
+
+ if (lconn->ddc && drm_probe_ddc(lconn->ddc))
+ return connector_status_connected;
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_VIRTUAL)
+ return connector_status_connected;
+
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_DVIA) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_DVID) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_DVII))
+ return connector_status_disconnected;
+
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_HDMIA) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIB))
+ return connector_status_disconnected;
+
+ return connector_status_unknown;
+}
+
+
+/*
+ * @connector: point to the drm_connector structure
+ *
+ * Clean up connector resources
+ */
+static void lsdc_connector_destroy(struct drm_connector *connector)
+{
+ struct drm_device *ddev = connector->dev;
+ struct lsdc_connector *lconn = to_lsdc_connector(connector);
+
+ if (lconn) {
+ if (lconn->ddc)
+ lsdc_destroy_i2c(connector->dev, lconn->ddc);
+
+ drm_info(ddev, "destroying connector%u\n", lconn->index);
+
+ devm_kfree(ddev->dev, lconn);
+ }
+
+ drm_connector_cleanup(connector);
+}
+
+
+static const struct drm_connector_helper_funcs lsdc_connector_helpers = {
+ .get_modes = lsdc_get_modes,
+};
+
+/*
+ * These provide the minimum set of functions required to handle a connector
+ *
+ * Control connectors on a given device.
+ *
+ * Each CRTC may have one or more connectors attached to it.
+ * The functions below allow the core DRM code to control
+ * connectors, enumerate available modes, etc.
+ */
+static const struct drm_connector_funcs lsdc_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = lsdc_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = lsdc_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+
+/* Get the simple EDID data from the device tree
+ * the length must be EDID_LENGTH, since it is simple.
+ *
+ * @np: device node contain edid data
+ * @edid_data: where the edid data to store to
+ */
+static bool lsdc_get_edid_from_dtb(struct device_node *np,
+ unsigned char *edid_data)
+{
+ int length;
+ const void *prop;
+
+ if (np == NULL)
+ return false;
+
+ prop = of_get_property(np, "edid", &length);
+ if (prop && (length == EDID_LENGTH)) {
+ memcpy(edid_data, prop, EDID_LENGTH);
+ return true;
+ }
+
+ return false;
+}
that is required. And it's not clear to me why you'd want EDID in the
DTB?