[d-kernel] [PATCH 20/32] drm: added Baikal-M SoC video display unit driver
Alexey Sheplyakov
asheplyakov на basealt.ru
Ср Дек 14 16:19:07 MSK 2022
Due to hardware peculiarities using both HDMI and DP outputs is
possible only with some constraints:
- Resolution of both displays should be exactly the same
- HDMI viewport must be above or below the display port one
Co-developed-by: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
Signed-off-by: Alexey Sheplyakov <asheplyakov на basealt.ru>
X-feature-Baikal-M
---
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/baikal/Kconfig | 15 +
drivers/gpu/drm/baikal/Makefile | 10 +
drivers/gpu/drm/baikal/baikal-hdmi.c | 119 ++++++
drivers/gpu/drm/baikal/baikal_vdu_connector.c | 118 ++++++
drivers/gpu/drm/baikal/baikal_vdu_crtc.c | 345 +++++++++++++++++
drivers/gpu/drm/baikal/baikal_vdu_debugfs.c | 87 +++++
drivers/gpu/drm/baikal/baikal_vdu_drm.h | 65 ++++
drivers/gpu/drm/baikal/baikal_vdu_drv.c | 364 ++++++++++++++++++
drivers/gpu/drm/baikal/baikal_vdu_plane.c | 210 ++++++++++
drivers/gpu/drm/baikal/baikal_vdu_regs.h | 139 +++++++
drivers/gpu/drm/bridge/Kconfig | 7 +
13 files changed, 1481 insertions(+)
create mode 100644 drivers/gpu/drm/baikal/Kconfig
create mode 100644 drivers/gpu/drm/baikal/Makefile
create mode 100644 drivers/gpu/drm/baikal/baikal-hdmi.c
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_connector.c
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_crtc.c
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_debugfs.c
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_drm.h
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_drv.c
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_plane.c
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_regs.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 34f5a092c99e..e0738ba51430 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -232,6 +232,7 @@ config DRM_SCHED
source "drivers/gpu/drm/i2c/Kconfig"
source "drivers/gpu/drm/arm/Kconfig"
+source "drivers/gpu/drm/baikal/Kconfig"
config DRM_RADEON
tristate "ATI Radeon"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0b283e46f28b..db0456c3dd20 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -148,3 +148,4 @@ obj-y += gud/
obj-$(CONFIG_DRM_HYPERV) += hyperv/
obj-y += solomon/
obj-$(CONFIG_DRM_SPRD) += sprd/
+obj-$(CONFIG_DRM_BAIKAL_VDU) += baikal/
diff --git a/drivers/gpu/drm/baikal/Kconfig b/drivers/gpu/drm/baikal/Kconfig
new file mode 100644
index 000000000000..a514fd430ee6
--- /dev/null
+++ b/drivers/gpu/drm/baikal/Kconfig
@@ -0,0 +1,15 @@
+config DRM_BAIKAL_VDU
+ tristate "DRM Support for Baikal-M VDU"
+ depends on DRM
+ depends on ARM || ARM64 || COMPILE_TEST
+ depends on COMMON_CLK
+ default y if ARCH_BAIKAL
+ select DRM_KMS_HELPER
+ select DRM_KMS_DMA_HELPER
+ select DRM_GEM_DMA_HELPER
+ select DRM_PANEL
+ select DRM_BAIKAL_HDMI
+ select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+ help
+ Choose this option for DRM support for the Baikal-M Video Display Unit (VDU).
+ If M is selected the module will be called baikal_vdu_drm.
diff --git a/drivers/gpu/drm/baikal/Makefile b/drivers/gpu/drm/baikal/Makefile
new file mode 100644
index 000000000000..eb029494e823
--- /dev/null
+++ b/drivers/gpu/drm/baikal/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+baikal_vdu_drm-y += baikal_vdu_connector.o \
+ baikal_vdu_crtc.o \
+ baikal_vdu_drv.o \
+ baikal_vdu_plane.o
+
+baikal_vdu_drm-$(CONFIG_DEBUG_FS) += baikal_vdu_debugfs.o
+
+obj-$(CONFIG_DRM_BAIKAL_VDU) += baikal_vdu_drm.o
+obj-$(CONFIG_DRM_BAIKAL_HDMI) += baikal-hdmi.o
diff --git a/drivers/gpu/drm/baikal/baikal-hdmi.c b/drivers/gpu/drm/baikal/baikal-hdmi.c
new file mode 100644
index 000000000000..6a55d03d93f8
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal-hdmi.c
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Baikal Electronics BE-M1000 DesignWare HDMI 2.0 Tx PHY support driver
+ *
+ * Copyright (C) 2019-2021 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart на ideasonboard.com)
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <drm/drm_modes.h>
+
+#include <drm/bridge/dw_hdmi.h>
+
+int fixed_clock = 0;
+int max_clock = 0;
+
+static const struct dw_hdmi_mpll_config baikal_hdmi_mpll_cfg[] = {
+ /* pixelclk opmode gmp */
+ { 44900000, { { 0x00b3, 0x0000 }, }, },
+ { 90000000, { { 0x0072, 0x0001 }, }, },
+ { 182750000, { { 0x0051, 0x0002 }, }, },
+ { 340000000, { { 0x0040, 0x0003 }, }, },
+ { 594000000, { { 0x1a40, 0x0003 }, }, },
+ { ~0UL, { { 0x0000, 0x0000 }, }, }
+};
+
+static const struct dw_hdmi_curr_ctrl baikal_hdmi_cur_ctr[] = {
+ /* pixelclk current */
+ { 44900000, { 0x0000, }, },
+ { 90000000, { 0x0008, }, },
+ { 182750000, { 0x001b, }, },
+ { 340000000, { 0x0036, }, },
+ { 594000000, { 0x003f, }, },
+ { ~0UL, { 0x0000, }, }
+};
+
+static const struct dw_hdmi_phy_config baikal_hdmi_phy_cfg[] = {
+ /* pixelclk symbol term vlev */
+ { 148250000, 0x8009, 0x0004, 0x0232},
+ { 218250000, 0x8009, 0x0004, 0x0230},
+ { 288000000, 0x8009, 0x0004, 0x0273},
+ { 340000000, 0x8029, 0x0004, 0x0273},
+ { 594000000, 0x8039, 0x0004, 0x014a},
+ { ~0UL, 0x0000, 0x0000, 0x0000}
+};
+
+static enum drm_mode_status baikal_hdmi_mode_valid(struct dw_hdmi *hdmi,
+ void *data,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ if (mode->clock < 13500)
+ return MODE_CLOCK_LOW;
+ if (mode->clock >= 340000)
+ return MODE_CLOCK_HIGH;
+ if (fixed_clock && mode->clock != fixed_clock)
+ return MODE_BAD;
+ if (max_clock && mode->clock > max_clock)
+ return MODE_BAD;
+
+ return MODE_OK;
+}
+
+static struct dw_hdmi_plat_data baikal_dw_hdmi_plat_data = {
+ .mpll_cfg = baikal_hdmi_mpll_cfg,
+ .cur_ctr = baikal_hdmi_cur_ctr,
+ .phy_config = baikal_hdmi_phy_cfg,
+ .mode_valid = baikal_hdmi_mode_valid,
+};
+
+static int baikal_dw_hdmi_probe(struct platform_device *pdev)
+{
+ struct dw_hdmi *hdmi;
+ hdmi = dw_hdmi_probe(pdev, &baikal_dw_hdmi_plat_data);
+ if (IS_ERR(hdmi)) {
+ return PTR_ERR(hdmi);
+ } else {
+ return 0;
+ }
+}
+
+static int baikal_dw_hdmi_remove(struct platform_device *pdev)
+{
+ struct dw_hdmi *hdmi = platform_get_drvdata(pdev);
+ dw_hdmi_remove(hdmi);
+ return 0;
+}
+
+static const struct of_device_id baikal_dw_hdmi_of_table[] = {
+ { .compatible = "baikal,hdmi" },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, baikal_dw_hdmi_of_table);
+
+static struct platform_driver baikal_dw_hdmi_platform_driver = {
+ .probe = baikal_dw_hdmi_probe,
+ .remove = baikal_dw_hdmi_remove,
+ .driver = {
+ .name = "baikal-dw-hdmi",
+ .of_match_table = baikal_dw_hdmi_of_table,
+ },
+};
+
+module_param(fixed_clock, int, 0644);
+module_param(max_clock, int, 0644);
+
+module_platform_driver(baikal_dw_hdmi_platform_driver);
+
+MODULE_AUTHOR("Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal BE-M1000 SoC DesignWare HDMI 2.0 Tx + Gen2 PHY Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_connector.c b/drivers/gpu/drm/baikal/baikal_vdu_connector.c
new file mode 100644
index 000000000000..2f20cf3da627
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_connector.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied на linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * baikal_vdu_connector.c
+ * Implementation of the connector functions for Baikal Electronics BE-M1000 SoC's VDU
+ */
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+#define to_baikal_vdu_private(x) \
+ container_of(x, struct baikal_vdu_private, connector)
+
+static void baikal_vdu_drm_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status baikal_vdu_drm_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct baikal_vdu_private *priv = to_baikal_vdu_private(connector);
+
+ return (priv->panel ?
+ connector_status_connected :
+ connector_status_disconnected);
+}
+
+static int baikal_vdu_drm_connector_helper_get_modes(
+ struct drm_connector *connector)
+{
+ struct baikal_vdu_private *priv = to_baikal_vdu_private(connector);
+
+ if (!priv->panel)
+ return 0;
+
+ return drm_panel_get_modes(priv->panel, connector);
+}
+
+const struct drm_connector_funcs connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = baikal_vdu_drm_connector_destroy,
+ .detect = baikal_vdu_drm_connector_detect,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = baikal_vdu_drm_connector_helper_get_modes,
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+int baikal_vdu_lvds_connector_create(struct drm_device *dev)
+{
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct drm_connector *connector = &priv->connector;
+ struct drm_encoder *encoder = &priv->encoder;
+ int ret = 0;
+
+ ret = drm_connector_init(dev, connector, &connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ if (ret) {
+ dev_err(dev->dev, "drm_connector_init failed: %d\n", ret);
+ goto out;
+ }
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+ ret = drm_encoder_init(dev, encoder, &encoder_funcs,
+ DRM_MODE_ENCODER_LVDS, NULL);
+ if (ret) {
+ dev_err(dev->dev, "drm_encoder_init failed: %d\n", ret);
+ goto out;
+ }
+ encoder->crtc = &priv->crtc;
+ encoder->possible_crtcs = drm_crtc_mask(encoder->crtc);
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret) {
+ dev_err(dev->dev, "drm_connector_attach_encoder failed: %d\n", ret);
+ goto out;
+ }
+ ret = drm_connector_register(connector);
+ if (ret) {
+ dev_err(dev->dev, "drm_connector_register failed: %d\n", ret);
+ goto out;
+ }
+out:
+ return ret;
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_crtc.c b/drivers/gpu/drm/baikal/baikal_vdu_crtc.c
new file mode 100644
index 000000000000..e338ff8b3080
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_crtc.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied на linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * baikal_vdu_crtc.c
+ * Implementation of the CRTC functions for Baikal Electronics BE-M1000 VDU driver
+ */
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_vblank.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+struct baikal_vdu_crtc_mode_fixup {
+ int vdisplay;
+ int vfp_add;
+};
+
+static const struct baikal_vdu_crtc_mode_fixup mode_fixups[] = {
+ { 480, 38 },
+ { 600, 8 },
+ { 720, 43 },
+ { 768, 43 },
+ { 800, 71 },
+ { 864, 71 },
+ { 900, 71 },
+ { 960, 71 },
+ { 1024, 25 },
+ { 1050, 25 },
+ { 1080, 8 },
+ { 1200, 32 },
+ { 1440, 27 },
+ { ~0U },
+};
+
+irqreturn_t baikal_vdu_irq(int irq, void *data)
+{
+ struct drm_device *drm = data;
+ struct baikal_vdu_private *priv = drm->dev_private;
+ irqreturn_t status = IRQ_NONE;
+ u32 raw_stat;
+ u32 irq_stat;
+
+ irq_stat = readl(priv->regs + IVR);
+ raw_stat = readl(priv->regs + ISR);
+
+ if (irq_stat & INTR_VCT) {
+ priv->counters[10]++;
+ drm_crtc_handle_vblank(&priv->crtc);
+ status = IRQ_HANDLED;
+ }
+
+ if (irq_stat & INTR_FER) {
+ priv->counters[11]++;
+ priv->counters[12] = readl(priv->regs + DBAR);
+ priv->counters[13] = readl(priv->regs + DCAR);
+ priv->counters[14] = readl(priv->regs + MRR);
+ status = IRQ_HANDLED;
+ }
+
+ priv->counters[3] |= raw_stat;
+
+ /* Clear all interrupts */
+ writel(irq_stat, priv->regs + ISR);
+
+ return status;
+}
+
+bool baikal_vdu_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct baikal_vdu_private *priv = crtc->dev->dev_private;
+
+ memcpy(adjusted_mode, mode, sizeof(*mode));
+
+ if (!priv->mode_fixup)
+ return true;
+
+ if (priv->mode_fixup == -1) {
+ const struct baikal_vdu_crtc_mode_fixup *fixups = mode_fixups;
+ for (; fixups && fixups->vdisplay != ~0U; ++fixups) {
+ if (mode->vdisplay <= fixups->vdisplay)
+ break;
+ }
+ if (fixups->vdisplay == ~0U)
+ return true;
+ else
+ priv->mode_fixup = fixups->vfp_add;
+ }
+
+ adjusted_mode->vtotal += priv->mode_fixup;
+ adjusted_mode->vsync_start += priv->mode_fixup;
+ adjusted_mode->vsync_end += priv->mode_fixup;
+ adjusted_mode->clock = mode->clock * adjusted_mode->vtotal / mode->vtotal;
+
+ return true;
+}
+
+static void baikal_vdu_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct baikal_vdu_private *priv = dev->dev_private;
+ const struct drm_display_mode *orig_mode = &crtc->state->mode;
+ const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ unsigned int ppl, hsw, hfp, hbp;
+ unsigned int lpp, vsw, vfp, vbp;
+ unsigned int reg;
+
+ drm_mode_debug_printmodeline(orig_mode);
+ drm_mode_debug_printmodeline(mode);
+
+ ppl = mode->hdisplay / 16;
+ if (priv->type == VDU_TYPE_LVDS) {
+ hsw = mode->hsync_end - mode->hsync_start;
+ hfp = mode->hsync_start - mode->hdisplay - 1;
+ hbp = mode->htotal - mode->hsync_end;
+ } else {
+ hsw = mode->hsync_end - mode->hsync_start - 1;
+ hfp = mode->hsync_start - mode->hdisplay;
+ hbp = mode->htotal - mode->hsync_end - 1;
+ }
+
+ lpp = mode->vdisplay;
+ vsw = mode->vsync_end - mode->vsync_start;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vbp = mode->vtotal - mode->vsync_end;
+
+ writel((HTR_HFP(hfp) & HTR_HFP_MASK) |
+ (HTR_PPL(ppl) & HTR_PPL_MASK) |
+ (HTR_HBP(hbp) & HTR_HBP_MASK) |
+ (HTR_HSW(hsw) & HTR_HSW_MASK),
+ priv->regs + HTR);
+
+ if (mode->hdisplay > 4080 || ppl * 16 != mode->hdisplay)
+ writel((HPPLOR_HPPLO(mode->hdisplay) & HPPLOR_HPPLO_MASK) | HPPLOR_HPOE,
+ priv->regs + HPPLOR);
+
+ writel((VTR1_VSW(vsw) & VTR1_VSW_MASK) |
+ (VTR1_VFP(vfp) & VTR1_VFP_MASK) |
+ (VTR1_VBP(vbp) & VTR1_VBP_MASK),
+ priv->regs + VTR1);
+
+ writel(lpp & VTR2_LPP_MASK, priv->regs + VTR2);
+
+ writel((HVTER_VSWE(vsw >> VTR1_VSW_LSB_WIDTH) & HVTER_VSWE_MASK) |
+ (HVTER_HSWE(hsw >> HTR_HSW_LSB_WIDTH) & HVTER_HSWE_MASK) |
+ (HVTER_VBPE(vbp >> VTR1_VBP_LSB_WIDTH) & HVTER_VBPE_MASK) |
+ (HVTER_VFPE(vfp >> VTR1_VFP_LSB_WIDTH) & HVTER_VFPE_MASK) |
+ (HVTER_HBPE(hbp >> HTR_HBP_LSB_WIDTH) & HVTER_HBPE_MASK) |
+ (HVTER_HFPE(hfp >> HTR_HFP_LSB_WIDTH) & HVTER_HFPE_MASK),
+ priv->regs + HVTER);
+
+ /* Set polarities */
+ reg = readl(priv->regs + CR1);
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ reg |= CR1_VSP;
+ else
+ reg &= ~CR1_VSP;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ reg |= CR1_HSP;
+ else
+ reg &= ~CR1_HSP;
+ reg |= CR1_DEP; // set DE to active high;
+ writel(reg, priv->regs + CR1);
+
+ crtc->hwmode = crtc->state->adjusted_mode;
+}
+
+static void baikal_vdu_crtc_helper_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct baikal_vdu_private *priv = crtc->dev->dev_private;
+ struct drm_panel *panel = priv->panel;
+ const char *data_mapping = NULL;
+ u32 cntl, gpio;
+
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "priv = %px\n", priv);
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "enabling pixel clock\n");
+ clk_prepare_enable(priv->clk);
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "panel = %px\n", panel);
+ drm_panel_prepare(panel);
+
+ writel(ISCR_VSC_VFP, priv->regs + ISCR);
+
+ /* release clock reset; enable clocking */
+ cntl = readl(priv->regs + PCTR);
+ cntl |= PCTR_PCR + PCTR_PCI;
+ writel(cntl, priv->regs + PCTR);
+
+ /* Set 16-word input FIFO watermark */
+ /* Enable and Power Up */
+ cntl = readl(priv->regs + CR1);
+ cntl &= ~CR1_FDW_MASK;
+ cntl |= CR1_LCE + CR1_FDW_16_WORDS;
+
+ if (priv->type == VDU_TYPE_LVDS) {
+ if (panel) {
+ of_property_read_string(panel->dev->of_node,
+ "data-mapping", &data_mapping);
+ }
+ if (!data_mapping) {
+ cntl |= CR1_OPS_LCD18;
+ dev_dbg(crtc->dev->dev, "data mapping not specified, using jeida-18");
+ } else if (!strncmp(data_mapping, "vesa-24", 7)) {
+ cntl |= CR1_OPS_LCD24;
+ dev_dbg(crtc->dev->dev, "using vesa-24 mapping\n");
+ } else if (!strncmp(data_mapping, "jeida-18", 8)) {
+ cntl |= CR1_OPS_LCD18;
+ dev_dbg(crtc->dev->dev, "using jeida-18 mapping\n");
+ } else {
+ dev_warn(crtc->dev->dev, "unsupported data mapping '%s', using vesa-24\n", data_mapping);
+ cntl |= CR1_OPS_LCD24;
+ }
+
+ gpio = GPIOR_UHD_ENB;
+ if (priv->ep_count == 4)
+ gpio |= GPIOR_UHD_QUAD_PORT;
+ else if (priv->ep_count == 2)
+ gpio |= GPIOR_UHD_DUAL_PORT;
+ else
+ gpio |= GPIOR_UHD_SNGL_PORT;
+ writel(gpio, priv->regs + GPIOR);
+ } else
+ cntl |= CR1_OPS_LCD24;
+ writel(cntl, priv->regs + CR1);
+
+ drm_panel_enable(priv->panel);
+ drm_crtc_vblank_on(crtc);
+}
+
+void baikal_vdu_crtc_helper_disable(struct drm_crtc *crtc)
+{
+ struct baikal_vdu_private *priv = crtc->dev->dev_private;
+
+ drm_crtc_vblank_off(crtc);
+ drm_panel_disable(priv->panel);
+
+ drm_panel_unprepare(priv->panel);
+
+ /* Disable clock */
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "disabling pixel clock\n");
+ clk_disable_unprepare(priv->clk);
+}
+
+static void baikal_vdu_crtc_helper_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ if (event) {
+ crtc->state->event = NULL;
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->active && 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 int baikal_vdu_enable_vblank(struct drm_crtc *crtc)
+{
+ struct baikal_vdu_private *priv = crtc->dev->dev_private;
+
+ /* clear interrupt status */
+ writel(0x3ffff, priv->regs + ISR);
+
+ writel(INTR_VCT + INTR_FER, priv->regs + IMR);
+
+ return 0;
+}
+
+static void baikal_vdu_disable_vblank(struct drm_crtc *crtc)
+{
+ struct baikal_vdu_private *priv = crtc->dev->dev_private;
+
+ /* clear interrupt status */
+ writel(0x3ffff, priv->regs + ISR);
+
+ writel(INTR_FER, priv->regs + IMR);
+}
+
+const struct drm_crtc_funcs crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = baikal_vdu_enable_vblank,
+ .disable_vblank = baikal_vdu_disable_vblank,
+};
+
+const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+ .mode_fixup = baikal_vdu_crtc_mode_fixup,
+ .mode_set_nofb = baikal_vdu_crtc_helper_mode_set_nofb,
+ .atomic_flush = baikal_vdu_crtc_helper_atomic_flush,
+ .disable = baikal_vdu_crtc_helper_disable,
+ .atomic_enable = baikal_vdu_crtc_helper_enable,
+};
+
+int baikal_vdu_crtc_create(struct drm_device *dev)
+{
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct drm_crtc *crtc = &priv->crtc;
+
+ drm_crtc_init_with_planes(dev, crtc,
+ &priv->primary, NULL,
+ &crtc_funcs, "primary");
+ drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+ /* XXX: The runtime clock disabling still results in
+ * occasional system hangs, and needs debugging.
+ */
+
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "enabling pixel clock\n");
+ clk_prepare_enable(priv->clk);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_debugfs.c b/drivers/gpu/drm/baikal/baikal_vdu_debugfs.c
new file mode 100644
index 000000000000..77be6aa588dc
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_debugfs.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright © 2017 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+#define REGDEF(reg) { reg, #reg }
+static const struct {
+ u32 reg;
+ const char *name;
+} baikal_vdu_reg_defs[] = {
+ REGDEF(CR1),
+ REGDEF(HTR),
+ REGDEF(VTR1),
+ REGDEF(VTR2),
+ REGDEF(PCTR),
+ REGDEF(ISR),
+ REGDEF(IMR),
+ REGDEF(IVR),
+ REGDEF(ISCR),
+ REGDEF(DBAR),
+ REGDEF(DCAR),
+ REGDEF(DEAR),
+ REGDEF(HVTER),
+ REGDEF(HPPLOR),
+ REGDEF(GPIOR),
+ REGDEF(OWER),
+ REGDEF(OWXSER0),
+ REGDEF(OWYSER0),
+ REGDEF(OWDBAR0),
+ REGDEF(OWDCAR0),
+ REGDEF(OWDEAR0),
+ REGDEF(OWXSER1),
+ REGDEF(OWYSER1),
+ REGDEF(OWDBAR1),
+ REGDEF(OWDCAR1),
+ REGDEF(OWDEAR1),
+ REGDEF(MRR),
+};
+
+int baikal_vdu_debugfs_regs(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct baikal_vdu_private *priv = dev->dev_private;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(baikal_vdu_reg_defs); i++) {
+ seq_printf(m, "%s (0x%04x): 0x%08x\n",
+ baikal_vdu_reg_defs[i].name, baikal_vdu_reg_defs[i].reg,
+ readl(priv->regs + baikal_vdu_reg_defs[i].reg));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->counters); i++) {
+ seq_printf(m, "COUNTER[%d]: 0x%08x\n", i, priv->counters[i]);
+ }
+
+ return 0;
+}
+
+static const struct drm_info_list baikal_vdu_debugfs_list[] = {
+ {"regs", baikal_vdu_debugfs_regs, 0},
+};
+
+void baikal_vdu_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(baikal_vdu_debugfs_list,
+ ARRAY_SIZE(baikal_vdu_debugfs_list),
+ minor->debugfs_root, minor);
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_drm.h b/drivers/gpu/drm/baikal/baikal_vdu_drm.h
new file mode 100644
index 000000000000..755d4abeedf7
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_drm.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied на linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#ifndef __BAIKAL_VDU_DRM_H__
+#define __BAIKAL_VDU_DRM_H__
+
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#define VDU_TYPE_HDMI 0
+#define VDU_TYPE_LVDS 1
+
+struct baikal_vdu_private {
+ struct drm_device *drm;
+
+ unsigned int irq;
+ bool irq_enabled;
+
+ struct drm_connector connector;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
+ struct drm_panel *panel;
+ struct drm_bridge *bridge;
+ struct drm_plane primary;
+
+ void *regs;
+ struct clk *clk;
+ u32 counters[20];
+ int mode_fixup;
+ int type;
+ u32 ep_count;
+ u32 fb_addr;
+ u32 fb_end;
+
+ struct gpio_desc *enable_gpio;
+};
+
+/* CRTC Functions */
+int baikal_vdu_crtc_create(struct drm_device *dev);
+irqreturn_t baikal_vdu_irq(int irq, void *data);
+
+int baikal_vdu_primary_plane_init(struct drm_device *dev);
+
+/* Connector Functions */
+int baikal_vdu_lvds_connector_create(struct drm_device *dev);
+
+void baikal_vdu_debugfs_init(struct drm_minor *minor);
+
+#endif /* __BAIKAL_VDU_DRM_H__ */
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_drv.c b/drivers/gpu/drm/baikal/baikal_vdu_drv.c
new file mode 100644
index 000000000000..18fa1762dab0
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_drv.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ * All bugs by Alexey Sheplyakov <asheplyakov на altlinux.org>
+ *
+ * This driver is based on ARM PL111 DRM driver
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied на linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_aperture.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+#define DRIVER_NAME "baikal-vdu"
+#define DRIVER_DESC "DRM module for Baikal VDU"
+#define DRIVER_DATE "20200131"
+
+#define BAIKAL_SMC_SCP_LOG_DISABLE 0x82000200
+
+int mode_fixup = 0;
+
+static struct drm_mode_config_funcs mode_config_funcs = {
+ .fb_create = drm_gem_fb_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static const struct drm_encoder_funcs baikal_vdu_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+DEFINE_DRM_GEM_DMA_FOPS(drm_fops);
+
+static struct drm_driver vdu_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+ .ioctls = NULL,
+ .fops = &drm_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = 1,
+ .minor = 0,
+ .patchlevel = 0,
+ DRM_GEM_DMA_DRIVER_OPS,
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = baikal_vdu_debugfs_init,
+#endif
+};
+
+static int vdu_modeset_init(struct drm_device *dev)
+{
+ struct drm_mode_config *mode_config;
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct arm_smccc_res res;
+ int ret = 0, ep_count = 0;
+
+ if (priv == NULL)
+ return -EINVAL;
+
+ drm_mode_config_init(dev);
+ mode_config = &dev->mode_config;
+ mode_config->funcs = &mode_config_funcs;
+ mode_config->min_width = 1;
+ mode_config->max_width = 4095;
+ mode_config->min_height = 1;
+ mode_config->max_height = 4095;
+
+ ret = baikal_vdu_primary_plane_init(dev);
+ if (ret != 0) {
+ dev_err(dev->dev, "Failed to init primary plane\n");
+ goto out_config;
+ }
+
+ ret = baikal_vdu_crtc_create(dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to create crtc\n");
+ goto out_config;
+ }
+
+ ret = drm_of_find_panel_or_bridge(dev->dev->of_node, -1, -1,
+ &priv->panel,
+ &priv->bridge);
+ if (ret == -EPROBE_DEFER) {
+ dev_info(dev->dev, "Bridge probe deferred\n");
+ goto out_config;
+ }
+ ep_count = of_graph_get_endpoint_count(dev->dev->of_node);
+ if (ep_count <= 0) {
+ dev_err(dev->dev, "no endpoints connected to panel/bridge\n");
+ goto out_config;
+ }
+ priv->ep_count = ep_count;
+ dev_dbg(dev->dev, "panel/bridge has %d endpoints\n", priv->ep_count);
+
+ if (priv->bridge) {
+ struct drm_encoder *encoder = &priv->encoder;
+ ret = drm_encoder_init(dev, encoder, &baikal_vdu_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret) {
+ dev_err(dev->dev, "failed to create DRM encoder\n");
+ goto out_config;
+ }
+ encoder->crtc = &priv->crtc;
+ encoder->possible_crtcs = drm_crtc_mask(encoder->crtc);
+ priv->bridge->encoder = &priv->encoder;
+ ret = drm_bridge_attach(&priv->encoder, priv->bridge, NULL, 0);
+ if (ret) {
+ dev_err(dev->dev, "Failed to attach DRM bridge %d\n", ret);
+ goto out_config;
+ }
+ } else if (priv->panel) {
+ dev_dbg(dev->dev, "panel has %d endpoints\n", priv->ep_count);
+ ret = baikal_vdu_lvds_connector_create(dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to create DRM connector\n");
+ goto out_config;
+ }
+ } else
+ ret = -EINVAL;
+
+ if (ret) {
+ dev_err(dev->dev, "No bridge or panel attached!\n");
+ goto out_config;
+ }
+
+ priv->clk = clk_get(dev->dev, "pclk");
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev->dev, "fatal: unable to get pclk, err %ld\n", PTR_ERR(priv->clk));
+ ret = PTR_ERR(priv->clk);
+ goto out_config;
+ }
+
+ priv->mode_fixup = mode_fixup;
+
+ drm_aperture_remove_framebuffers(false, &vdu_drm_driver);
+
+ ret = drm_vblank_init(dev, 1);
+ if (ret != 0) {
+ dev_err(dev->dev, "Failed to init vblank\n");
+ goto out_clk;
+ }
+
+ arm_smccc_smc(BAIKAL_SMC_SCP_LOG_DISABLE, 0, 0, 0, 0, 0, 0, 0, &res);
+
+ drm_mode_config_reset(dev);
+
+ drm_kms_helper_poll_init(dev);
+
+ ret = drm_dev_register(dev, 0);
+ if (ret)
+ goto out_clk;
+
+ drm_fbdev_generic_setup(dev, 32);
+ goto finish;
+
+out_clk:
+ clk_put(priv->clk);
+out_config:
+ drm_mode_config_cleanup(dev);
+finish:
+ return ret;
+}
+
+
+static int baikal_vdu_irq_install(struct baikal_vdu_private *priv, int irq)
+{
+ int ret;
+ ret= request_irq(irq, baikal_vdu_irq, 0, DRIVER_NAME, priv->drm);
+ if (ret < 0)
+ return ret;
+ priv->irq_enabled = true;
+ return 0;
+}
+
+static void baikal_vdu_irq_uninstall(struct baikal_vdu_private *priv)
+{
+ if (priv->irq_enabled) {
+ priv->irq_enabled = false;
+ disable_irq(priv->irq);
+ free_irq(priv->irq, priv->drm);
+ }
+}
+
+static int vdu_maybe_enable_lvds(struct baikal_vdu_private *vdu)
+{
+ int err = 0;
+ struct device *dev;
+ if (!vdu->drm) {
+ pr_err("%s: vdu->drm is NULL\n", __func__);
+ return -EINVAL;
+ }
+ dev = vdu->drm->dev;
+
+ vdu->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(vdu->enable_gpio)) {
+ err = (int)PTR_ERR(vdu->enable_gpio);
+ dev_err(dev, "failed to get enable-gpios, error %d\n", err);
+ vdu->enable_gpio = NULL;
+ return err;
+ }
+ if (vdu->enable_gpio) {
+ dev_dbg(dev, "%s: setting enable-gpio\n", __func__);
+ gpiod_set_value_cansleep(vdu->enable_gpio, 1);
+ } else {
+ dev_dbg(dev, "%s: no enable-gpios, assuming it's handled by panel-lvds\n", __func__);
+ }
+ return 0;
+}
+
+static int baikal_vdu_drm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct baikal_vdu_private *priv;
+ struct drm_device *drm;
+ struct resource *mem;
+ int irq;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ drm = drm_dev_alloc(&vdu_drm_driver, dev);
+ if (IS_ERR(drm))
+ return PTR_ERR(drm);
+ platform_set_drvdata(pdev, drm);
+ priv->drm = drm;
+ drm->dev_private = priv;
+
+ if (!(mem = platform_get_resource(pdev, IORESOURCE_MEM, 0))) {
+ dev_err(dev, "%s no MMIO resource specified\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(priv->regs)) {
+ dev_err(dev, "%s MMIO allocation failed\n", __func__);
+ return PTR_ERR(priv->regs);
+ }
+
+ /* turn off interrupts before requesting the irq */
+ writel(0, priv->regs + IMR);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "%s no IRQ resource specified\n", __func__);
+ return -EINVAL;
+ }
+ priv->irq = irq;
+
+ ret = baikal_vdu_irq_install(priv, irq);
+ if (ret != 0) {
+ dev_err(dev, "%s IRQ %d allocation failed\n", __func__, irq);
+ return ret;
+ }
+
+ if (pdev->dev.of_node && of_property_read_bool(pdev->dev.of_node, "lvds-out")) {
+ priv->type = VDU_TYPE_LVDS;
+ if (of_property_read_u32(pdev->dev.of_node, "num-lanes", &priv->ep_count))
+ priv->ep_count = 1;
+ }
+ else
+ priv->type = VDU_TYPE_HDMI;
+
+ ret = vdu_modeset_init(drm);
+ if (ret != 0) {
+ dev_err(dev, "Failed to init modeset\n");
+ goto dev_unref;
+ }
+
+ ret = vdu_maybe_enable_lvds(priv);
+ if (ret != 0) {
+ dev_err(dev, "failed to enable LVDS\n");
+ }
+
+ return 0;
+
+dev_unref:
+ writel(0, priv->regs + IMR);
+ writel(0x3ffff, priv->regs + ISR);
+ baikal_vdu_irq_uninstall(priv);
+ drm->dev_private = NULL;
+ drm_dev_put(drm);
+ return ret;
+}
+
+static int baikal_vdu_drm_remove(struct platform_device *pdev)
+{
+ struct drm_device *drm;
+ struct baikal_vdu_private *priv;
+
+ drm = platform_get_drvdata(pdev);
+ if (!drm) {
+ return -1;
+ }
+ priv = drm->dev_private;
+
+ drm_dev_unregister(drm);
+ drm_mode_config_cleanup(drm);
+ baikal_vdu_irq_uninstall(priv);
+ drm->dev_private = NULL;
+ drm_dev_put(drm);
+
+ return 0;
+}
+
+static const struct of_device_id baikal_vdu_of_match[] = {
+ { .compatible = "baikal,vdu" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, baikal_vdu_of_match);
+
+static struct platform_driver baikal_vdu_platform_driver = {
+ .probe = baikal_vdu_drm_probe,
+ .remove = baikal_vdu_drm_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = baikal_vdu_of_match,
+ },
+};
+
+module_param(mode_fixup, int, 0644);
+
+module_platform_driver(baikal_vdu_platform_driver);
+
+MODULE_AUTHOR("Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal Electronics BE-M1000 Video Display Unit (VDU) DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_SOFTDEP("pre: baikal_hdmi");
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_plane.c b/drivers/gpu/drm/baikal/baikal_vdu_plane.c
new file mode 100644
index 000000000000..0be1e0967914
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_plane.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied на linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/of_graph.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+static int baikal_vdu_primary_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *atomic_state)
+{
+ struct drm_device *dev = plane->dev;
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct drm_crtc_state *crtc_state;
+ struct drm_plane_state *state;
+ struct drm_display_mode *mode;
+ int rate, ret;
+ u32 cntl;
+
+ state = drm_atomic_get_new_plane_state(atomic_state, plane);
+ if (!state || !state->crtc)
+ return 0;
+
+ crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ dev_warn(dev->dev, "failed to get crtc_state: %d\n", ret);
+ return ret;
+ }
+ mode = &crtc_state->adjusted_mode;
+ rate = mode->clock * 1000;
+ if (rate == clk_get_rate(priv->clk))
+ return 0;
+
+ /* hold clock domain reset; disable clocking */
+ writel(0, priv->regs + PCTR);
+
+ if (__clk_is_enabled(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ ret = clk_set_rate(priv->clk, rate);
+ DRM_DEV_DEBUG_DRIVER(dev->dev, "Requested pixel clock is %d Hz\n", rate);
+
+ if (ret < 0) {
+ DRM_ERROR("Cannot set desired pixel clock (%d Hz)\n",
+ rate);
+ ret = -EINVAL;
+ } else {
+ clk_prepare_enable(priv->clk);
+ if (__clk_is_enabled(priv->clk))
+ ret = 0;
+ else {
+ DRM_ERROR("PLL could not lock at desired frequency (%d Hz)\n",
+ rate);
+ ret = -EINVAL;
+ }
+ }
+
+ /* release clock domain reset; enable clocking */
+ cntl = readl(priv->regs + PCTR);
+ cntl |= PCTR_PCR + PCTR_PCI;
+ writel(cntl, priv->regs + PCTR);
+
+ return ret;
+}
+
+static void baikal_vdu_primary_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_device *dev = plane->dev;
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ u32 cntl, addr, end;
+
+ if (!fb)
+ return;
+
+ addr = drm_fb_dma_get_gem_addr(fb, state, 0);
+ priv->fb_addr = addr & 0xfffffff8;
+
+ cntl = readl(priv->regs + CR1);
+ cntl &= ~CR1_BPP_MASK;
+
+ /* Note that the the hardware's format reader takes 'r' from
+ * the low bit, while DRM formats list channels from high bit
+ * to low bit as you read left to right.
+ */
+ switch (fb->format->format) {
+ case DRM_FORMAT_BGR888:
+ cntl |= CR1_BPP24 | CR1_FBP | CR1_BGR;
+ break;
+ case DRM_FORMAT_RGB888:
+ cntl |= CR1_BPP24 | CR1_FBP;
+ break;
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ cntl |= CR1_BPP24 | CR1_BGR;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ cntl |= CR1_BPP24;
+ break;
+ case DRM_FORMAT_BGR565:
+ cntl |= CR1_BPP16_565 | CR1_BGR;
+ break;
+ case DRM_FORMAT_RGB565:
+ cntl |= CR1_BPP16_565;
+ break;
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_XBGR1555:
+ cntl |= CR1_BPP16_555 | CR1_BGR;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_XRGB1555:
+ cntl |= CR1_BPP16_555;
+ break;
+ default:
+ WARN_ONCE(true, "Unknown FB format 0x%08x, set XRGB8888 instead\n",
+ fb->format->format);
+ cntl |= CR1_BPP24;
+ break;
+ }
+
+ writel(priv->fb_addr, priv->regs + DBAR);
+ end = ((priv->fb_addr + fb->height * fb->pitches[0] - 1) & MRR_DEAR_MRR_MASK) | \
+ MRR_OUTSTND_RQ(4);
+
+ if (priv->fb_end < end) {
+ writel(end, priv->regs + MRR);
+ priv->fb_end = end;
+ }
+ writel(cntl, priv->regs + CR1);
+}
+
+static const struct drm_plane_helper_funcs baikal_vdu_primary_plane_helper_funcs = {
+ .atomic_check = baikal_vdu_primary_plane_atomic_check,
+ .atomic_update = baikal_vdu_primary_plane_atomic_update,
+};
+
+static const struct drm_plane_funcs baikal_vdu_primary_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .destroy = drm_plane_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+int baikal_vdu_primary_plane_init(struct drm_device *drm)
+{
+ struct baikal_vdu_private *priv = drm->dev_private;
+ struct drm_plane *plane = &priv->primary;
+ static const u32 formats[] = {
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_XRGB1555,
+ };
+ int ret;
+
+ ret = drm_universal_plane_init(drm, plane, 0,
+ &baikal_vdu_primary_plane_funcs,
+ formats,
+ ARRAY_SIZE(formats),
+ NULL,
+ DRM_PLANE_TYPE_PRIMARY,
+ NULL);
+ if (ret)
+ return ret;
+
+ drm_plane_helper_add(plane, &baikal_vdu_primary_plane_helper_funcs);
+
+ return 0;
+}
+
+
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_regs.h b/drivers/gpu/drm/baikal/baikal_vdu_regs.h
new file mode 100644
index 000000000000..5553fcac5fec
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_regs.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019-2021 Baikal Electronics JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko на baikalelectronics.ru>
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * David A Rusling
+ * Copyright (C) 2001 ARM Limited
+ */
+
+#ifndef __BAIKAL_VDU_REGS_H__
+#define __BAIKAL_VDU_REGS_H__
+
+#define CR1 0x000
+#define HTR 0x008
+#define VTR1 0x00C
+#define VTR2 0x010
+#define PCTR 0x014
+#define ISR 0x018
+#define IMR 0x01C
+#define IVR 0x020
+#define ISCR 0x024
+#define DBAR 0x028
+#define DCAR 0x02C
+#define DEAR 0x030
+#define HVTER 0x044
+#define HPPLOR 0x048
+#define GPIOR 0x1F8
+#define OWER 0x600
+#define OWXSER0 0x604
+#define OWYSER0 0x608
+#define OWDBAR0 0x60C
+#define OWDCAR0 0x610
+#define OWDEAR0 0x614
+#define OWXSER1 0x618
+#define OWYSER1 0x61C
+#define OWDBAR1 0x620
+#define OWDCAR1 0x624
+#define OWDEAR1 0x628
+#define MRR 0xFFC
+
+#define INTR_BAU BIT(7)
+#define INTR_VCT BIT(6)
+#define INTR_MBE BIT(5)
+#define INTR_FER BIT(4)
+
+#define CR1_FBP BIT(19)
+#define CR1_FDW_MASK GENMASK(17, 16)
+#define CR1_FDW_4_WORDS (0 << 16)
+#define CR1_FDW_8_WORDS (1 << 16)
+#define CR1_FDW_16_WORDS (2 << 16)
+#define CR1_OPS_LCD18 (0 << 13)
+#define CR1_OPS_LCD24 (1 << 13)
+#define CR1_OPS_565 (0 << 12)
+#define CR1_OPS_555 (1 << 12)
+#define CR1_VSP BIT(11)
+#define CR1_HSP BIT(10)
+#define CR1_DEP BIT(8)
+#define CR1_BGR BIT(5)
+#define CR1_BPP_MASK GENMASK(4, 2)
+#define CR1_BPP1 (0 << 2)
+#define CR1_BPP2 (1 << 2)
+#define CR1_BPP4 (2 << 2)
+#define CR1_BPP8 (3 << 2)
+#define CR1_BPP16 (4 << 2)
+#define CR1_BPP18 (5 << 2)
+#define CR1_BPP24 (6 << 2)
+#define CR1_LCE BIT(0)
+
+#define CR1_BPP16_555 ((CR1_BPP16) | (CR1_OPS_555))
+#define CR1_BPP16_565 ((CR1_BPP16) | (CR1_OPS_565))
+
+#define VTR1_VBP_MASK GENMASK(23, 16)
+#define VTR1_VBP(x) ((x) << 16)
+#define VTR1_VBP_LSB_WIDTH 8
+#define VTR1_VFP_MASK GENMASK(15, 8)
+#define VTR1_VFP(x) ((x) << 8)
+#define VTR1_VFP_LSB_WIDTH 8
+#define VTR1_VSW_MASK GENMASK(7, 0)
+#define VTR1_VSW(x) ((x) << 0)
+#define VTR1_VSW_LSB_WIDTH 8
+
+#define VTR2_LPP_MASK GENMASK(11, 0)
+
+#define HTR_HSW_MASK GENMASK(31, 24)
+#define HTR_HSW(x) ((x) << 24)
+#define HTR_HSW_LSB_WIDTH 8
+#define HTR_HBP_MASK GENMASK(23, 16)
+#define HTR_HBP(x) ((x) << 16)
+#define HTR_HBP_LSB_WIDTH 8
+#define HTR_PPL_MASK GENMASK(15, 8)
+#define HTR_PPL(x) ((x) << 8)
+#define HTR_HFP_MASK GENMASK(7, 0)
+#define HTR_HFP(x) ((x) << 0)
+#define HTR_HFP_LSB_WIDTH 8
+
+#define PCTR_PCI2 BIT(11)
+#define PCTR_PCR BIT(10)
+#define PCTR_PCI BIT(9)
+#define PCTR_PCB BIT(8)
+#define PCTR_PCD_MASK GENMASK(7, 0)
+#define PCTR_MAX_PCD 128
+
+#define ISCR_VSC_OFF 0x0
+#define ISCR_VSC_VSW 0x4
+#define ISCR_VSC_VBP 0x5
+#define ISCR_VSC_VACTIVE 0x6
+#define ISCR_VSC_VFP 0x7
+
+#define HVTER_VSWE_MASK GENMASK(25, 24)
+#define HVTER_VSWE(x) ((x) << 24)
+#define HVTER_HSWE_MASK GENMASK(17, 16)
+#define HVTER_HSWE(x) ((x) << 16)
+#define HVTER_VBPE_MASK GENMASK(13, 12)
+#define HVTER_VBPE(x) ((x) << 12)
+#define HVTER_VFPE_MASK GENMASK(9, 8)
+#define HVTER_VFPE(x) ((x) << 8)
+#define HVTER_HBPE_MASK GENMASK(5, 4)
+#define HVTER_HBPE(x) ((x) << 4)
+#define HVTER_HFPE_MASK GENMASK(1, 0)
+#define HVTER_HFPE(x) ((x) << 0)
+
+#define HPPLOR_HPOE BIT(31)
+#define HPPLOR_HPPLO_MASK GENMASK(11, 0)
+#define HPPLOR_HPPLO(x) ((x) << 0)
+
+#define GPIOR_UHD_MASK GENMASK(23, 16)
+#define GPIOR_UHD_SNGL_PORT (0 << 18)
+#define GPIOR_UHD_DUAL_PORT (1 << 18)
+#define GPIOR_UHD_QUAD_PORT (2 << 18)
+#define GPIOR_UHD_ENB BIT(17)
+
+#define MRR_DEAR_MRR_MASK GENMASK(31, 3)
+#define MRR_OUTSTND_RQ_MASK GENMASK(2, 0)
+#define MRR_OUTSTND_RQ(x) ((x >> 1) << 0)
+
+#endif /* __BAIKAL_VDU_REGS_H__ */
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index b6b83c4e9083..4ca1a26630a2 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -173,6 +173,13 @@ config DRM_LVDS_CODEC
Support for transparent LVDS encoders and decoders that don't
require any configuration.
+config DRM_BAIKAL_HDMI
+ tristate "Baikal-M HDMI transmitter"
+ default y if ARCH_BAIKAL
+ select DRM_DW_HDMI
+ help
+ Choose this if you want to use HDMI on Baikal-M.
+
config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW
tristate "MegaChips stdp4028-ge-b850v3-fw and stdp2690-ge-b850v3-fw"
depends on OF
--
2.33.5
Подробная информация о списке рассылки devel-kernel