2022-06-25 09:31:14

by 李晨阳

[permalink] [raw]
Subject: [PATCH v7 1/4] drm/loongson: Add DRM Driver for Loongson 7A1000 bridge chip

This patch adds an initial DRM driver for the Loongson LS7A1000
bridge chip(LS7A). The Loongson 7A1000 bridge chip is Loongson’s
first dedicated chipset product, providing north-south bridge functionality
for Loongson processors. The bridge chip is connected to the Loongson
Series 3 processor via the HT high-speed bus interface and has an
integrated DisplayController, DDR3 SDRAM memory controller,
and PCIE, SATA, USB, GMAC, I2C, UART, GPIO, and other interfaces.
The display controller supports dual DVO signal output,dual routability
frame buffer. Each display supports up to 1920x1080@60Hz, RGB888 and
RGB565 color depths. And supports hardware cursor, gamma correction,
interrupt, soft reset, etc. The video memory interface uses 16-bit DDR3
SDRAM interface with a maximum data rate of 1333 Mbps.
At present, DC device detection and DRM driver registration are
completed, the crtc/plane/encoder/connector objects has been
implemented.
On Loongson 3A4000 CPU and 7A1000 system, we have achieved the use
of dual screen, and support dual screen clone mode and expansion
mode.
Link: https://loongson.github.io/LoongArch-Documentation/Loongson-7A1000-usermanual-EN.html#display-controller

v12:
- Use drm_bridge_connector in driver.

v11:
- Remove a lot of useless code.
- Add help information.
- Delete unnecessary header files.

v10:
- Replace the drmm_ version functions.
- Replace the simple_encoder version function.
- Alphabetize file names.

v9:
- Optimize the error handling process.
- Remove the useless flags parameter.
- Fix some incorrect use of variables and constructs.

v8:
- Update the atomic_update function interface.

v7:
- The pixel clock is limited to less than 173000.

v6:
- Remove spin_lock in mmio reg read and write.
- TO_UNCAC is replac with ioremap.
- Fix error arguments in crtc_atomic_enable/disable/mode_valid.

v5:
- Change the name of the chip to LS7A.
- Change magic value in crtc to macros.
- Correct mistakes words.
- Change the register operation function prefix to ls7a.

v4:
- Move the mode_valid function to the crtc.

v3:
- Move the mode_valid function to the connector and optimize it.
- Fix num_crtc calculation method.

v2:
- Complete the case of 32-bit color in CRTC.

Signed-off-by: Yi Li <[email protected]>
Signed-off-by: Chenyang Li <[email protected]>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/loongson/Kconfig | 13 +
drivers/gpu/drm/loongson/Makefile | 13 +
drivers/gpu/drm/loongson/loongson_connector.c | 25 ++
drivers/gpu/drm/loongson/loongson_crtc.c | 244 ++++++++++++++++
drivers/gpu/drm/loongson/loongson_device.c | 38 +++
drivers/gpu/drm/loongson/loongson_drv.c | 275 ++++++++++++++++++
drivers/gpu/drm/loongson/loongson_drv.h | 132 +++++++++
drivers/gpu/drm/loongson/loongson_encoder.c | 59 ++++
drivers/gpu/drm/loongson/loongson_plane.c | 97 ++++++
11 files changed, 899 insertions(+)
create mode 100644 drivers/gpu/drm/loongson/Kconfig
create mode 100644 drivers/gpu/drm/loongson/Makefile
create mode 100644 drivers/gpu/drm/loongson/loongson_connector.c
create mode 100644 drivers/gpu/drm/loongson/loongson_crtc.c
create mode 100644 drivers/gpu/drm/loongson/loongson_device.c
create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c
create mode 100644 drivers/gpu/drm/loongson/loongson_drv.h
create mode 100644 drivers/gpu/drm/loongson/loongson_encoder.c
create mode 100644 drivers/gpu/drm/loongson/loongson_plane.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f1422bee3dcc..d5547e492a3f 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -392,6 +392,8 @@ source "drivers/gpu/drm/vboxvideo/Kconfig"

source "drivers/gpu/drm/lima/Kconfig"

+source "drivers/gpu/drm/loongson/Kconfig"
+
source "drivers/gpu/drm/panfrost/Kconfig"

source "drivers/gpu/drm/aspeed/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c2ef5f9fce54..173b967e2884 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -125,6 +125,7 @@ obj-$(CONFIG_DRM_TVE200) += tve200/
obj-$(CONFIG_DRM_XEN) += xen/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_DRM_LIMA) += lima/
+obj-$(CONFIG_DRM_LOONGSON) += loongson/
obj-$(CONFIG_DRM_PANFROST) += panfrost/
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
obj-$(CONFIG_DRM_MCDE) += mcde/
diff --git a/drivers/gpu/drm/loongson/Kconfig b/drivers/gpu/drm/loongson/Kconfig
new file mode 100644
index 000000000000..2484824fb50b
--- /dev/null
+++ b/drivers/gpu/drm/loongson/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_LOONGSON
+ tristate "DRM support for LS7A bridge chipset"
+ depends on DRM && PCI
+ depends on CPU_LOONGSON64 || COMPILE_TEST
+ select DRM_KMS_HELPER
+ select DRM_VRAM_HELPER
+ help
+ The loongson 7A bridge chip (LS7A) is a special chipset for
+ loongson CPU. LS7A internal integrated display controllers,
+ support two display channels.
+ If compiled as a module it will be called loongson.
diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
new file mode 100644
index 000000000000..534a64047fb6
--- /dev/null
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for loongson drm drivers.
+# This driver provides support for the
+# Direct Rendering Infrastructure (DRI)
+
+loongson-y := loongson_connector.o \
+ loongson_crtc.o \
+ loongson_device.o \
+ loongson_drv.o \
+ loongson_encoder.o \
+ loongson_plane.o
+obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_connector.c b/drivers/gpu/drm/loongson/loongson_connector.c
new file mode 100644
index 000000000000..22fd2af8b3e6
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_connector.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_bridge_connector.h>
+
+#include "loongson_drv.h"
+
+int loongson_bridge_connector_init(struct loongson_device *ldev, int index)
+{
+ struct drm_device *dev = &ldev->dev;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder = &ldev->mode_info[index].encoder->base;
+
+ connector = drm_bridge_connector_init(dev, encoder);
+ if (IS_ERR(connector)) {
+ dev_err(dev->dev, "Unable to create bridge connector\n");
+ return PTR_ERR(connector);
+ }
+ drm_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/loongson/loongson_crtc.c b/drivers/gpu/drm/loongson/loongson_crtc.c
new file mode 100644
index 000000000000..611378cef8e8
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_crtc.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <drm/drm_fourcc.h>
+
+#include "loongson_drv.h"
+
+static void try_each_loopc(u32 clk, u32 pstdiv, u32 frefc,
+ struct pix_pll *pll_config)
+{
+ u32 loopc;
+ u32 clk_out;
+ u32 precision;
+ u32 min = 1000;
+ u32 base_clk = 100000L;
+
+ for (loopc = LOOPC_MIN; loopc < LOOPC_MAX; loopc++) {
+ if ((loopc < FRE_REF_MIN * frefc) ||
+ (loopc > FRE_REF_MAX * frefc))
+ continue;
+
+ clk_out = base_clk * loopc / frefc;
+ precision = (clk > clk_out) ? (clk - clk_out) : (clk_out - clk);
+ if (precision < min) {
+ pll_config->l2_div = pstdiv;
+ pll_config->l1_loopc = loopc;
+ pll_config->l1_frefc = frefc;
+ }
+ }
+}
+
+static void cal_freq(u32 pixclock, struct pix_pll *pll_config)
+{
+ u32 pstdiv;
+ u32 frefc;
+ u32 clk;
+
+ for (pstdiv = 1; pstdiv < PST_DIV_MAX; pstdiv++) {
+ clk = pixclock * pstdiv;
+ for (frefc = DIV_REF_MIN; frefc <= DIV_REF_MAX; frefc++)
+ try_each_loopc(clk, pstdiv, frefc, pll_config);
+ }
+}
+
+static void config_pll(struct loongson_device *ldev, unsigned long pll_base,
+ struct pix_pll *pll_cfg)
+{
+ u32 val;
+ u32 count = 0;
+
+ /* clear sel_pll_out0 */
+ val = ls7a_io_rreg(ldev, pll_base + 0x4);
+ val &= ~(1UL << 8);
+ ls7a_io_wreg(ldev, pll_base + 0x4, val);
+
+ /* set pll_pd */
+ val = ls7a_io_rreg(ldev, pll_base + 0x4);
+ val |= (1UL << 13);
+ ls7a_io_wreg(ldev, pll_base + 0x4, val);
+
+ /* clear set_pll_param */
+ val = ls7a_io_rreg(ldev, pll_base + 0x4);
+ val &= ~(1UL << 11);
+ ls7a_io_wreg(ldev, pll_base + 0x4, val);
+
+ /* clear old value & config new value */
+ val = ls7a_io_rreg(ldev, pll_base + 0x4);
+ val &= ~(0x7fUL << 0);
+ val |= (pll_cfg->l1_frefc << 0); /* refc */
+ ls7a_io_wreg(ldev, pll_base + 0x4, val);
+ val = ls7a_io_rreg(ldev, pll_base + 0x0);
+ val &= ~(0x7fUL << 0);
+ val |= (pll_cfg->l2_div << 0); /* div */
+ val &= ~(0x1ffUL << 21);
+ val |= (pll_cfg->l1_loopc << 21); /* loopc */
+ ls7a_io_wreg(ldev, pll_base + 0x0, val);
+
+ /* set set_pll_param */
+ val = ls7a_io_rreg(ldev, pll_base + 0x4);
+ val |= (1UL << 11);
+ ls7a_io_wreg(ldev, pll_base + 0x4, val);
+ /* clear pll_pd */
+ val = ls7a_io_rreg(ldev, pll_base + 0x4);
+ val &= ~(1UL << 13);
+ ls7a_io_wreg(ldev, pll_base + 0x4, val);
+
+ while (!(ls7a_io_rreg(ldev, pll_base + 0x4) & 0x80)) {
+ cpu_relax();
+ count++;
+ if (count >= 1000) {
+ drm_err(&ldev->dev, "loongson-7A PLL lock failed\n");
+ break;
+ }
+ }
+
+ val = ls7a_io_rreg(ldev, pll_base + 0x4);
+ val |= (1UL << 8);
+ ls7a_io_wreg(ldev, pll_base + 0x4, val);
+}
+
+static void loongson_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct loongson_device *ldev = to_loongson_device(dev);
+ struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ const struct drm_format_info *format;
+ struct pix_pll pll_cfg;
+ u32 hr, hss, hse, hfl;
+ u32 vr, vss, vse, vfl;
+ u32 pix_freq;
+ u32 reg_offset;
+
+ hr = mode->hdisplay;
+ hss = mode->hsync_start;
+ hse = mode->hsync_end;
+ hfl = mode->htotal;
+
+ vr = mode->vdisplay;
+ vss = mode->vsync_start;
+ vse = mode->vsync_end;
+ vfl = mode->vtotal;
+
+ pix_freq = mode->clock;
+ reg_offset = lcrtc->reg_offset;
+ format = crtc->primary->state->fb->format;
+
+ ls7a_mm_wreg(ldev, FB_DITCFG_REG + reg_offset, 0);
+ ls7a_mm_wreg(ldev, FB_DITTAB_LO_REG + reg_offset, 0);
+ ls7a_mm_wreg(ldev, FB_DITTAB_HI_REG + reg_offset, 0);
+ ls7a_mm_wreg(ldev, FB_PANCFG_REG + reg_offset, FB_PANCFG_DEF);
+ ls7a_mm_wreg(ldev, FB_PANTIM_REG + reg_offset, 0);
+
+ ls7a_mm_wreg(ldev, FB_HDISPLAY_REG + reg_offset, (hfl << 16) | hr);
+ ls7a_mm_wreg(ldev, FB_HSYNC_REG + reg_offset,
+ FB_HSYNC_PULSE | (hse << 16) | hss);
+
+ ls7a_mm_wreg(ldev, FB_VDISPLAY_REG + reg_offset, (vfl << 16) | vr);
+ ls7a_mm_wreg(ldev, FB_VSYNC_REG + reg_offset,
+ FB_VSYNC_PULSE | (vse << 16) | vss);
+
+ switch (format->format) {
+ case DRM_FORMAT_RGB565:
+ lcrtc->cfg_reg |= 0x3;
+ break;
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ default:
+ lcrtc->cfg_reg |= 0x4;
+ break;
+ }
+
+ ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
+
+ cal_freq(pix_freq, &pll_cfg);
+ config_pll(ldev, LS7A_PIX_PLL + reg_offset, &pll_cfg);
+}
+
+static void loongson_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct loongson_device *ldev = to_loongson_device(dev);
+ struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
+ u32 reg_offset = lcrtc->reg_offset;
+
+ lcrtc->cfg_reg |= CFG_ENABLE;
+ ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
+}
+
+static void loongson_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct loongson_device *ldev = to_loongson_device(dev);
+ struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
+ u32 reg_offset = lcrtc->reg_offset;
+
+ lcrtc->cfg_reg &= ~CFG_ENABLE;
+ ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
+}
+
+static enum drm_mode_status loongson_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ if (mode->hdisplay > 1920)
+ return MODE_BAD;
+ if (mode->vdisplay > 1080)
+ return MODE_BAD;
+ if (mode->hdisplay % 64)
+ return MODE_BAD;
+ if (mode->clock >= 173000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static const struct drm_crtc_helper_funcs loongson_crtc_helper_funcs = {
+ .mode_valid = loongson_mode_valid,
+ .atomic_enable = loongson_crtc_atomic_enable,
+ .atomic_disable = loongson_crtc_atomic_disable,
+ .mode_set_nofb = loongson_crtc_mode_set_nofb,
+};
+
+static const struct drm_crtc_funcs loongson_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+int loongson_crtc_init(struct loongson_device *ldev, int index)
+{
+ struct drm_device *dev = &ldev->dev;
+ struct loongson_plane *plane;
+ struct loongson_crtc *lcrtc;
+
+ plane = loongson_plane_init(dev, index);
+ if (IS_ERR(plane))
+ return PTR_ERR(plane);
+
+ lcrtc = drmm_crtc_alloc_with_planes(dev, struct loongson_crtc, base,
+ &plane->base, NULL,
+ &loongson_crtc_funcs, NULL);
+ if (IS_ERR(lcrtc))
+ return PTR_ERR(lcrtc);
+
+ lcrtc->ldev = ldev;
+ lcrtc->reg_offset = index * REG_OFFSET;
+ lcrtc->cfg_reg = CFG_RESET;
+ lcrtc->crtc_id = index;
+ lcrtc->plane = plane;
+
+ drm_crtc_helper_add(&lcrtc->base, &loongson_crtc_helper_funcs);
+
+ ldev->mode_info[index].crtc = lcrtc;
+
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c
new file mode 100644
index 000000000000..74089d64f989
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_device.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include "loongson_drv.h"
+
+u32 loongson_gpu_offset(struct drm_plane_state *state,
+ struct loongson_device *ldev)
+{
+ struct drm_gem_vram_object *gbo;
+ u32 gpu_addr;
+
+ gbo = drm_gem_vram_of_gem(state->fb->obj[0]);
+ gpu_addr = ldev->vram_start + drm_gem_vram_offset(gbo);
+
+ return gpu_addr;
+}
+
+u32 ls7a_io_rreg(struct loongson_device *ldev, u32 offset)
+{
+ return readl(ldev->io + offset);
+}
+
+void ls7a_io_wreg(struct loongson_device *ldev, u32 offset, u32 val)
+{
+ writel(val, ldev->io + offset);
+}
+
+u32 ls7a_mm_rreg(struct loongson_device *ldev, u32 offset)
+{
+ return readl(ldev->mmio + offset);
+}
+
+void ls7a_mm_wreg(struct loongson_device *ldev, u32 offset, u32 val)
+{
+ writel(val, ldev->mmio + offset);
+}
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
new file mode 100644
index 000000000000..4993b8d9e8ca
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+/*
+ * Loongson LS7A1000 bridge chipset drm driver
+ * Author: Yi Li, [email protected]
+ * Chenyang Li, [email protected]
+ */
+
+#include <linux/console.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_probe_helper.h>
+
+#include "loongson_drv.h"
+
+/* Interface history:
+ * 0.1 - original.
+ */
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 1
+
+static const struct drm_mode_config_funcs loongson_mode_funcs = {
+ .fb_create = drm_gem_fb_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+ .output_poll_changed = drm_fb_helper_output_poll_changed,
+ .mode_valid = drm_vram_helper_mode_valid
+};
+
+static int loongson_device_init(struct drm_device *dev)
+{
+ struct loongson_device *ldev = to_loongson_device(dev);
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+ struct pci_dev *gpu_pdev;
+ resource_size_t aper_base;
+ resource_size_t aper_size;
+ resource_size_t mmio_base;
+ resource_size_t mmio_size;
+ int ret;
+
+ /* GPU MEM */
+ /* We need get 7A-gpu pci device information for ldev->gpu_pdev */
+ /* dev->pdev save 7A-dc pci device information */
+ gpu_pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON,
+ PCI_DEVICE_ID_LOONGSON_GPU, NULL);
+ ret = pci_enable_device(gpu_pdev);
+ if (ret)
+ return ret;
+ pci_set_drvdata(gpu_pdev, dev);
+
+ aper_base = pci_resource_start(gpu_pdev, 2);
+ aper_size = pci_resource_len(gpu_pdev, 2);
+ ldev->vram_start = aper_base;
+ ldev->vram_size = aper_size;
+
+ if (!devm_request_mem_region(dev->dev, ldev->vram_start,
+ ldev->vram_size, "loongson_vram")) {
+ drm_err(dev, "Can't reserve VRAM\n");
+ return -ENXIO;
+ }
+
+ /* DC MEM */
+ mmio_base = pci_resource_start(pdev, 0);
+ mmio_size = pci_resource_len(pdev, 0);
+ ldev->mmio = devm_ioremap(dev->dev, mmio_base, mmio_size);
+ if (!ldev->mmio) {
+ drm_err(dev, "Cannot map mmio region\n");
+ return -ENOMEM;
+ }
+
+ if (!devm_request_mem_region(dev->dev, mmio_base,
+ mmio_size, "loongson_mmio")) {
+ drm_err(dev, "Can't reserve mmio registers\n");
+ return -ENOMEM;
+ }
+
+ /* DC IO */
+ ldev->io = devm_ioremap(dev->dev, LS7A_CHIPCFG_REG_BASE, 0xf);
+ if (!ldev->io)
+ return -ENOMEM;
+
+ ldev->num_crtc = 2;
+
+ drm_info(dev, "DC mmio base 0x%llx size 0x%llx io 0x%llx\n",
+ mmio_base, mmio_size, *(u64 *)ldev->io);
+ drm_info(dev, "GPU vram start = 0x%x size = 0x%x\n",
+ ldev->vram_start, ldev->vram_size);
+
+ return 0;
+}
+
+int loongson_modeset_init(struct loongson_device *ldev)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < ldev->num_crtc; i++) {
+ ret = loongson_crtc_init(ldev, i);
+ if (ret) {
+ drm_warn(&ldev->dev, "loongson crtc%d init fail\n", i);
+ continue;
+ }
+
+ ret = loongson_encoder_init(ldev, i);
+ if (ret) {
+ drm_err(&ldev->dev, "loongson_encoder_init failed\n");
+ return ret;
+ }
+
+ ret = loongson_bridge_connector_init(ldev, i);
+ if (ret) {
+ drm_err(&ldev->dev, "loongson_connector_init failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int loongson_driver_init(struct drm_device *dev)
+{
+ struct loongson_device *ldev = to_loongson_device(dev);
+ int ret;
+
+ ret = loongson_device_init(dev);
+ if (ret)
+ goto err;
+
+ ret = drmm_vram_helper_init(dev, ldev->vram_start, ldev->vram_size);
+ if (ret) {
+ drm_err(dev, "Error initializing vram %d\n", ret);
+ goto err;
+ }
+
+ ret = drmm_mode_config_init(dev);
+ if (ret)
+ return ret;
+
+ dev->mode_config.min_width = 1;
+ dev->mode_config.min_height = 1;
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+ dev->mode_config.preferred_depth = 32;
+ dev->mode_config.prefer_shadow = 1;
+ dev->mode_config.fb_base = ldev->vram_start;
+ dev->mode_config.funcs = (void *)&loongson_mode_funcs;
+
+ ret = loongson_modeset_init(ldev);
+ if (ret) {
+ drm_err(dev, "Fatal error during modeset init: %d\n", ret);
+ goto err;
+ }
+
+ drm_kms_helper_poll_init(dev);
+ drm_mode_config_reset(dev);
+
+ return 0;
+
+err:
+ drm_err(dev, "failed to initialize drm driver: %d\n", ret);
+ return ret;
+}
+
+static void loongson_driver_fini(struct drm_device *dev)
+{
+ dev->dev_private = NULL;
+ dev_set_drvdata(dev->dev, NULL);
+}
+
+DEFINE_DRM_GEM_FOPS(fops);
+
+static struct drm_driver loongson_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+ .fops = &fops,
+ DRM_GEM_VRAM_DRIVER,
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+static int loongson_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct loongson_device *ldev;
+ struct drm_device *dev;
+ int ret;
+
+ DRM_INFO("Start loongson drm probe.\n");
+ ldev = devm_drm_dev_alloc(&pdev->dev, &loongson_driver,
+ struct loongson_device, dev);
+ if (IS_ERR(ldev))
+ return PTR_ERR(ldev);
+ dev = &ldev->dev;
+
+ pci_set_drvdata(pdev, dev);
+
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ drm_err(dev, "failed to enable pci device: %d\n", ret);
+ goto err_free;
+ }
+
+ ret = loongson_driver_init(dev);
+ if (ret) {
+ drm_err(dev, "failed to load loongson: %d\n", ret);
+ goto err_pdev;
+ }
+
+ ret = drm_dev_register(dev, 0);
+ if (ret) {
+ drm_err(dev, "failed to register drv for userspace access: %d\n",
+ ret);
+ goto driver_fini;
+ }
+
+ drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth);
+ DRM_INFO("loongson fbdev enabled.\n");
+
+ return 0;
+
+driver_fini:
+ loongson_driver_fini(dev);
+err_pdev:
+ pci_disable_device(pdev);
+err_free:
+ drm_dev_put(dev);
+ return ret;
+}
+
+static void loongson_pci_remove(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+ drm_dev_unregister(dev);
+ loongson_driver_fini(dev);
+}
+
+static struct pci_device_id loongson_pci_devices[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC) },
+ {0,}
+};
+
+static struct pci_driver loongson_drm_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = loongson_pci_devices,
+ .probe = loongson_pci_probe,
+ .remove = loongson_pci_remove,
+};
+
+static int __init loongson_drm_init(void)
+{
+ return pci_register_driver(&loongson_drm_pci_driver);
+}
+
+static void __exit loongson_drm_exit(void)
+{
+ pci_unregister_driver(&loongson_drm_pci_driver);
+}
+
+module_init(loongson_drm_init);
+module_exit(loongson_drm_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
new file mode 100644
index 000000000000..e9e97db00110
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef __LOONGSON_DRV_H__
+#define __LOONGSON_DRV_H__
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_gem_vram_helper.h>
+#include <drm/drm_bridge.h>
+
+/* General customization:
+ */
+#define DRIVER_AUTHOR "Loongson graphics driver team"
+#define DRIVER_NAME "loongson-drm"
+#define DRIVER_DESC "Loongson LS7A DRM driver"
+#define DRIVER_DATE "20200915"
+
+#define to_loongson_crtc(x) container_of(x, struct loongson_crtc, base)
+#define to_loongson_encoder(x) container_of(x, struct loongson_encoder, base)
+
+#define LS7A_CHIPCFG_REG_BASE (0x10010000)
+#define PCI_DEVICE_ID_LOONGSON_DC 0x7a06
+#define PCI_DEVICE_ID_LOONGSON_GPU 0x7a15
+#define LS7A_PIX_PLL (0x04b0)
+#define REG_OFFSET (0x10)
+#define FB_CFG_REG (0x1240)
+#define FB_ADDR0_REG (0x1260)
+#define FB_ADDR1_REG (0x1580)
+#define FB_STRI_REG (0x1280)
+#define FB_DITCFG_REG (0x1360)
+#define FB_DITTAB_LO_REG (0x1380)
+#define FB_DITTAB_HI_REG (0x13a0)
+#define FB_PANCFG_REG (0x13c0)
+#define FB_PANTIM_REG (0x13e0)
+#define FB_HDISPLAY_REG (0x1400)
+#define FB_HSYNC_REG (0x1420)
+#define FB_VDISPLAY_REG (0x1480)
+#define FB_VSYNC_REG (0x14a0)
+
+#define CFG_FMT GENMASK(2, 0)
+#define CFG_FBSWITCH BIT(7)
+#define CFG_ENABLE BIT(8)
+#define CFG_FBNUM BIT(11)
+#define CFG_GAMMAR BIT(12)
+#define CFG_RESET BIT(20)
+
+#define FB_PANCFG_DEF 0x80001311
+#define FB_HSYNC_PULSE (1 << 30)
+#define FB_VSYNC_PULSE (1 << 30)
+
+/* PIX PLL */
+#define LOOPC_MIN 24
+#define LOOPC_MAX 161
+#define FRE_REF_MIN 12
+#define FRE_REF_MAX 32
+#define DIV_REF_MIN 3
+#define DIV_REF_MAX 5
+#define PST_DIV_MAX 64
+
+struct pix_pll {
+ u32 l2_div;
+ u32 l1_loopc;
+ u32 l1_frefc;
+};
+
+struct loongson_crtc {
+ struct drm_crtc base;
+ struct loongson_device *ldev;
+ u32 crtc_id;
+ u32 reg_offset;
+ u32 cfg_reg;
+ struct loongson_plane *plane;
+};
+
+struct loongson_plane {
+ struct drm_plane base;
+};
+
+struct loongson_encoder {
+ struct drm_encoder base;
+ struct drm_bridge bridge;
+ struct loongson_device *ldev;
+ struct loongson_crtc *lcrtc;
+};
+
+struct loongson_mode_info {
+ struct loongson_crtc *crtc;
+ struct loongson_encoder *encoder;
+};
+
+struct loongson_device {
+ struct drm_device dev;
+ struct drm_atomic_state *state;
+
+ void __iomem *mmio;
+ void __iomem *io;
+ u32 vram_start;
+ u32 vram_size;
+
+ u32 num_crtc;
+ struct loongson_mode_info mode_info[2];
+ struct pci_dev *gpu_pdev; /* LS7A gpu device info */
+};
+
+static inline struct loongson_device *to_loongson_device(struct drm_device *dev)
+{
+ return container_of(dev, struct loongson_device, dev);
+}
+
+/* crtc */
+int loongson_crtc_init(struct loongson_device *ldev, int index);
+
+/* connector */
+int loongson_bridge_connector_init(struct loongson_device *ldev, int index);
+
+/* encoder */
+int loongson_encoder_init(struct loongson_device *ldev, int index);
+
+/* plane */
+struct loongson_plane *loongson_plane_init(struct drm_device *dev, int index);
+
+/* device */
+u32 loongson_gpu_offset(struct drm_plane_state *state,
+ struct loongson_device *dev);
+u32 ls7a_mm_rreg(struct loongson_device *ldev, u32 offset);
+void ls7a_mm_wreg(struct loongson_device *ldev, u32 offset, u32 val);
+u32 ls7a_io_rreg(struct loongson_device *ldev, u32 offset);
+void ls7a_io_wreg(struct loongson_device *ldev, u32 offset, u32 val);
+
+#endif /* __LOONGSON_DRV_H__ */
diff --git a/drivers/gpu/drm/loongson/loongson_encoder.c b/drivers/gpu/drm/loongson/loongson_encoder.c
new file mode 100644
index 000000000000..5b94f707f1a0
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_encoder.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <drm/drm_simple_kms_helper.h>
+
+#include "loongson_drv.h"
+
+static int loongson_bridge_get_modes(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ int count;
+
+ count = drm_add_modes_noedid(connector, 1920, 1080);
+ drm_set_preferred_mode(connector, 1024, 768);
+
+ return count;
+}
+
+static const struct drm_bridge_funcs loongson_encoder_bridge_funcs = {
+ .get_modes = loongson_bridge_get_modes,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+};
+
+int loongson_encoder_init(struct loongson_device *ldev, int index)
+{
+ struct drm_device *dev = &ldev->dev;
+ struct loongson_encoder *lencoder;
+ int ret;
+
+ lencoder = drmm_simple_encoder_alloc(dev, struct loongson_encoder,
+ base, DRM_MODE_ENCODER_DAC);
+ if (IS_ERR(lencoder))
+ return PTR_ERR(lencoder);
+
+ lencoder->base.possible_crtcs = 1 << index;
+ ldev->mode_info[index].encoder = lencoder;
+
+ lencoder->bridge.funcs = &loongson_encoder_bridge_funcs;
+ lencoder->bridge.ops = DRM_BRIDGE_OP_MODES;
+ if (index == 0)
+ lencoder->bridge.type = DRM_MODE_CONNECTOR_VGA;
+ else if (index == 1)
+ lencoder->bridge.type = DRM_MODE_CONNECTOR_DVIA;
+
+ drm_bridge_add(&lencoder->bridge);
+
+ ret = drm_bridge_attach(&lencoder->base, &lencoder->bridge, NULL,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret) {
+ dev_err(dev->dev, "Failed to attach bridge: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/loongson/loongson_plane.c b/drivers/gpu/drm/loongson/loongson_plane.c
new file mode 100644
index 000000000000..04d50bf722f1
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_plane.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <drm/drm_fourcc.h>
+
+#include "loongson_drv.h"
+
+static void loongson_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct loongson_crtc *lcrtc;
+ struct loongson_device *ldev;
+ struct drm_plane_state *lstate = plane->state;
+ u32 gpu_addr = 0;
+ u32 fb_addr = 0;
+ u32 reg_val = 0;
+ u32 reg_offset;
+ u32 pitch;
+ u8 depth;
+ u32 x, y;
+
+ if (!lstate->crtc || !lstate->fb)
+ return;
+
+ pitch = lstate->fb->pitches[0];
+ lcrtc = to_loongson_crtc(lstate->crtc);
+ ldev = lcrtc->ldev;
+ reg_offset = lcrtc->reg_offset;
+ x = lstate->crtc->x;
+ y = lstate->crtc->y;
+ depth = lstate->fb->format->cpp[0] << 3;
+
+ gpu_addr = loongson_gpu_offset(lstate, ldev);
+ reg_val = (pitch + 255) & ~255;
+ ls7a_mm_wreg(ldev, FB_STRI_REG + reg_offset, reg_val);
+
+ switch (depth) {
+ case 12 ... 16:
+ fb_addr = gpu_addr + y * pitch + ALIGN(x, 64) * 2;
+ break;
+ case 24 ... 32:
+ default:
+ fb_addr = gpu_addr + y * pitch + ALIGN(x, 64) * 4;
+ break;
+ }
+
+ ls7a_mm_wreg(ldev, FB_ADDR0_REG + reg_offset, fb_addr);
+ ls7a_mm_wreg(ldev, FB_ADDR1_REG + reg_offset, fb_addr);
+ reg_val = lcrtc->cfg_reg | CFG_ENABLE;
+ ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, reg_val);
+}
+
+static const uint32_t loongson_formats[] = {
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+};
+
+static const uint64_t loongson_format_modifiers[] = { DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID };
+
+static const struct drm_plane_funcs loongson_plane_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .update_plane = drm_atomic_helper_update_plane,
+};
+
+static const struct drm_plane_helper_funcs loongson_plane_helper_funcs = {
+ .prepare_fb = drm_gem_vram_plane_helper_prepare_fb,
+ .cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb,
+ .atomic_update = loongson_plane_atomic_update,
+};
+
+struct loongson_plane *loongson_plane_init(struct drm_device *dev, int index)
+{
+ struct loongson_plane *plane;
+
+ plane = drmm_universal_plane_alloc(dev, struct loongson_plane, base,
+ BIT(index), &loongson_plane_funcs,
+ loongson_formats,
+ ARRAY_SIZE(loongson_formats),
+ loongson_format_modifiers,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (IS_ERR(plane)) {
+ drm_err(dev, "failed to allocate and initialize plane\n");
+ return plane;
+ }
+
+ drm_plane_helper_add(&plane->base, &loongson_plane_helper_funcs);
+
+ return plane;
+}
--
2.25.1


2022-06-25 09:32:29

by 李晨阳

[permalink] [raw]
Subject: [PATCH v7 4/4] drm/loongson: Use acpi to get video bios.

Add get video bios from the ACPI table.
Define new acpi table "VIAT" for loongson video bios.

Signed-off-by: Chenyang Li <[email protected]>
---
drivers/gpu/drm/loongson/Makefile | 3 +-
drivers/gpu/drm/loongson/loongson_drv.c | 21 +++++++-
drivers/gpu/drm/loongson/loongson_drv.h | 1 +
drivers/gpu/drm/loongson/loongson_vbios.c | 62 +++++++++++++++++++++++
drivers/gpu/drm/loongson/loongson_vbios.h | 29 +++++++++++
5 files changed, 113 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/loongson/loongson_vbios.c
create mode 100644 drivers/gpu/drm/loongson/loongson_vbios.h

diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
index b083854d789b..b774a91003d9 100644
--- a/drivers/gpu/drm/loongson/Makefile
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -11,5 +11,6 @@ loongson-y := loongson_connector.o \
loongson_encoder.o \
loongson_i2c.o \
loongson_irq.o \
- loongson_plane.o
+ loongson_plane.o \
+ loongson_vbios.o
obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
index 1d6f35e78813..1c212200f088 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.c
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -19,14 +19,28 @@
#include <drm/drm_probe_helper.h>

#include "loongson_drv.h"
+#include "loongson_vbios.h"
+
+/*
+ * Completed
+ * 1.Displays controller device initialization and display funcitons
+ * 2.I2c bus driver and DDC functions
+ * 3.Vblank and vsync interrupt support
+ * 4.Use acpi to get video bios
+ * Todo
+ * 1.Video bios parse functions
+ * 2.Hardware cursor driver
+ * 3.New device support as well as Loongson GPU
+ */

/* Interface history:
* 0.1 - original.
* 0.2 - add i2c and connector detect.
* 0.3 - Vblank and vsync interrupt support.
+ * 0.4 - Use acpi to get vbios.
*/
#define DRIVER_MAJOR 0
-#define DRIVER_MINOR 3
+#define DRIVER_MINOR 4

static const struct drm_mode_config_funcs loongson_mode_funcs = {
.fb_create = drm_gem_fb_create,
@@ -88,7 +102,10 @@ static int loongson_device_init(struct drm_device *dev)
if (!ldev->io)
return -ENOMEM;

- ldev->num_crtc = 2;
+ if (!loongson_vbios_init(ldev)) {
+ DRM_WARN("Get vbios failed, enable two crtc\n");
+ ldev->num_crtc = 2;
+ }

ret = loongson_dc_gpio_init(ldev);
if (ret)
diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
index af47e68487fd..4e5cb5977c9a 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.h
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -109,6 +109,7 @@ struct loongson_device {
u32 num_crtc;
struct loongson_mode_info mode_info[2];
struct pci_dev *gpu_pdev; /* LS7A gpu device info */
+ void *vbios;

struct loongson_i2c i2c_bus[DC_MAX_I2C_BUS];
};
diff --git a/drivers/gpu/drm/loongson/loongson_vbios.c b/drivers/gpu/drm/loongson/loongson_vbios.c
new file mode 100644
index 000000000000..2b3a2757102a
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_vbios.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include "loongson_drv.h"
+#include "loongson_vbios.h"
+
+#ifdef CONFIG_ACPI
+static bool read_bios_from_acpi(struct loongson_device *ldev)
+{
+ void *vaddr;
+ struct acpi_table_header *hdr;
+ struct acpi_viat_table *viat;
+ acpi_size tbl_size;
+
+ if (!ACPI_SUCCESS(acpi_get_table("VIAT", 1, &hdr)))
+ return false;
+
+ tbl_size = hdr->length;
+ if (tbl_size != sizeof(struct acpi_viat_table)) {
+ DRM_WARN("ACPI viat table present but broken(too short #1)\n");
+ return false;
+ }
+
+ viat = (struct acpi_viat_table *)hdr;
+ ldev->vbios = kmalloc(VBIOS_SIZE, GFP_KERNEL);
+ if (!ldev->vbios) {
+ kfree(ldev->vbios);
+ return false;
+ }
+
+ vaddr = phys_to_virt(viat->vbios_addr);
+ memcpy(ldev->vbios, vaddr, VBIOS_SIZE);
+ DRM_INFO("Get vbios from ACPI success!\n");
+
+ return true;
+}
+#else
+static bool read_bios_from_acpi(struct loongson_device *ldev)
+{
+ return false;
+}
+#endif
+
+bool loongson_vbios_init(struct loongson_device *ldev)
+{
+ int ret;
+ struct loongson_vbios *header;
+
+ ret = read_bios_from_acpi(ldev);
+ if (!ret)
+ return ret;
+
+ header = ldev->vbios;
+ ldev->num_crtc = header->crtc_num;
+
+ DRM_INFO("Loongson vbios version %d.%d crtc num %d.\n",
+ header->version_major, header->version_minor, ldev->num_crtc);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/loongson/loongson_vbios.h b/drivers/gpu/drm/loongson/loongson_vbios.h
new file mode 100644
index 000000000000..b7d8ce15c6c5
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_vbios.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef __LOONGSON_VBIOS_H__
+#define __LOONGSON_VBIOS_H__
+
+#include <linux/acpi.h>
+
+#define VBIOS_SIZE 0x40000
+
+struct loongson_vbios {
+ uint32_t version_major;
+ uint32_t version_minor;
+ uint32_t crtc_num;
+} __packed;
+
+#ifdef CONFIG_ACPI
+/* VBOIS INFO ADDRESS TABLE */
+struct acpi_viat_table {
+ struct acpi_table_header header;
+ u64 vbios_addr;
+} __packed;
+#endif
+
+bool loongson_vbios_init(struct loongson_device *ldev);
+
+#endif /* __LOONGSON_VBIOS_H__ */
--
2.25.1

2022-06-25 09:37:52

by 李晨阳

[permalink] [raw]
Subject: [PATCH v7 3/4] drm/loongson: Add interrupt driver for LS7A.

Add LS7A DC vsync interrupt enable and close function, and
register irq_handler function interface.
Add vbrank event processing flow.

v4:
- Replace drm_irq_install with devm_request_irq.
- Delete the irq_ hooks in drm_driver.

v3:
- Improve code readability.
- Use the to_pci_dev function to get pci_dev.

v2:
- Added error handling in the loongson_drm_load function.

Signed-off-by: Chenyang Li <[email protected]>
---
drivers/gpu/drm/loongson/Makefile | 1 +
drivers/gpu/drm/loongson/loongson_crtc.c | 37 +++++++++++
drivers/gpu/drm/loongson/loongson_drv.c | 9 ++-
drivers/gpu/drm/loongson/loongson_drv.h | 10 +++
drivers/gpu/drm/loongson/loongson_irq.c | 84 ++++++++++++++++++++++++
5 files changed, 140 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/loongson/loongson_irq.c

diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
index 4a5fab7d000b..b083854d789b 100644
--- a/drivers/gpu/drm/loongson/Makefile
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -10,5 +10,6 @@ loongson-y := loongson_connector.o \
loongson_drv.o \
loongson_encoder.o \
loongson_i2c.o \
+ loongson_irq.o \
loongson_plane.o
obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_crtc.c b/drivers/gpu/drm/loongson/loongson_crtc.c
index 611378cef8e8..6a31ff911ce1 100644
--- a/drivers/gpu/drm/loongson/loongson_crtc.c
+++ b/drivers/gpu/drm/loongson/loongson_crtc.c
@@ -4,6 +4,7 @@
*/

#include <drm/drm_fourcc.h>
+#include <drm/drm_vblank.h>

#include "loongson_drv.h"

@@ -167,8 +168,14 @@ static void loongson_crtc_atomic_enable(struct drm_crtc *crtc,
struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
u32 reg_offset = lcrtc->reg_offset;

+ if (lcrtc->cfg_reg & CFG_ENABLE)
+ goto vblank_on;
+
lcrtc->cfg_reg |= CFG_ENABLE;
ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
+
+vblank_on:
+ drm_crtc_vblank_on(crtc);
}

static void loongson_crtc_atomic_disable(struct drm_crtc *crtc,
@@ -181,6 +188,33 @@ static void loongson_crtc_atomic_disable(struct drm_crtc *crtc,

lcrtc->cfg_reg &= ~CFG_ENABLE;
ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+
+ drm_crtc_vblank_off(crtc);
+}
+
+static void loongson_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ if (!event)
+ return;
+
+ 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 enum drm_mode_status loongson_mode_valid(struct drm_crtc *crtc,
@@ -200,6 +234,7 @@ static enum drm_mode_status loongson_mode_valid(struct drm_crtc *crtc,

static const struct drm_crtc_helper_funcs loongson_crtc_helper_funcs = {
.mode_valid = loongson_mode_valid,
+ .atomic_flush = loongson_crtc_atomic_flush,
.atomic_enable = loongson_crtc_atomic_enable,
.atomic_disable = loongson_crtc_atomic_disable,
.mode_set_nofb = loongson_crtc_mode_set_nofb,
@@ -211,6 +246,8 @@ static const struct drm_crtc_funcs loongson_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = loongson_crtc_enable_vblank,
+ .disable_vblank = loongson_crtc_disable_vblank,
};

int loongson_crtc_init(struct loongson_device *ldev, int index)
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
index 2e3ef6193767..1d6f35e78813 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.c
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -23,9 +23,10 @@
/* Interface history:
* 0.1 - original.
* 0.2 - add i2c and connector detect.
+ * 0.3 - Vblank and vsync interrupt support.
*/
#define DRIVER_MAJOR 0
-#define DRIVER_MINOR 2
+#define DRIVER_MINOR 3

static const struct drm_mode_config_funcs loongson_mode_funcs = {
.fb_create = drm_gem_fb_create,
@@ -167,6 +168,12 @@ static int loongson_driver_init(struct drm_device *dev)
goto err;
}

+ ret = loongson_irq_init(ldev);
+ if (ret) {
+ dev_err(dev->dev, "Fatal error during irq init: %d\n", ret);
+ goto err;
+ }
+
drm_kms_helper_poll_init(dev);
drm_mode_config_reset(dev);

diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
index 5be29d2d1a49..af47e68487fd 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.h
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -40,6 +40,7 @@
#define FB_HSYNC_REG (0x1420)
#define FB_VDISPLAY_REG (0x1480)
#define FB_VSYNC_REG (0x14a0)
+#define FB_INT_REG (0x1570)

#define CFG_FMT GENMASK(2, 0)
#define CFG_FBSWITCH BIT(7)
@@ -51,6 +52,10 @@
#define FB_PANCFG_DEF 0x80001311
#define FB_HSYNC_PULSE (1 << 30)
#define FB_VSYNC_PULSE (1 << 30)
+#define FB_VSYNC1_ENABLE (1 << 16)
+#define FB_VSYNC0_ENABLE (1 << 18)
+#define FB_VSYNC1_INT (1 << 0)
+#define FB_VSYNC0_INT (1 << 2)

/* PIX PLL */
#define LOOPC_MIN 24
@@ -128,6 +133,11 @@ struct loongson_plane *loongson_plane_init(struct drm_device *dev, int index);
/* i2c */
int loongson_dc_gpio_init(struct loongson_device *ldev);

+/* irq */
+int loongson_irq_init(struct loongson_device *ldev);
+int loongson_crtc_enable_vblank(struct drm_crtc *crtc);
+void loongson_crtc_disable_vblank(struct drm_crtc *crtc);
+
/* device */
u32 loongson_gpu_offset(struct drm_plane_state *state,
struct loongson_device *dev);
diff --git a/drivers/gpu/drm/loongson/loongson_irq.c b/drivers/gpu/drm/loongson/loongson_irq.c
new file mode 100644
index 000000000000..ba215d585dc7
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_irq.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_vblank.h>
+
+#include "loongson_drv.h"
+
+static irqreturn_t loongson_irq_handler(int irq, void *arg)
+{
+ struct drm_device *dev = (struct drm_device *) arg;
+ struct loongson_device *ldev = to_loongson_device(dev);
+ struct loongson_crtc *lcrtc;
+ u32 val;
+
+ val = ls7a_mm_rreg(ldev, FB_INT_REG);
+ ls7a_mm_wreg(ldev, FB_INT_REG, val & (0xffff << 16));
+
+ if (val & FB_VSYNC0_INT)
+ lcrtc = ldev->mode_info[0].crtc;
+ else if (val & FB_VSYNC1_INT)
+ lcrtc = ldev->mode_info[1].crtc;
+
+ drm_crtc_handle_vblank(&lcrtc->base);
+
+ return IRQ_HANDLED;
+}
+
+int loongson_irq_init(struct loongson_device *ldev)
+{
+ int ret;
+ struct drm_device *dev = &ldev->dev;
+ int irq = to_pci_dev(dev->dev)->irq;
+
+ ret = drm_vblank_init(dev, ldev->num_crtc);
+ if (ret) {
+ dev_err(dev->dev, "Fatal error during vblank init: %d\n", ret);
+ return ret;
+ }
+ DRM_INFO("drm vblank init finished\n");
+
+ ret = devm_request_irq(dev->dev, irq, loongson_irq_handler, 0,
+ "loongson-drm", dev);
+ if (ret) {
+ dev_err(dev->dev, "Fatal error during irq install: %d\n", ret);
+ return ret;
+ }
+ DRM_INFO("loongson irq initialized\n");
+
+ return 0;
+}
+
+int loongson_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+ struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
+ struct loongson_device *ldev = lcrtc->ldev;
+ u32 reg_val = ls7a_mm_rreg(ldev, FB_INT_REG);
+
+ if (lcrtc->crtc_id)
+ reg_val |= FB_VSYNC1_ENABLE;
+ else
+ reg_val |= FB_VSYNC0_ENABLE;
+
+ ls7a_mm_wreg(ldev, FB_INT_REG, reg_val);
+
+ return 0;
+}
+
+void loongson_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+ struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
+ struct loongson_device *ldev = lcrtc->ldev;
+ u32 reg_val = ls7a_mm_rreg(ldev, FB_INT_REG);
+
+ if (lcrtc->crtc_id)
+ reg_val &= ~FB_VSYNC1_ENABLE;
+ else
+ reg_val &= ~FB_VSYNC0_ENABLE;
+
+ ls7a_mm_wreg(ldev, FB_INT_REG, reg_val);
+}
--
2.25.1

2022-06-25 09:40:10

by 李晨阳

[permalink] [raw]
Subject: [PATCH v7 2/4] drm/loongson: Add GPIO and I2C driver for loongson drm.

Implement use GPIO and I2C driver to detect connector
and fetch EDID via DDC.

v5:
- Use braidge->ddc to get EDID and detect connector.

v4:
- Delete the gpio_chip subsystem call.
- Delete some redundant prints.

v3:
- Change some driver log to the drm_ version.

v2:
- Optimize the error handling process.
- Delete loongson_i2c_bus_match and loongson_i2c_add function.
- Optimize part of the code flow.

Signed-off-by: Yi Li <[email protected]>
Signed-off-by: Chenyang Li <[email protected]>
---
drivers/gpu/drm/loongson/Makefile | 1 +
drivers/gpu/drm/loongson/loongson_drv.c | 13 +-
drivers/gpu/drm/loongson/loongson_drv.h | 7 +
drivers/gpu/drm/loongson/loongson_encoder.c | 31 +++-
drivers/gpu/drm/loongson/loongson_i2c.c | 191 ++++++++++++++++++++
drivers/gpu/drm/loongson/loongson_i2c.h | 33 ++++
6 files changed, 266 insertions(+), 10 deletions(-)
create mode 100644 drivers/gpu/drm/loongson/loongson_i2c.c
create mode 100644 drivers/gpu/drm/loongson/loongson_i2c.h

diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
index 534a64047fb6..4a5fab7d000b 100644
--- a/drivers/gpu/drm/loongson/Makefile
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -9,5 +9,6 @@ loongson-y := loongson_connector.o \
loongson_device.o \
loongson_drv.o \
loongson_encoder.o \
+ loongson_i2c.o \
loongson_plane.o
obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
index 4993b8d9e8ca..2e3ef6193767 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.c
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -22,9 +22,10 @@

/* Interface history:
* 0.1 - original.
+ * 0.2 - add i2c and connector detect.
*/
#define DRIVER_MAJOR 0
-#define DRIVER_MINOR 1
+#define DRIVER_MINOR 2

static const struct drm_mode_config_funcs loongson_mode_funcs = {
.fb_create = drm_gem_fb_create,
@@ -88,6 +89,14 @@ static int loongson_device_init(struct drm_device *dev)

ldev->num_crtc = 2;

+ ret = loongson_dc_gpio_init(ldev);
+ if (ret)
+ return ret;
+
+ ret = loongson_i2c_init(ldev);
+ if (ret)
+ return ret;
+
drm_info(dev, "DC mmio base 0x%llx size 0x%llx io 0x%llx\n",
mmio_base, mmio_size, *(u64 *)ldev->io);
drm_info(dev, "GPU vram start = 0x%x size = 0x%x\n",
@@ -96,7 +105,7 @@ static int loongson_device_init(struct drm_device *dev)
return 0;
}

-int loongson_modeset_init(struct loongson_device *ldev)
+static int loongson_modeset_init(struct loongson_device *ldev)
{
int i;
int ret;
diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
index e9e97db00110..5be29d2d1a49 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.h
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -10,6 +10,8 @@
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_bridge.h>

+#include "loongson_i2c.h"
+
/* General customization:
*/
#define DRIVER_AUTHOR "Loongson graphics driver team"
@@ -102,6 +104,8 @@ struct loongson_device {
u32 num_crtc;
struct loongson_mode_info mode_info[2];
struct pci_dev *gpu_pdev; /* LS7A gpu device info */
+
+ struct loongson_i2c i2c_bus[DC_MAX_I2C_BUS];
};

static inline struct loongson_device *to_loongson_device(struct drm_device *dev)
@@ -121,6 +125,9 @@ int loongson_encoder_init(struct loongson_device *ldev, int index);
/* plane */
struct loongson_plane *loongson_plane_init(struct drm_device *dev, int index);

+/* i2c */
+int loongson_dc_gpio_init(struct loongson_device *ldev);
+
/* device */
u32 loongson_gpu_offset(struct drm_plane_state *state,
struct loongson_device *dev);
diff --git a/drivers/gpu/drm/loongson/loongson_encoder.c b/drivers/gpu/drm/loongson/loongson_encoder.c
index 5b94f707f1a0..accf144479f8 100644
--- a/drivers/gpu/drm/loongson/loongson_encoder.c
+++ b/drivers/gpu/drm/loongson/loongson_encoder.c
@@ -7,19 +7,31 @@

#include "loongson_drv.h"

-static int loongson_bridge_get_modes(struct drm_bridge *bridge,
- struct drm_connector *connector)
+enum drm_connector_status loongson_bridge_detect(struct drm_bridge *bridge)
{
- int count;
+ unsigned char start = 0x0;
+ struct i2c_msg msgs = {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ };

- count = drm_add_modes_noedid(connector, 1920, 1080);
- drm_set_preferred_mode(connector, 1024, 768);
+ if (i2c_transfer(bridge->ddc, &msgs, 1) != 1)
+ return connector_status_disconnected;
+ else
+ return connector_status_connected;
+}

- return count;
+static struct edid *loongson_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ return drm_get_edid(connector, bridge->ddc);
}

static const struct drm_bridge_funcs loongson_encoder_bridge_funcs = {
- .get_modes = loongson_bridge_get_modes,
+ .detect = loongson_bridge_detect,
+ .get_edid = loongson_bridge_get_edid,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
@@ -40,7 +52,10 @@ int loongson_encoder_init(struct loongson_device *ldev, int index)
ldev->mode_info[index].encoder = lencoder;

lencoder->bridge.funcs = &loongson_encoder_bridge_funcs;
- lencoder->bridge.ops = DRM_BRIDGE_OP_MODES;
+ lencoder->bridge.ddc = ldev->i2c_bus[index].adapter;
+ lencoder->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+ | DRM_BRIDGE_OP_MODES;
+
if (index == 0)
lencoder->bridge.type = DRM_MODE_CONNECTOR_VGA;
else if (index == 1)
diff --git a/drivers/gpu/drm/loongson/loongson_i2c.c b/drivers/gpu/drm/loongson/loongson_i2c.c
new file mode 100644
index 000000000000..acc50ca3df22
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_i2c.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include "loongson_drv.h"
+#include "loongson_i2c.h"
+
+static inline void dc_gpio_set_dir(struct loongson_device *ldev,
+ unsigned int pin, int input)
+{
+ u32 temp;
+
+ temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_CFG_OFFSET);
+ if (input)
+ temp |= 1UL << pin;
+ else
+ temp &= ~(1UL << pin);
+
+ ls7a_mm_wreg(ldev, LS7A_DC_GPIO_CFG_OFFSET, temp);
+}
+
+static void dc_gpio_set_val(struct loongson_device *ldev, unsigned int pin,
+ int high)
+{
+ u32 temp;
+
+ temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_OUT_OFFSET);
+ if (high)
+ temp |= 1UL << pin;
+ else
+ temp &= ~(1UL << pin);
+
+ ls7a_mm_wreg(ldev, LS7A_DC_GPIO_OUT_OFFSET, temp);
+}
+
+static void loongson_i2c_set_data(void *i2c, int value)
+{
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->data;
+
+ if (value)
+ dc_gpio_set_dir(ldev, pin, 1);
+ else {
+ dc_gpio_set_val(ldev, pin, 0);
+ dc_gpio_set_dir(ldev, pin, 0);
+ }
+}
+
+static void loongson_i2c_set_clock(void *i2c, int value)
+{
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->clock;
+
+ if (value)
+ dc_gpio_set_dir(ldev, pin, 1);
+ else {
+ dc_gpio_set_val(ldev, pin, 0);
+ dc_gpio_set_dir(ldev, pin, 0);
+ }
+}
+
+static int loongson_i2c_get_data(void *i2c)
+{
+ int val;
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->data;
+
+ val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
+
+ return (val >> pin) & 1;
+}
+
+static int loongson_i2c_get_clock(void *i2c)
+{
+ int val;
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->clock;
+
+ val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
+
+ return (val >> pin) & 1;
+}
+
+static int loongson_i2c_create(struct loongson_device *ldev,
+ struct loongson_i2c *li2c, const char *name)
+{
+ int ret;
+ unsigned int i2c_num;
+ struct drm_device *dev = &ldev->dev;
+ struct i2c_client *i2c_cli;
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_algo_bit_data *i2c_algo_data;
+ const struct i2c_board_info i2c_info = {
+ .type = "ddc-dev",
+ .addr = DDC_ADDR,
+ .flags = I2C_CLASS_DDC,
+ };
+
+ i2c_num = li2c->i2c_id;
+ i2c_adapter = devm_kzalloc(dev->dev, sizeof(*i2c_adapter), GFP_KERNEL);
+ if (!i2c_adapter)
+ return -ENOMEM;
+
+ i2c_algo_data = devm_kzalloc(dev->dev, sizeof(*i2c_algo_data), GFP_KERNEL);
+ if (!i2c_algo_data) {
+ ret = -ENOMEM;
+ goto free_adapter;
+ }
+
+ i2c_adapter->owner = THIS_MODULE;
+ i2c_adapter->class = I2C_CLASS_DDC;
+ i2c_adapter->algo_data = i2c_algo_data;
+ i2c_adapter->dev.parent = dev->dev;
+ i2c_adapter->nr = -1;
+ snprintf(i2c_adapter->name, sizeof(i2c_adapter->name), "%s%d",
+ name, i2c_num);
+
+ li2c->data = i2c_num * 2;
+ li2c->clock = i2c_num * 2 + 1;
+ DRM_INFO("Created i2c-%d, sda=%d, scl=%d\n",
+ i2c_num, li2c->data, li2c->clock);
+
+ i2c_algo_data->setsda = loongson_i2c_set_data;
+ i2c_algo_data->setscl = loongson_i2c_set_clock;
+ i2c_algo_data->getsda = loongson_i2c_get_data;
+ i2c_algo_data->getscl = loongson_i2c_get_clock;
+ i2c_algo_data->udelay = DC_I2C_TON;
+ i2c_algo_data->timeout = usecs_to_jiffies(2200);
+
+ ret = i2c_bit_add_numbered_bus(i2c_adapter);
+ if (ret)
+ goto free_algo_data;
+
+ li2c->adapter = i2c_adapter;
+ i2c_algo_data->data = li2c;
+ i2c_set_adapdata(li2c->adapter, li2c);
+ li2c->ldev = ldev;
+ DRM_INFO("Register i2c algo-bit adapter [%s]\n", i2c_adapter->name);
+
+ i2c_cli = i2c_new_client_device(i2c_adapter, &i2c_info);
+ if (IS_ERR(i2c_cli)) {
+ ret = PTR_ERR(i2c_cli);
+ goto remove_i2c_adapter;
+ }
+
+ return 0;
+
+remove_i2c_adapter:
+ drm_err(dev, "Failed to create i2c client\n");
+ i2c_del_adapter(i2c_adapter);
+free_algo_data:
+ drm_err(dev, "Failed to register i2c adapter %s\n", i2c_adapter->name);
+ kfree(i2c_algo_data);
+free_adapter:
+ kfree(i2c_adapter);
+
+ return ret;
+}
+
+int loongson_dc_gpio_init(struct loongson_device *ldev)
+{
+ int pin;
+
+ /* set gpio dir output 0-3 */
+ for (pin = 0; pin < 4; pin++) {
+ dc_gpio_set_val(ldev, pin, 0);
+ dc_gpio_set_dir(ldev, pin, 0);
+ }
+
+ return 0;
+}
+
+int loongson_i2c_init(struct loongson_device *ldev)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ ldev->i2c_bus[1].i2c_id = i;
+ ret = loongson_i2c_create(ldev, &ldev->i2c_bus[i], DC_I2C_NAME);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/loongson/loongson_i2c.h b/drivers/gpu/drm/loongson/loongson_i2c.h
new file mode 100644
index 000000000000..9944c838d3e4
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_i2c.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef __LOONGSON_I2C_H__
+#define __LOONGSON_I2C_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <drm/drm_edid.h>
+
+#define DC_I2C_TON 5
+#define DC_I2C_NAME "ls_dc_i2c"
+#define DC_MAX_I2C_BUS 2
+
+#define LS7A_DC_GPIO_CFG_OFFSET (0x1660)
+#define LS7A_DC_GPIO_IN_OFFSET (0x1650)
+#define LS7A_DC_GPIO_OUT_OFFSET (0x1650)
+
+struct loongson_device;
+struct loongson_i2c {
+ struct loongson_device *ldev;
+ struct i2c_adapter *adapter;
+ u32 data;
+ u32 clock;
+ u32 i2c_id;
+};
+
+int loongson_i2c_init(struct loongson_device *ldev);
+
+#endif /* __LOONGSON_I2C_H__ */
--
2.25.1

2022-06-25 11:57:40

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 1/4] drm/loongson: Add DRM Driver for Loongson 7A1000 bridge chip

Hi Chenyang,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on linus/master v5.19-rc3 next-20220624]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/intel-lab-lkp/linux/commits/Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
config: powerpc-allmodconfig
compiler: powerpc-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/438d0791edb6352903bf09dfe214453526081075
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
git checkout 438d0791edb6352903bf09dfe214453526081075
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/gpu/drm/loongson/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <[email protected]>

All error/warnings (new ones prefixed by >>):

drivers/gpu/drm/loongson/loongson_crtc.c: In function 'loongson_crtc_mode_set_nofb':
>> drivers/gpu/drm/loongson/loongson_crtc.c:128:42: error: invalid use of undefined type 'struct drm_framebuffer'
128 | format = crtc->primary->state->fb->format;
| ^~
--
drivers/gpu/drm/loongson/loongson_device.c: In function 'loongson_gpu_offset':
>> drivers/gpu/drm/loongson/loongson_device.c:14:44: error: invalid use of undefined type 'struct drm_framebuffer'
14 | gbo = drm_gem_vram_of_gem(state->fb->obj[0]);
| ^~
--
In file included from include/linux/device.h:15,
from include/linux/pci.h:37,
from drivers/gpu/drm/loongson/loongson_drv.c:14:
drivers/gpu/drm/loongson/loongson_drv.c: In function 'loongson_device_init':
>> include/drm/drm_print.h:425:39: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 3 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
425 | dev_##level##type((drm)->dev, "[drm] " fmt, ##__VA_ARGS__)
| ^~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:150:58: note: in expansion of macro 'dev_fmt'
150 | dev_printk_index_wrap(_dev_info, KERN_INFO, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
include/drm/drm_print.h:425:9: note: in expansion of macro 'dev_info'
425 | dev_##level##type((drm)->dev, "[drm] " fmt, ##__VA_ARGS__)
| ^~~~
include/drm/drm_print.h:429:9: note: in expansion of macro '__drm_printk'
429 | __drm_printk((drm), info,, fmt, ##__VA_ARGS__)
| ^~~~~~~~~~~~
drivers/gpu/drm/loongson/loongson_drv.c:91:9: note: in expansion of macro 'drm_info'
91 | drm_info(dev, "DC mmio base 0x%llx size 0x%llx io 0x%llx\n",
| ^~~~~~~~
include/drm/drm_print.h:425:39: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'resource_size_t' {aka 'unsigned int'} [-Wformat=]
425 | dev_##level##type((drm)->dev, "[drm] " fmt, ##__VA_ARGS__)
| ^~~~~~~~
include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
110 | _p_func(dev, fmt, ##__VA_ARGS__); \
| ^~~
include/linux/dev_printk.h:150:58: note: in expansion of macro 'dev_fmt'
150 | dev_printk_index_wrap(_dev_info, KERN_INFO, dev, dev_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~
include/drm/drm_print.h:425:9: note: in expansion of macro 'dev_info'
425 | dev_##level##type((drm)->dev, "[drm] " fmt, ##__VA_ARGS__)
| ^~~~
include/drm/drm_print.h:429:9: note: in expansion of macro '__drm_printk'
429 | __drm_printk((drm), info,, fmt, ##__VA_ARGS__)
| ^~~~~~~~~~~~
drivers/gpu/drm/loongson/loongson_drv.c:91:9: note: in expansion of macro 'drm_info'
91 | drm_info(dev, "DC mmio base 0x%llx size 0x%llx io 0x%llx\n",
| ^~~~~~~~
drivers/gpu/drm/loongson/loongson_drv.c: At top level:
drivers/gpu/drm/loongson/loongson_drv.c:99:5: warning: no previous prototype for 'loongson_modeset_init' [-Wmissing-prototypes]
99 | int loongson_modeset_init(struct loongson_device *ldev)
| ^~~~~~~~~~~~~~~~~~~~~
--
drivers/gpu/drm/loongson/loongson_plane.c: In function 'loongson_plane_atomic_update':
>> drivers/gpu/drm/loongson/loongson_plane.c:27:27: error: invalid use of undefined type 'struct drm_framebuffer'
27 | pitch = lstate->fb->pitches[0];
| ^~
drivers/gpu/drm/loongson/loongson_plane.c:33:27: error: invalid use of undefined type 'struct drm_framebuffer'
33 | depth = lstate->fb->format->cpp[0] << 3;
| ^~


vim +128 drivers/gpu/drm/loongson/loongson_crtc.c

102
103 static void loongson_crtc_mode_set_nofb(struct drm_crtc *crtc)
104 {
105 struct drm_device *dev = crtc->dev;
106 struct loongson_device *ldev = to_loongson_device(dev);
107 struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
108 struct drm_display_mode *mode = &crtc->state->adjusted_mode;
109 const struct drm_format_info *format;
110 struct pix_pll pll_cfg;
111 u32 hr, hss, hse, hfl;
112 u32 vr, vss, vse, vfl;
113 u32 pix_freq;
114 u32 reg_offset;
115
116 hr = mode->hdisplay;
117 hss = mode->hsync_start;
118 hse = mode->hsync_end;
119 hfl = mode->htotal;
120
121 vr = mode->vdisplay;
122 vss = mode->vsync_start;
123 vse = mode->vsync_end;
124 vfl = mode->vtotal;
125
126 pix_freq = mode->clock;
127 reg_offset = lcrtc->reg_offset;
> 128 format = crtc->primary->state->fb->format;
129
130 ls7a_mm_wreg(ldev, FB_DITCFG_REG + reg_offset, 0);
131 ls7a_mm_wreg(ldev, FB_DITTAB_LO_REG + reg_offset, 0);
132 ls7a_mm_wreg(ldev, FB_DITTAB_HI_REG + reg_offset, 0);
133 ls7a_mm_wreg(ldev, FB_PANCFG_REG + reg_offset, FB_PANCFG_DEF);
134 ls7a_mm_wreg(ldev, FB_PANTIM_REG + reg_offset, 0);
135
136 ls7a_mm_wreg(ldev, FB_HDISPLAY_REG + reg_offset, (hfl << 16) | hr);
137 ls7a_mm_wreg(ldev, FB_HSYNC_REG + reg_offset,
138 FB_HSYNC_PULSE | (hse << 16) | hss);
139
140 ls7a_mm_wreg(ldev, FB_VDISPLAY_REG + reg_offset, (vfl << 16) | vr);
141 ls7a_mm_wreg(ldev, FB_VSYNC_REG + reg_offset,
142 FB_VSYNC_PULSE | (vse << 16) | vss);
143
144 switch (format->format) {
145 case DRM_FORMAT_RGB565:
146 lcrtc->cfg_reg |= 0x3;
147 break;
148 case DRM_FORMAT_RGB888:
149 case DRM_FORMAT_XRGB8888:
150 case DRM_FORMAT_ARGB8888:
151 default:
152 lcrtc->cfg_reg |= 0x4;
153 break;
154 }
155
156 ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
157
158 cal_freq(pix_freq, &pll_cfg);
159 config_pll(ldev, LS7A_PIX_PLL + reg_offset, &pll_cfg);
160 }
161

--
0-DAY CI Kernel Test Service
https://01.org/lkp

2022-06-25 12:41:16

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 2/4] drm/loongson: Add GPIO and I2C driver for loongson drm.

Hi Chenyang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on drm-misc/drm-misc-next]
[also build test WARNING on linus/master v5.19-rc3 next-20220624]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/intel-lab-lkp/linux/commits/Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
config: powerpc-allmodconfig
compiler: powerpc-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/3af56da81352153b38e05c082b8f2bf8c9fc0320
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
git checkout 3af56da81352153b38e05c082b8f2bf8c9fc0320
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/gpu/drm/loongson/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/loongson/loongson_encoder.c:10:27: warning: no previous prototype for 'loongson_bridge_detect' [-Wmissing-prototypes]
10 | enum drm_connector_status loongson_bridge_detect(struct drm_bridge *bridge)
| ^~~~~~~~~~~~~~~~~~~~~~


vim +/loongson_bridge_detect +10 drivers/gpu/drm/loongson/loongson_encoder.c

9
> 10 enum drm_connector_status loongson_bridge_detect(struct drm_bridge *bridge)
11 {
12 unsigned char start = 0x0;
13 struct i2c_msg msgs = {
14 .addr = DDC_ADDR,
15 .flags = 0,
16 .len = 1,
17 .buf = &start,
18 };
19
20 if (i2c_transfer(bridge->ddc, &msgs, 1) != 1)
21 return connector_status_disconnected;
22 else
23 return connector_status_connected;
24 }
25

--
0-DAY CI Kernel Test Service
https://01.org/lkp

2022-07-11 14:54:44

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 2/4] drm/loongson: Add GPIO and I2C driver for loongson drm.

Hi Chenyang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on drm-misc/drm-misc-next]
[also build test WARNING on linus/master v5.19-rc6 next-20220708]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
config: arm64-allmodconfig
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 77a38f6839980bfac61babb40d83772c51427011)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# https://github.com/intel-lab-lkp/linux/commit/3af56da81352153b38e05c082b8f2bf8c9fc0320
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
git checkout 3af56da81352153b38e05c082b8f2bf8c9fc0320
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash drivers/gpu/drm/loongson/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/loongson/loongson_encoder.c:10:27: warning: no previous prototype for function 'loongson_bridge_detect' [-Wmissing-prototypes]
enum drm_connector_status loongson_bridge_detect(struct drm_bridge *bridge)
^
drivers/gpu/drm/loongson/loongson_encoder.c:10:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
enum drm_connector_status loongson_bridge_detect(struct drm_bridge *bridge)
^
static
1 warning generated.


vim +/loongson_bridge_detect +10 drivers/gpu/drm/loongson/loongson_encoder.c

9
> 10 enum drm_connector_status loongson_bridge_detect(struct drm_bridge *bridge)
11 {
12 unsigned char start = 0x0;
13 struct i2c_msg msgs = {
14 .addr = DDC_ADDR,
15 .flags = 0,
16 .len = 1,
17 .buf = &start,
18 };
19
20 if (i2c_transfer(bridge->ddc, &msgs, 1) != 1)
21 return connector_status_disconnected;
22 else
23 return connector_status_connected;
24 }
25

--
0-DAY CI Kernel Test Service
https://01.org/lkp


Attachments:
(No filename) (2.88 kB)
config (352.73 kB)
Download all attachments

2022-07-11 20:59:50

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 3/4] drm/loongson: Add interrupt driver for LS7A.

Hi Chenyang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on drm-misc/drm-misc-next]
[also build test WARNING on linus/master v5.19-rc6 next-20220708]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
config: arm64-allmodconfig (https://download.01.org/0day-ci/archive/20220712/[email protected]/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 77a38f6839980bfac61babb40d83772c51427011)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# https://github.com/intel-lab-lkp/linux/commit/7cad653ee3a3b83188e2d91335269753e134b808
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Chenyang-Li/drm-loongson-Add-DRM-Driver-for-Loongson-7A1000-bridge-chip/20220625-171037
git checkout 7cad653ee3a3b83188e2d91335269753e134b808
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash drivers/gpu/drm/loongson/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/loongson/loongson_irq.c:24:11: warning: variable 'lcrtc' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
else if (val & FB_VSYNC1_INT)
^~~~~~~~~~~~~~~~~~~
drivers/gpu/drm/loongson/loongson_irq.c:27:26: note: uninitialized use occurs here
drm_crtc_handle_vblank(&lcrtc->base);
^~~~~
drivers/gpu/drm/loongson/loongson_irq.c:24:7: note: remove the 'if' if its condition is always true
else if (val & FB_VSYNC1_INT)
^~~~~~~~~~~~~~~~~~~~~~~~
drivers/gpu/drm/loongson/loongson_irq.c:16:29: note: initialize the variable 'lcrtc' to silence this warning
struct loongson_crtc *lcrtc;
^
= NULL
1 warning generated.


vim +24 drivers/gpu/drm/loongson/loongson_irq.c

11
12 static irqreturn_t loongson_irq_handler(int irq, void *arg)
13 {
14 struct drm_device *dev = (struct drm_device *) arg;
15 struct loongson_device *ldev = to_loongson_device(dev);
16 struct loongson_crtc *lcrtc;
17 u32 val;
18
19 val = ls7a_mm_rreg(ldev, FB_INT_REG);
20 ls7a_mm_wreg(ldev, FB_INT_REG, val & (0xffff << 16));
21
22 if (val & FB_VSYNC0_INT)
23 lcrtc = ldev->mode_info[0].crtc;
> 24 else if (val & FB_VSYNC1_INT)
25 lcrtc = ldev->mode_info[1].crtc;
26
27 drm_crtc_handle_vblank(&lcrtc->base);
28
29 return IRQ_HANDLED;
30 }
31

--
0-DAY CI Kernel Test Service
https://01.org/lkp

2022-09-06 12:02:55

by 李晨阳

[permalink] [raw]
Subject: [PATCH v7 2/4] drm/loongson: Add GPIO and I2C driver for loongson drm.

Implement use GPIO and I2C driver to detect connector
and fetch EDID via DDC.

v5:
- Use braidge->ddc to get EDID and detect connector.

v4:
- Delete the gpio_chip subsystem call.
- Delete some redundant prints.

v3:
- Change some driver log to the drm_ version.

v2:
- Optimize the error handling process.
- Delete loongson_i2c_bus_match and loongson_i2c_add function.
- Optimize part of the code flow.

Signed-off-by: Yi Li <[email protected]>
Signed-off-by: Chenyang Li <[email protected]>
---
drivers/gpu/drm/loongson/Makefile | 1 +
drivers/gpu/drm/loongson/loongson_drv.c | 13 +-
drivers/gpu/drm/loongson/loongson_drv.h | 7 +
drivers/gpu/drm/loongson/loongson_encoder.c | 31 +++-
drivers/gpu/drm/loongson/loongson_i2c.c | 191 ++++++++++++++++++++
drivers/gpu/drm/loongson/loongson_i2c.h | 33 ++++
6 files changed, 266 insertions(+), 10 deletions(-)
create mode 100644 drivers/gpu/drm/loongson/loongson_i2c.c
create mode 100644 drivers/gpu/drm/loongson/loongson_i2c.h

diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
index 534a64047fb6..4a5fab7d000b 100644
--- a/drivers/gpu/drm/loongson/Makefile
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -9,5 +9,6 @@ loongson-y := loongson_connector.o \
loongson_device.o \
loongson_drv.o \
loongson_encoder.o \
+ loongson_i2c.o \
loongson_plane.o
obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
index 4993b8d9e8ca..2e3ef6193767 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.c
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -22,9 +22,10 @@

/* Interface history:
* 0.1 - original.
+ * 0.2 - add i2c and connector detect.
*/
#define DRIVER_MAJOR 0
-#define DRIVER_MINOR 1
+#define DRIVER_MINOR 2

static const struct drm_mode_config_funcs loongson_mode_funcs = {
.fb_create = drm_gem_fb_create,
@@ -88,6 +89,14 @@ static int loongson_device_init(struct drm_device *dev)

ldev->num_crtc = 2;

+ ret = loongson_dc_gpio_init(ldev);
+ if (ret)
+ return ret;
+
+ ret = loongson_i2c_init(ldev);
+ if (ret)
+ return ret;
+
drm_info(dev, "DC mmio base 0x%llx size 0x%llx io 0x%llx\n",
mmio_base, mmio_size, *(u64 *)ldev->io);
drm_info(dev, "GPU vram start = 0x%x size = 0x%x\n",
@@ -96,7 +105,7 @@ static int loongson_device_init(struct drm_device *dev)
return 0;
}

-int loongson_modeset_init(struct loongson_device *ldev)
+static int loongson_modeset_init(struct loongson_device *ldev)
{
int i;
int ret;
diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
index e9e97db00110..5be29d2d1a49 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.h
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -10,6 +10,8 @@
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_bridge.h>

+#include "loongson_i2c.h"
+
/* General customization:
*/
#define DRIVER_AUTHOR "Loongson graphics driver team"
@@ -102,6 +104,8 @@ struct loongson_device {
u32 num_crtc;
struct loongson_mode_info mode_info[2];
struct pci_dev *gpu_pdev; /* LS7A gpu device info */
+
+ struct loongson_i2c i2c_bus[DC_MAX_I2C_BUS];
};

static inline struct loongson_device *to_loongson_device(struct drm_device *dev)
@@ -121,6 +125,9 @@ int loongson_encoder_init(struct loongson_device *ldev, int index);
/* plane */
struct loongson_plane *loongson_plane_init(struct drm_device *dev, int index);

+/* i2c */
+int loongson_dc_gpio_init(struct loongson_device *ldev);
+
/* device */
u32 loongson_gpu_offset(struct drm_plane_state *state,
struct loongson_device *dev);
diff --git a/drivers/gpu/drm/loongson/loongson_encoder.c b/drivers/gpu/drm/loongson/loongson_encoder.c
index 5b94f707f1a0..accf144479f8 100644
--- a/drivers/gpu/drm/loongson/loongson_encoder.c
+++ b/drivers/gpu/drm/loongson/loongson_encoder.c
@@ -7,19 +7,31 @@

#include "loongson_drv.h"

-static int loongson_bridge_get_modes(struct drm_bridge *bridge,
- struct drm_connector *connector)
+enum drm_connector_status loongson_bridge_detect(struct drm_bridge *bridge)
{
- int count;
+ unsigned char start = 0x0;
+ struct i2c_msg msgs = {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ };

- count = drm_add_modes_noedid(connector, 1920, 1080);
- drm_set_preferred_mode(connector, 1024, 768);
+ if (i2c_transfer(bridge->ddc, &msgs, 1) != 1)
+ return connector_status_disconnected;
+ else
+ return connector_status_connected;
+}

- return count;
+static struct edid *loongson_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ return drm_get_edid(connector, bridge->ddc);
}

static const struct drm_bridge_funcs loongson_encoder_bridge_funcs = {
- .get_modes = loongson_bridge_get_modes,
+ .detect = loongson_bridge_detect,
+ .get_edid = loongson_bridge_get_edid,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
@@ -40,7 +52,10 @@ int loongson_encoder_init(struct loongson_device *ldev, int index)
ldev->mode_info[index].encoder = lencoder;

lencoder->bridge.funcs = &loongson_encoder_bridge_funcs;
- lencoder->bridge.ops = DRM_BRIDGE_OP_MODES;
+ lencoder->bridge.ddc = ldev->i2c_bus[index].adapter;
+ lencoder->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+ | DRM_BRIDGE_OP_MODES;
+
if (index == 0)
lencoder->bridge.type = DRM_MODE_CONNECTOR_VGA;
else if (index == 1)
diff --git a/drivers/gpu/drm/loongson/loongson_i2c.c b/drivers/gpu/drm/loongson/loongson_i2c.c
new file mode 100644
index 000000000000..acc50ca3df22
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_i2c.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include "loongson_drv.h"
+#include "loongson_i2c.h"
+
+static inline void dc_gpio_set_dir(struct loongson_device *ldev,
+ unsigned int pin, int input)
+{
+ u32 temp;
+
+ temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_CFG_OFFSET);
+ if (input)
+ temp |= 1UL << pin;
+ else
+ temp &= ~(1UL << pin);
+
+ ls7a_mm_wreg(ldev, LS7A_DC_GPIO_CFG_OFFSET, temp);
+}
+
+static void dc_gpio_set_val(struct loongson_device *ldev, unsigned int pin,
+ int high)
+{
+ u32 temp;
+
+ temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_OUT_OFFSET);
+ if (high)
+ temp |= 1UL << pin;
+ else
+ temp &= ~(1UL << pin);
+
+ ls7a_mm_wreg(ldev, LS7A_DC_GPIO_OUT_OFFSET, temp);
+}
+
+static void loongson_i2c_set_data(void *i2c, int value)
+{
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->data;
+
+ if (value)
+ dc_gpio_set_dir(ldev, pin, 1);
+ else {
+ dc_gpio_set_val(ldev, pin, 0);
+ dc_gpio_set_dir(ldev, pin, 0);
+ }
+}
+
+static void loongson_i2c_set_clock(void *i2c, int value)
+{
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->clock;
+
+ if (value)
+ dc_gpio_set_dir(ldev, pin, 1);
+ else {
+ dc_gpio_set_val(ldev, pin, 0);
+ dc_gpio_set_dir(ldev, pin, 0);
+ }
+}
+
+static int loongson_i2c_get_data(void *i2c)
+{
+ int val;
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->data;
+
+ val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
+
+ return (val >> pin) & 1;
+}
+
+static int loongson_i2c_get_clock(void *i2c)
+{
+ int val;
+ struct loongson_i2c *li2c = i2c;
+ struct loongson_device *ldev = li2c->ldev;
+ unsigned int pin = li2c->clock;
+
+ val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
+
+ return (val >> pin) & 1;
+}
+
+static int loongson_i2c_create(struct loongson_device *ldev,
+ struct loongson_i2c *li2c, const char *name)
+{
+ int ret;
+ unsigned int i2c_num;
+ struct drm_device *dev = &ldev->dev;
+ struct i2c_client *i2c_cli;
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_algo_bit_data *i2c_algo_data;
+ const struct i2c_board_info i2c_info = {
+ .type = "ddc-dev",
+ .addr = DDC_ADDR,
+ .flags = I2C_CLASS_DDC,
+ };
+
+ i2c_num = li2c->i2c_id;
+ i2c_adapter = devm_kzalloc(dev->dev, sizeof(*i2c_adapter), GFP_KERNEL);
+ if (!i2c_adapter)
+ return -ENOMEM;
+
+ i2c_algo_data = devm_kzalloc(dev->dev, sizeof(*i2c_algo_data), GFP_KERNEL);
+ if (!i2c_algo_data) {
+ ret = -ENOMEM;
+ goto free_adapter;
+ }
+
+ i2c_adapter->owner = THIS_MODULE;
+ i2c_adapter->class = I2C_CLASS_DDC;
+ i2c_adapter->algo_data = i2c_algo_data;
+ i2c_adapter->dev.parent = dev->dev;
+ i2c_adapter->nr = -1;
+ snprintf(i2c_adapter->name, sizeof(i2c_adapter->name), "%s%d",
+ name, i2c_num);
+
+ li2c->data = i2c_num * 2;
+ li2c->clock = i2c_num * 2 + 1;
+ DRM_INFO("Created i2c-%d, sda=%d, scl=%d\n",
+ i2c_num, li2c->data, li2c->clock);
+
+ i2c_algo_data->setsda = loongson_i2c_set_data;
+ i2c_algo_data->setscl = loongson_i2c_set_clock;
+ i2c_algo_data->getsda = loongson_i2c_get_data;
+ i2c_algo_data->getscl = loongson_i2c_get_clock;
+ i2c_algo_data->udelay = DC_I2C_TON;
+ i2c_algo_data->timeout = usecs_to_jiffies(2200);
+
+ ret = i2c_bit_add_numbered_bus(i2c_adapter);
+ if (ret)
+ goto free_algo_data;
+
+ li2c->adapter = i2c_adapter;
+ i2c_algo_data->data = li2c;
+ i2c_set_adapdata(li2c->adapter, li2c);
+ li2c->ldev = ldev;
+ DRM_INFO("Register i2c algo-bit adapter [%s]\n", i2c_adapter->name);
+
+ i2c_cli = i2c_new_client_device(i2c_adapter, &i2c_info);
+ if (IS_ERR(i2c_cli)) {
+ ret = PTR_ERR(i2c_cli);
+ goto remove_i2c_adapter;
+ }
+
+ return 0;
+
+remove_i2c_adapter:
+ drm_err(dev, "Failed to create i2c client\n");
+ i2c_del_adapter(i2c_adapter);
+free_algo_data:
+ drm_err(dev, "Failed to register i2c adapter %s\n", i2c_adapter->name);
+ kfree(i2c_algo_data);
+free_adapter:
+ kfree(i2c_adapter);
+
+ return ret;
+}
+
+int loongson_dc_gpio_init(struct loongson_device *ldev)
+{
+ int pin;
+
+ /* set gpio dir output 0-3 */
+ for (pin = 0; pin < 4; pin++) {
+ dc_gpio_set_val(ldev, pin, 0);
+ dc_gpio_set_dir(ldev, pin, 0);
+ }
+
+ return 0;
+}
+
+int loongson_i2c_init(struct loongson_device *ldev)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ ldev->i2c_bus[1].i2c_id = i;
+ ret = loongson_i2c_create(ldev, &ldev->i2c_bus[i], DC_I2C_NAME);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/loongson/loongson_i2c.h b/drivers/gpu/drm/loongson/loongson_i2c.h
new file mode 100644
index 000000000000..9944c838d3e4
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_i2c.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef __LOONGSON_I2C_H__
+#define __LOONGSON_I2C_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <drm/drm_edid.h>
+
+#define DC_I2C_TON 5
+#define DC_I2C_NAME "ls_dc_i2c"
+#define DC_MAX_I2C_BUS 2
+
+#define LS7A_DC_GPIO_CFG_OFFSET (0x1660)
+#define LS7A_DC_GPIO_IN_OFFSET (0x1650)
+#define LS7A_DC_GPIO_OUT_OFFSET (0x1650)
+
+struct loongson_device;
+struct loongson_i2c {
+ struct loongson_device *ldev;
+ struct i2c_adapter *adapter;
+ u32 data;
+ u32 clock;
+ u32 i2c_id;
+};
+
+int loongson_i2c_init(struct loongson_device *ldev);
+
+#endif /* __LOONGSON_I2C_H__ */
--
2.25.1


本邮件及其附件含有龙芯中科的商业秘密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制或散发)本邮件及其附件中的信息。如果您错收本邮件,请您立即电话或邮件通知发件人并删除本邮件。
This email and its attachments contain confidential information from Loongson Technology , which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this email in error, please notify the sender by phone or email immediately and delete it.

2022-09-06 12:07:47

by 李晨阳

[permalink] [raw]
Subject: [PATCH v7 3/4] drm/loongson: Add interrupt driver for LS7A.

Add LS7A DC vsync interrupt enable and close function, and
register irq_handler function interface.
Add vbrank event processing flow.

v4:
- Replace drm_irq_install with devm_request_irq.
- Delete the irq_ hooks in drm_driver.

v3:
- Improve code readability.
- Use the to_pci_dev function to get pci_dev.

v2:
- Added error handling in the loongson_drm_load function.

Signed-off-by: Chenyang Li <[email protected]>
---
drivers/gpu/drm/loongson/Makefile | 1 +
drivers/gpu/drm/loongson/loongson_crtc.c | 37 +++++++++++
drivers/gpu/drm/loongson/loongson_drv.c | 9 ++-
drivers/gpu/drm/loongson/loongson_drv.h | 10 +++
drivers/gpu/drm/loongson/loongson_irq.c | 84 ++++++++++++++++++++++++
5 files changed, 140 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/loongson/loongson_irq.c

diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
index 4a5fab7d000b..b083854d789b 100644
--- a/drivers/gpu/drm/loongson/Makefile
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -10,5 +10,6 @@ loongson-y := loongson_connector.o \
loongson_drv.o \
loongson_encoder.o \
loongson_i2c.o \
+ loongson_irq.o \
loongson_plane.o
obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_crtc.c b/drivers/gpu/drm/loongson/loongson_crtc.c
index 611378cef8e8..6a31ff911ce1 100644
--- a/drivers/gpu/drm/loongson/loongson_crtc.c
+++ b/drivers/gpu/drm/loongson/loongson_crtc.c
@@ -4,6 +4,7 @@
*/

#include <drm/drm_fourcc.h>
+#include <drm/drm_vblank.h>

#include "loongson_drv.h"

@@ -167,8 +168,14 @@ static void loongson_crtc_atomic_enable(struct drm_crtc *crtc,
struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
u32 reg_offset = lcrtc->reg_offset;

+ if (lcrtc->cfg_reg & CFG_ENABLE)
+ goto vblank_on;
+
lcrtc->cfg_reg |= CFG_ENABLE;
ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
+
+vblank_on:
+ drm_crtc_vblank_on(crtc);
}

static void loongson_crtc_atomic_disable(struct drm_crtc *crtc,
@@ -181,6 +188,33 @@ static void loongson_crtc_atomic_disable(struct drm_crtc *crtc,

lcrtc->cfg_reg &= ~CFG_ENABLE;
ls7a_mm_wreg(ldev, FB_CFG_REG + reg_offset, lcrtc->cfg_reg);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+
+ drm_crtc_vblank_off(crtc);
+}
+
+static void loongson_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ if (!event)
+ return;
+
+ 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 enum drm_mode_status loongson_mode_valid(struct drm_crtc *crtc,
@@ -200,6 +234,7 @@ static enum drm_mode_status loongson_mode_valid(struct drm_crtc *crtc,

static const struct drm_crtc_helper_funcs loongson_crtc_helper_funcs = {
.mode_valid = loongson_mode_valid,
+ .atomic_flush = loongson_crtc_atomic_flush,
.atomic_enable = loongson_crtc_atomic_enable,
.atomic_disable = loongson_crtc_atomic_disable,
.mode_set_nofb = loongson_crtc_mode_set_nofb,
@@ -211,6 +246,8 @@ static const struct drm_crtc_funcs loongson_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = loongson_crtc_enable_vblank,
+ .disable_vblank = loongson_crtc_disable_vblank,
};

int loongson_crtc_init(struct loongson_device *ldev, int index)
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
index 2e3ef6193767..1d6f35e78813 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.c
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -23,9 +23,10 @@
/* Interface history:
* 0.1 - original.
* 0.2 - add i2c and connector detect.
+ * 0.3 - Vblank and vsync interrupt support.
*/
#define DRIVER_MAJOR 0
-#define DRIVER_MINOR 2
+#define DRIVER_MINOR 3

static const struct drm_mode_config_funcs loongson_mode_funcs = {
.fb_create = drm_gem_fb_create,
@@ -167,6 +168,12 @@ static int loongson_driver_init(struct drm_device *dev)
goto err;
}

+ ret = loongson_irq_init(ldev);
+ if (ret) {
+ dev_err(dev->dev, "Fatal error during irq init: %d\n", ret);
+ goto err;
+ }
+
drm_kms_helper_poll_init(dev);
drm_mode_config_reset(dev);

diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
index 5be29d2d1a49..af47e68487fd 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.h
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -40,6 +40,7 @@
#define FB_HSYNC_REG (0x1420)
#define FB_VDISPLAY_REG (0x1480)
#define FB_VSYNC_REG (0x14a0)
+#define FB_INT_REG (0x1570)

#define CFG_FMT GENMASK(2, 0)
#define CFG_FBSWITCH BIT(7)
@@ -51,6 +52,10 @@
#define FB_PANCFG_DEF 0x80001311
#define FB_HSYNC_PULSE (1 << 30)
#define FB_VSYNC_PULSE (1 << 30)
+#define FB_VSYNC1_ENABLE (1 << 16)
+#define FB_VSYNC0_ENABLE (1 << 18)
+#define FB_VSYNC1_INT (1 << 0)
+#define FB_VSYNC0_INT (1 << 2)

/* PIX PLL */
#define LOOPC_MIN 24
@@ -128,6 +133,11 @@ struct loongson_plane *loongson_plane_init(struct drm_device *dev, int index);
/* i2c */
int loongson_dc_gpio_init(struct loongson_device *ldev);

+/* irq */
+int loongson_irq_init(struct loongson_device *ldev);
+int loongson_crtc_enable_vblank(struct drm_crtc *crtc);
+void loongson_crtc_disable_vblank(struct drm_crtc *crtc);
+
/* device */
u32 loongson_gpu_offset(struct drm_plane_state *state,
struct loongson_device *dev);
diff --git a/drivers/gpu/drm/loongson/loongson_irq.c b/drivers/gpu/drm/loongson/loongson_irq.c
new file mode 100644
index 000000000000..ba215d585dc7
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_irq.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_vblank.h>
+
+#include "loongson_drv.h"
+
+static irqreturn_t loongson_irq_handler(int irq, void *arg)
+{
+ struct drm_device *dev = (struct drm_device *) arg;
+ struct loongson_device *ldev = to_loongson_device(dev);
+ struct loongson_crtc *lcrtc;
+ u32 val;
+
+ val = ls7a_mm_rreg(ldev, FB_INT_REG);
+ ls7a_mm_wreg(ldev, FB_INT_REG, val & (0xffff << 16));
+
+ if (val & FB_VSYNC0_INT)
+ lcrtc = ldev->mode_info[0].crtc;
+ else if (val & FB_VSYNC1_INT)
+ lcrtc = ldev->mode_info[1].crtc;
+
+ drm_crtc_handle_vblank(&lcrtc->base);
+
+ return IRQ_HANDLED;
+}
+
+int loongson_irq_init(struct loongson_device *ldev)
+{
+ int ret;
+ struct drm_device *dev = &ldev->dev;
+ int irq = to_pci_dev(dev->dev)->irq;
+
+ ret = drm_vblank_init(dev, ldev->num_crtc);
+ if (ret) {
+ dev_err(dev->dev, "Fatal error during vblank init: %d\n", ret);
+ return ret;
+ }
+ DRM_INFO("drm vblank init finished\n");
+
+ ret = devm_request_irq(dev->dev, irq, loongson_irq_handler, 0,
+ "loongson-drm", dev);
+ if (ret) {
+ dev_err(dev->dev, "Fatal error during irq install: %d\n", ret);
+ return ret;
+ }
+ DRM_INFO("loongson irq initialized\n");
+
+ return 0;
+}
+
+int loongson_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+ struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
+ struct loongson_device *ldev = lcrtc->ldev;
+ u32 reg_val = ls7a_mm_rreg(ldev, FB_INT_REG);
+
+ if (lcrtc->crtc_id)
+ reg_val |= FB_VSYNC1_ENABLE;
+ else
+ reg_val |= FB_VSYNC0_ENABLE;
+
+ ls7a_mm_wreg(ldev, FB_INT_REG, reg_val);
+
+ return 0;
+}
+
+void loongson_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+ struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
+ struct loongson_device *ldev = lcrtc->ldev;
+ u32 reg_val = ls7a_mm_rreg(ldev, FB_INT_REG);
+
+ if (lcrtc->crtc_id)
+ reg_val &= ~FB_VSYNC1_ENABLE;
+ else
+ reg_val &= ~FB_VSYNC0_ENABLE;
+
+ ls7a_mm_wreg(ldev, FB_INT_REG, reg_val);
+}
--
2.25.1


本邮件及其附件含有龙芯中科的商业秘密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制或散发)本邮件及其附件中的信息。如果您错收本邮件,请您立即电话或邮件通知发件人并删除本邮件。
This email and its attachments contain confidential information from Loongson Technology , which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this email in error, please notify the sender by phone or email immediately and delete it.

2022-09-06 12:21:33

by 李晨阳

[permalink] [raw]
Subject: [PATCH v7 4/4] drm/loongson: Use acpi to get video bios.

Add get video bios from the ACPI table.
Define new acpi table "VIAT" for loongson video bios.

Signed-off-by: Chenyang Li <[email protected]>
---
drivers/gpu/drm/loongson/Makefile | 3 +-
drivers/gpu/drm/loongson/loongson_drv.c | 21 +++++++-
drivers/gpu/drm/loongson/loongson_drv.h | 1 +
drivers/gpu/drm/loongson/loongson_vbios.c | 62 +++++++++++++++++++++++
drivers/gpu/drm/loongson/loongson_vbios.h | 29 +++++++++++
5 files changed, 113 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/loongson/loongson_vbios.c
create mode 100644 drivers/gpu/drm/loongson/loongson_vbios.h

diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile
index b083854d789b..b774a91003d9 100644
--- a/drivers/gpu/drm/loongson/Makefile
+++ b/drivers/gpu/drm/loongson/Makefile
@@ -11,5 +11,6 @@ loongson-y := loongson_connector.o \
loongson_encoder.o \
loongson_i2c.o \
loongson_irq.o \
- loongson_plane.o
+ loongson_plane.o \
+ loongson_vbios.o
obj-$(CONFIG_DRM_LOONGSON) += loongson.o
diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c
index 1d6f35e78813..1c212200f088 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.c
+++ b/drivers/gpu/drm/loongson/loongson_drv.c
@@ -19,14 +19,28 @@
#include <drm/drm_probe_helper.h>

#include "loongson_drv.h"
+#include "loongson_vbios.h"
+
+/*
+ * Completed
+ * 1.Displays controller device initialization and display funcitons
+ * 2.I2c bus driver and DDC functions
+ * 3.Vblank and vsync interrupt support
+ * 4.Use acpi to get video bios
+ * Todo
+ * 1.Video bios parse functions
+ * 2.Hardware cursor driver
+ * 3.New device support as well as Loongson GPU
+ */

/* Interface history:
* 0.1 - original.
* 0.2 - add i2c and connector detect.
* 0.3 - Vblank and vsync interrupt support.
+ * 0.4 - Use acpi to get vbios.
*/
#define DRIVER_MAJOR 0
-#define DRIVER_MINOR 3
+#define DRIVER_MINOR 4

static const struct drm_mode_config_funcs loongson_mode_funcs = {
.fb_create = drm_gem_fb_create,
@@ -88,7 +102,10 @@ static int loongson_device_init(struct drm_device *dev)
if (!ldev->io)
return -ENOMEM;

- ldev->num_crtc = 2;
+ if (!loongson_vbios_init(ldev)) {
+ DRM_WARN("Get vbios failed, enable two crtc\n");
+ ldev->num_crtc = 2;
+ }

ret = loongson_dc_gpio_init(ldev);
if (ret)
diff --git a/drivers/gpu/drm/loongson/loongson_drv.h b/drivers/gpu/drm/loongson/loongson_drv.h
index af47e68487fd..4e5cb5977c9a 100644
--- a/drivers/gpu/drm/loongson/loongson_drv.h
+++ b/drivers/gpu/drm/loongson/loongson_drv.h
@@ -109,6 +109,7 @@ struct loongson_device {
u32 num_crtc;
struct loongson_mode_info mode_info[2];
struct pci_dev *gpu_pdev; /* LS7A gpu device info */
+ void *vbios;

struct loongson_i2c i2c_bus[DC_MAX_I2C_BUS];
};
diff --git a/drivers/gpu/drm/loongson/loongson_vbios.c b/drivers/gpu/drm/loongson/loongson_vbios.c
new file mode 100644
index 000000000000..2b3a2757102a
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_vbios.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include "loongson_drv.h"
+#include "loongson_vbios.h"
+
+#ifdef CONFIG_ACPI
+static bool read_bios_from_acpi(struct loongson_device *ldev)
+{
+ void *vaddr;
+ struct acpi_table_header *hdr;
+ struct acpi_viat_table *viat;
+ acpi_size tbl_size;
+
+ if (!ACPI_SUCCESS(acpi_get_table("VIAT", 1, &hdr)))
+ return false;
+
+ tbl_size = hdr->length;
+ if (tbl_size != sizeof(struct acpi_viat_table)) {
+ DRM_WARN("ACPI viat table present but broken(too short #1)\n");
+ return false;
+ }
+
+ viat = (struct acpi_viat_table *)hdr;
+ ldev->vbios = kmalloc(VBIOS_SIZE, GFP_KERNEL);
+ if (!ldev->vbios) {
+ kfree(ldev->vbios);
+ return false;
+ }
+
+ vaddr = phys_to_virt(viat->vbios_addr);
+ memcpy(ldev->vbios, vaddr, VBIOS_SIZE);
+ DRM_INFO("Get vbios from ACPI success!\n");
+
+ return true;
+}
+#else
+static bool read_bios_from_acpi(struct loongson_device *ldev)
+{
+ return false;
+}
+#endif
+
+bool loongson_vbios_init(struct loongson_device *ldev)
+{
+ int ret;
+ struct loongson_vbios *header;
+
+ ret = read_bios_from_acpi(ldev);
+ if (!ret)
+ return ret;
+
+ header = ldev->vbios;
+ ldev->num_crtc = header->crtc_num;
+
+ DRM_INFO("Loongson vbios version %d.%d crtc num %d.\n",
+ header->version_major, header->version_minor, ldev->num_crtc);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/loongson/loongson_vbios.h b/drivers/gpu/drm/loongson/loongson_vbios.h
new file mode 100644
index 000000000000..b7d8ce15c6c5
--- /dev/null
+++ b/drivers/gpu/drm/loongson/loongson_vbios.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef __LOONGSON_VBIOS_H__
+#define __LOONGSON_VBIOS_H__
+
+#include <linux/acpi.h>
+
+#define VBIOS_SIZE 0x40000
+
+struct loongson_vbios {
+ uint32_t version_major;
+ uint32_t version_minor;
+ uint32_t crtc_num;
+} __packed;
+
+#ifdef CONFIG_ACPI
+/* VBOIS INFO ADDRESS TABLE */
+struct acpi_viat_table {
+ struct acpi_table_header header;
+ u64 vbios_addr;
+} __packed;
+#endif
+
+bool loongson_vbios_init(struct loongson_device *ldev);
+
+#endif /* __LOONGSON_VBIOS_H__ */
--
2.25.1


本邮件及其附件含有龙芯中科的商业秘密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制或散发)本邮件及其附件中的信息。如果您错收本邮件,请您立即电话或邮件通知发件人并删除本邮件。
This email and its attachments contain confidential information from Loongson Technology , which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this email in error, please notify the sender by phone or email immediately and delete it.