+
+static void lsdc_update_pixclk(struct drm_crtc *crtc, unsigned int pixclk, bool verbose)
+{
+ struct lsdc_display_pipe *dispipe;
+ struct lsdc_pll *pixpll;
+ const struct lsdc_pixpll_funcs *clkfun;
+ struct lsdc_crtc_state *priv_crtc_state;
+
+ priv_crtc_state = to_lsdc_crtc_state(crtc->state);
+
+ dispipe = container_of(crtc, struct lsdc_display_pipe, crtc);
+ pixpll = &dispipe->pixpll;
+ clkfun = pixpll->funcs;
+
+ /* config the pixel pll */
+ clkfun->update(pixpll, &priv_crtc_state->pparams);
+
+ if (verbose)
+ clkfun->print(pixpll, pixclk);
+}
+
+
+static void lsdc_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct drm_device *ddev = crtc->dev;
+ struct lsdc_device *ldev = to_lsdc(ddev);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ unsigned int hr = mode->hdisplay;
+ unsigned int hss = mode->hsync_start;
+ unsigned int hse = mode->hsync_end;
+ unsigned int hfl = mode->htotal;
+ unsigned int vr = mode->vdisplay;
+ unsigned int vss = mode->vsync_start;
+ unsigned int vse = mode->vsync_end;
+ unsigned int vfl = mode->vtotal;
+ unsigned int pixclock = mode->clock;
+ unsigned int index = drm_crtc_index(crtc);
+
+
+ if (index == 0) {
+ /* CRTC 0 */
+ u32 hsync, vsync;
+
+ lsdc_reg_write32(ldev, LSDC_CRTC0_FB_ORIGIN_REG, 0);
+
+ /* 26:16 total pixels, 10:0 visiable pixels, in horizontal */
+ lsdc_reg_write32(ldev, LSDC_CRTC0_HDISPLAY_REG,
+ (mode->crtc_htotal << 16) | mode->crtc_hdisplay);
+
+ /* 26:16 total pixels, 10:0 visiable pixels, in vertical */
+ lsdc_reg_write32(ldev, LSDC_CRTC0_VDISPLAY_REG,
+ (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);
+
+ /* 26:16 hsync end, 10:0 hsync start */
+ hsync = (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start;
+
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ hsync |= INV_HSYNC_BIT;
+
+ lsdc_reg_write32(ldev, LSDC_CRTC0_HSYNC_REG, EN_HSYNC_BIT | hsync);
+
+ /* 26:16 vsync end, 10:0 vsync start */
+ vsync = (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start;
+
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ vsync |= INV_VSYNC_BIT;
+
+ lsdc_reg_write32(ldev, LSDC_CRTC0_VSYNC_REG, EN_VSYNC_BIT | vsync);
+
+ } else if (index == 1) {
+ /* CRTC 1 */
+ u32 hsync, vsync;
+
+ lsdc_reg_write32(ldev, LSDC_CRTC1_FB_ORIGIN_REG, 0);
+
+ /* 26:16 total pixels, 10:0 visiable pixels, in horizontal */
+ lsdc_reg_write32(ldev, LSDC_CRTC1_HDISPLAY_REG,
+ (mode->crtc_htotal << 16) | mode->crtc_hdisplay);
+
+ /* 26:16 total pixels, 10:0 visiable pixels, in vertical */
+ lsdc_reg_write32(ldev, LSDC_CRTC1_VDISPLAY_REG,
+ (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);
+
+ /* 26:16 hsync end, 10:0 hsync start */
+ hsync = (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start;
+
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ hsync |= INV_HSYNC_BIT;
+
+ lsdc_reg_write32(ldev, LSDC_CRTC1_HSYNC_REG, EN_HSYNC_BIT | hsync);
+
+ /* 26:16 vsync end, 10:0 vsync start */
+ vsync = (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start;
+
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ vsync |= INV_VSYNC_BIT;
+
+ lsdc_reg_write32(ldev, LSDC_CRTC1_VSYNC_REG, EN_VSYNC_BIT | vsync);
+ }
+
+ drm_dbg_kms(ddev, "hdisplay=%d, hsync_start=%d, hsync_end=%d, htotal=%d\n",
+ hr, hss, hse, hfl);
+
+ drm_dbg_kms(ddev, "vdisplay=%d, vsync_start=%d, vsync_end=%d, vtotal=%d\n",
+ vr, vss, vse, vfl);
+
+ drm_dbg_kms(ddev, "%s modeset: %ux%u\n", crtc->name, hr, vr);
+
+ lsdc_update_pixclk(crtc, pixclock, ldev->verbose);
+}
+
+
+static void lsdc_enable_display(struct lsdc_device *ldev, unsigned int index)
+{
+ u32 val;
+
+ if (index == 0) {
+ val = lsdc_reg_read32(ldev, LSDC_CRTC0_CFG_REG);
+ val |= CFG_OUTPUT_EN_BIT;
+ lsdc_reg_write32(ldev, LSDC_CRTC0_CFG_REG, val);
+ } else if (index == 1) {
+ val = lsdc_reg_read32(ldev, LSDC_CRTC1_CFG_REG);
+ val |= CFG_OUTPUT_EN_BIT;
+ lsdc_reg_write32(ldev, LSDC_CRTC1_CFG_REG, val);
+ }
+}
+
+
+static void lsdc_disable_display(struct lsdc_device *ldev, unsigned int index)
+{
+ u32 val;
+
+ if (index == 0) {
+ val = lsdc_reg_read32(ldev, LSDC_CRTC0_CFG_REG);
+ val &= ~CFG_OUTPUT_EN_BIT;
+ lsdc_reg_write32(ldev, LSDC_CRTC0_CFG_REG, val);
+ } else if (index == 1) {
+ val = lsdc_reg_read32(ldev, LSDC_CRTC1_CFG_REG);
+ val &= ~CFG_OUTPUT_EN_BIT;
+ lsdc_reg_write32(ldev, LSDC_CRTC1_CFG_REG, val);
+ }
+}
+
+
+static void lsdc_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *ddev = crtc->dev;
+ struct lsdc_device *ldev = to_lsdc(ddev);
+
+ drm_crtc_vblank_on(crtc);
+
+ lsdc_enable_display(ldev, drm_crtc_index(crtc));
+
+ drm_dbg_kms(ddev, "%s: enabled\n", crtc->name);
+}
+
+
+static void lsdc_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *ddev = crtc->dev;
+ struct lsdc_device *ldev = to_lsdc(ddev);
+
+ drm_crtc_vblank_off(crtc);
+
+ lsdc_disable_display(ldev, drm_crtc_index(crtc));
+
+ drm_dbg_kms(ddev, "%s: disabled\n", crtc->name);
+}
+
+
+static void lsdc_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ if (event) {
+ crtc->state->event = NULL;
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ }
+}
+
+
+static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = {
+ .mode_valid = lsdc_crtc_helper_mode_valid,
+ .mode_set_nofb = lsdc_crtc_helper_mode_set_nofb,
+ .atomic_enable = lsdc_crtc_helper_atomic_enable,
+ .atomic_disable = lsdc_crtc_helper_atomic_disable,
+ .atomic_flush = lsdc_crtc_atomic_flush,
+};
+
+
+
+/**
+ * lsdc_crtc_init
+ *
+ * @ddev: point to the drm_device structure
+ * @index: hardware crtc index
+ *
+ * Init CRTC
+ */
+int lsdc_crtc_init(struct drm_device *ddev,
+ struct drm_crtc *crtc,
+ unsigned int index,
+ struct drm_plane *primary,
+ struct drm_plane *cursor)
+{
+ int ret;
+
+ drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs);
+
+ ret = drm_mode_crtc_set_gamma_size(crtc, 256);
+ if (ret)
+ drm_warn(ddev, "set the gamma table size failed\n");
+
+ return drm_crtc_init_with_planes(ddev,
+ crtc,
+ primary,
+ cursor,
+ &lsdc_crtc_funcs,
+ "crtc%d",
+ index);
+}
diff --git a/drivers/gpu/drm/lsdc/lsdc_drv.c b/drivers/gpu/drm/lsdc/lsdc_drv.c
new file mode 100644
index 000000000000..aac8901c3431
--- /dev/null
+++ b/drivers/gpu/drm/lsdc/lsdc_drv.c
@@ -0,0 +1,846 @@
+// 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.
+ */
+
+/*
+ * Authors:
+ * Sui Jingfeng <suijingfeng@xxxxxxxxxxx>
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/of_reserved_mem.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_aperture.h>
+#include <drm/drm_of.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_probe_helper.h>
+
+#include "lsdc_drv.h"
+#include "lsdc_irq.h"
+#include "lsdc_regs.h"
+#include "lsdc_connector.h"
+#include "lsdc_pll.h"
+
+
+#define DRIVER_AUTHOR "Sui Jingfeng <suijingfeng@xxxxxxxxxxx>"
+#define DRIVER_NAME "lsdc"
+#define DRIVER_DESC "drm driver for loongson's display controller"
+#define DRIVER_DATE "20200701"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+static int lsdc_modeset = 1;
+MODULE_PARM_DESC(modeset, "Enable/disable CMA-based KMS(1 = enabled(default), 0 = disabled)");
+module_param_named(modeset, lsdc_modeset, int, 0644);
+
+static int lsdc_cached_coherent = 1;
+MODULE_PARM_DESC(cached_coherent, "uss cached coherent mapping(1 = enabled(default), 0 = disabled)");
+module_param_named(cached_coherent, lsdc_cached_coherent, int, 0644);
+
+static int lsdc_dirty_update = -1;
+MODULE_PARM_DESC(dirty_update, "enable dirty update(1 = enabled, 0 = disabled(default))");
+module_param_named(dirty_update, lsdc_dirty_update, int, 0644);
+
+static int lsdc_use_vram_helper = -1;
+MODULE_PARM_DESC(use_vram_helper, "use vram helper based solution(1 = enabled, 0 = disabled(default))");
+module_param_named(use_vram_helper, lsdc_use_vram_helper, int, 0644);
+
+static int lsdc_verbose = -1;
+MODULE_PARM_DESC(verbose, "Enable/disable print some key information");
+module_param_named(verbose, lsdc_verbose, int, 0644);