kernel-5.15/0609-Baikal-M-video-unit-driver.patch
Mikhail Novosyolov 839b6a86b6 Add support of Baikal-M SoCs
Information about config values was taken from:

From 804820df7bcb3d53a33ecd074b1eac277e938f24 Mon Sep 17 00:00:00 2001
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
Date: Thu, 4 Feb 2021 19:35:14 +0400
Subject: [PATCH] config-aarch64: adjusted for Baikal-M (MBM1.0 board)

* DW_APB_TIMER=y, DW_APB_TIMER_OF=y: SoC clocks

* SERIAL_8250_DW=y: serial console

* I2C_DESIGNWARE_CORE=y, I2C_DESIGNWARE_PLATFORM=y: BMC (board
  management controller) and RTC (Real Time Clock) are connected
  via I2C.

* GPIO_DWAPB=y: device (PCIe, PHY, etc) reset/configuration

* RTC_DRV_PCF2127=y: RTC compiled in so the kernel automatically
  sets the system time from the hardware clock

* TP_BMC=y: amongst other things handles the power button

* DRM_BAIKAL_VDU=m, DRM_BAIKAL_HDMI=m: video unit and HDMI transmitter

* CMA_SIZE_MBYTES=256: video display unit and GPU use system RAM, hence
  CMA should reserve enough (contiguous) memory.
  Note: CMA reserves memory during very early init, hence the size
  has to be hard-coded into CONFIG

* MALI_MIDGARD=m: GPU driver, kernel side of proprietary mali blob.
  Note: kernel mode code is GPLv2, so it's fine to distribute it.

* SENSORS_BT1_PVT=m: hardware temperature/voltage sensors

* PCI_BAIKAL=m: PCIe root complex. Compiled as a module since takes
  ages (60 seconds or so) to probe the hardware. If compiled in
  substantially increases the boot time, and machine is completely
  unresponsive during probing PCIe. When built as a module probing
  executes concurrently with other boot activities (unless booting
  from a PCIe device)

* STMMAC_ETH=m, STMMAC_PLATFORM=m, DWMAC_BAIKAL=m: Ethernet driver
2021-06-22 16:35:50 +03:00

1722 lines
50 KiB
Diff

From 6bebe9c130b3d2d4e55ac421552b30f945013b76 Mon Sep 17 00:00:00 2001
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
Date: Thu, 30 Apr 2020 15:46:43 +0400
Subject: [PATCH 609/625] Baikal-M: video unit driver
Based on code from SDK-M-4.3 with the following improvements:
* Fixed autoloading when compiled as a module
* Avoid concurrent calls to BAIKAL_SMC_VDU_UPDATE_HDMI
* Added missing drm_crtc_vblank_{on,off}
* Moved enable/disable_vblank to crtc_funcs, so atomic_flip users
(Wayland compistors) work as expected
* Removed empty atomic_check
* Use `drm_fb_cma_get_gem_addr` instead of reimplementing it
---
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/baikal/Kconfig | 15 +
drivers/gpu/drm/baikal/Makefile | 12 +
drivers/gpu/drm/baikal/baikal-hdmi.c | 170 ++++++++++
drivers/gpu/drm/baikal/baikal_vdu_connector.c | 98 ++++++
drivers/gpu/drm/baikal/baikal_vdu_crtc.c | 310 ++++++++++++++++++
drivers/gpu/drm/baikal/baikal_vdu_debugfs.c | 87 +++++
drivers/gpu/drm/baikal/baikal_vdu_drm.h | 95 ++++++
drivers/gpu/drm/baikal/baikal_vdu_drv.c | 310 ++++++++++++++++++
drivers/gpu/drm/baikal/baikal_vdu_encoder.c | 51 +++
drivers/gpu/drm/baikal/baikal_vdu_gem.c | 37 +++
drivers/gpu/drm/baikal/baikal_vdu_plane.c | 233 +++++++++++++
drivers/gpu/drm/baikal/baikal_vdu_regs.h | 146 +++++++++
drivers/gpu/drm/bridge/Kconfig | 7 +
15 files changed, 1573 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_encoder.c
create mode 100644 drivers/gpu/drm/baikal/baikal_vdu_gem.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 ca868271f4c4..06a855255108 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -235,6 +235,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 81569009f884..063e40f93834 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -124,3 +124,4 @@ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
obj-$(CONFIG_DRM_MCDE) += mcde/
obj-$(CONFIG_DRM_TIDSS) += tidss/
obj-y += xlnx/
+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..7f3661ae5578
--- /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_CMA_HELPER
+ select DRM_GEM_CMA_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..4c3e9e67befb
--- /dev/null
+++ b/drivers/gpu/drm/baikal/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+baikal_vdu_drm-y += baikal_vdu_connector.o \
+ baikal_vdu_crtc.o \
+ baikal_vdu_drv.o \
+ baikal_vdu_encoder.o \
+ baikal_vdu_gem.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..541d5d126bfc
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal-hdmi.c
@@ -0,0 +1,170 @@
+/*
+ * Baikal Electronics BE-M1000 DesignWare HDMI 2.0 Tx PHY support driver
+ *
+ * Copyright (C) 2019 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)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <drm/drm_modes.h>
+
+#include <drm/bridge/dw_hdmi.h>
+
+#define BAIKAL_HDMI_PHY_OPMODE_PLLCFG 0x06 /* Mode of operation and PLL dividers */
+#define BAIKAL_HDMI_PHY_PLLCURRCTRL 0x10 /* PLL current */
+#define BAIKAL_HDMI_PHY_PLLGMPCTRL 0x15 /* PLL Gmp (conductance) */
+#define BAIKAL_HDMI_PHY_TXTERM 0x19 /* Rterm */
+#define BAIKAL_HDMI_PHY_VLEVCTRL 0x0e /* Voltage levels */
+#define BAIKAL_HDMI_PHY_CKSYMTXCTRL 0x09 /* Tx symbols control and slope boost */
+
+int fixed_clock = 0;
+int max_clock = 0;
+
+struct baikal_hdmi_phy_params {
+ unsigned long mpixelclock;
+ u16 opmode_div;
+ u16 curr;
+ u16 gmp;
+ u16 txterm;
+ u16 vlevctrl;
+ u16 cksymtxctrl;
+};
+
+static const struct baikal_hdmi_phy_params baikal_hdmi_phy_params[] = {
+ /* PCLK opmode current gmp txter vlevctrl cksymtxctrl */
+ { 44900000, 0x00b3, 0x0000, 0x0000, 0x0004, 0x0232, 0x8009 },
+ { 90000000, 0x0072, 0x0008, 0x0001, 0x0004, 0x0232, 0x8009 },
+ { 148250000, 0x0051, 0x001b, 0x0002, 0x0004, 0x0232, 0x8009 },
+ { 182750000, 0x0051, 0x001b, 0x0002, 0x0004, 0x0230, 0x8009 },
+ { 218250000, 0x0040, 0x0036, 0x0003, 0x0004, 0x0230, 0x8009 },
+ { 288000000, 0x0040, 0x0036, 0x0003, 0x0004, 0x0273, 0x8009 },
+ { 340000000, 0x0040, 0x0036, 0x0003, 0x0004, 0x0273, 0x8029 },
+ { 594000000, 0x1a40, 0x003f, 0x0003, 0x0004, 0x014a, 0x8039 },
+ { ~0UL },
+};
+
+static int baikal_hdmi_phy_configure(struct dw_hdmi *hdmi,
+ void *data,
+ unsigned long mpixelclock)
+{
+ const struct baikal_hdmi_phy_params *params = baikal_hdmi_phy_params;
+
+ for (; params && params->mpixelclock != ~0UL; ++params) {
+ if (mpixelclock <= params->mpixelclock)
+ break;
+ }
+
+ if (params->mpixelclock == ~0UL)
+ return -EINVAL;
+
+ dw_hdmi_phy_i2c_write(hdmi, params->opmode_div,
+ BAIKAL_HDMI_PHY_OPMODE_PLLCFG);
+ dw_hdmi_phy_i2c_write(hdmi, params->curr,
+ BAIKAL_HDMI_PHY_PLLCURRCTRL);
+ dw_hdmi_phy_i2c_write(hdmi, params->gmp,
+ BAIKAL_HDMI_PHY_PLLGMPCTRL);
+ dw_hdmi_phy_i2c_write(hdmi, params->txterm,
+ BAIKAL_HDMI_PHY_TXTERM);
+ dw_hdmi_phy_i2c_write(hdmi, params->vlevctrl,
+ BAIKAL_HDMI_PHY_VLEVCTRL);
+ dw_hdmi_phy_i2c_write(hdmi, params->cksymtxctrl,
+ BAIKAL_HDMI_PHY_CKSYMTXCTRL);
+
+ return 0;
+}
+
+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 const struct dw_hdmi_plat_data baikal_dw_hdmi_plat_data = {
+ .configure_phy = baikal_hdmi_phy_configure,
+ .mode_valid = baikal_hdmi_mode_valid,
+};
+
+static int baikal_dw_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct dw_hdmi *hdmi;
+ struct platform_device *pdev = to_platform_device(dev);
+ hdmi = dw_hdmi_probe(pdev, &baikal_dw_hdmi_plat_data);
+ if (IS_ERR(hdmi)) {
+ return PTR_ERR(hdmi);
+ } else
+ return 0;
+}
+
+static void baikal_dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dw_hdmi *hdmi = platform_get_drvdata(pdev);
+ dw_hdmi_unbind(hdmi);
+}
+
+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..ca48e230f174
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_connector.c
@@ -0,0 +1,98 @@
+/*
+ * 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"
+
+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_drm_connector *vdu_connector =
+ to_baikal_vdu_drm_connector(connector);
+
+ return (vdu_connector->panel ?
+ connector_status_connected :
+ connector_status_disconnected);
+}
+
+static int baikal_vdu_drm_connector_helper_get_modes(
+ struct drm_connector *connector)
+{
+ struct baikal_vdu_drm_connector *vdu_connector =
+ to_baikal_vdu_drm_connector(connector);
+
+ if (!vdu_connector) {
+ pr_err("%s: vdu_connector == NULL\n", __func__);
+ return 0;
+ }
+ if (!vdu_connector->panel)
+ return 0;
+
+ return drm_panel_get_modes(vdu_connector->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,
+ //.dpms = drm_atomic_helper_connector_dpms, // TODO enable it?
+ .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_connector_create(struct drm_device *dev)
+{
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct baikal_vdu_drm_connector *vdu_connector = &priv->connector;
+ struct drm_connector *connector = &vdu_connector->connector;
+
+ drm_connector_init(dev, connector, &connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+ return 0;
+}
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..6ef61791e299
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_crtc.c
@@ -0,0 +1,310 @@
+/*
+ * 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;
+ hsw = mode->hsync_end - mode->hsync_start - 1;
+ hfp = mode->hsync_start - mode->hdisplay;
+ hbp = mode->htotal - mode->hsync_end;
+
+ 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_crtc_state *old_state)
+{
+ struct baikal_vdu_private *priv = crtc->dev->dev_private;
+ u32 cntl;
+
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "enabling pixel clock\n");
+ clk_prepare_enable(priv->clk);
+
+ drm_panel_prepare(priv->connector.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 and 24-bit LCD interface mode */
+ /* Enable and Power Up */
+ cntl = readl(priv->regs + CR1);
+ cntl |= CR1_LCE + CR1_FDW_16_WORDS + CR1_OPS_LCD24;
+ writel(cntl, priv->regs + CR1);
+
+ drm_panel_enable(priv->connector.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->connector.panel);
+
+ /* Disable and Power Down */
+ //writel(0, priv->regs + CR1);
+
+ drm_panel_unprepare(priv->connector.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_crtc_state *old_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;
+
+ //clk_prepare_enable(priv->clk);
+
+ /* 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..d049335dab1d
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_drm.h
@@ -0,0 +1,95 @@
+/*
+ * 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>
+#include <linux/workqueue.h>
+
+struct clk;
+struct drm_device;
+struct drm_fbdev_cma;
+struct drm_panel;
+
+/*struct baikal_vdu_framebuffer {
+ u32 base;
+ u32 size;
+ u32 index;
+ u32 reg_base;
+ u32 reg_size;
+ u32 reg_width;
+ u32 reg_height;
+};*/
+
+struct baikal_vdu_drm_connector {
+ struct drm_connector connector;
+ struct drm_panel *panel;
+};
+
+struct baikal_vdu_private {
+ struct drm_device *drm;
+
+ struct baikal_vdu_drm_connector connector;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
+ struct drm_bridge *bridge;
+ struct drm_plane primary;
+
+ void *regs;
+ struct clk *clk;
+ spinlock_t lock;
+ u32 counters[20];
+ int mode_fixup;
+
+ u32 fb_addr;
+ u32 fb_end;
+
+ struct delayed_work update_work;
+};
+
+#define to_baikal_vdu_drm_connector(x) \
+ container_of(x, struct baikal_vdu_drm_connector, connector)
+
+extern const struct drm_encoder_funcs baikal_vdu_encoder_funcs;
+
+/* 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_connector_create(struct drm_device *dev);
+
+/* Encoder Functions */
+int baikal_vdu_encoder_init(struct drm_device *dev);
+
+/* GEM Functions */
+int baikal_vdu_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+
+void baikal_vdu_debugfs_init(struct drm_minor *minor);
+
+/* Worker functions */
+void baikal_vdu_update_work(struct work_struct *work);
+
+#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..0caa97dcb2e9
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_drv.c
@@ -0,0 +1,310 @@
+/*
+ * 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/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.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_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_irq.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 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;
+
+ 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 = 4096;
+ mode_config->min_height = 1;
+ mode_config->max_height = 4096;
+
+ 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->connector.panel,
+ &priv->bridge);
+ if (ret == -EPROBE_DEFER) {
+ dev_info(dev->dev, "Bridge probe deferred\n");
+ goto out_config;
+ }
+
+ ret = baikal_vdu_encoder_init(dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to create DRM encoder\n");
+ goto out_config;
+ }
+
+ if (priv->bridge) {
+ 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->connector.panel) {
+ ret = baikal_vdu_connector_create(dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to create DRM connector\n");
+ goto out_config;
+ }
+ ret = drm_connector_attach_encoder(&priv->connector.connector,
+ &priv->encoder);
+ if (ret != 0) {
+ dev_err(dev->dev, "Failed to attach encoder\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_fb_helper_remove_conflicting_framebuffers(NULL, "baikal-vdudrmfb", false);
+
+ 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);
+ INIT_DEFERRABLE_WORK(&priv->update_work,
+ baikal_vdu_update_work);
+
+ 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 const struct file_operations drm_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_gem_cma_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+};
+
+static struct drm_driver vdu_drm_driver = {
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
+ DRIVER_MODESET | DRIVER_ATOMIC,
+ .irq_handler = baikal_vdu_irq,
+ .ioctls = NULL,
+ .fops = &drm_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = 1,
+ .minor = 0,
+ .patchlevel = 0,
+ .dumb_create = baikal_vdu_dumb_create,
+ .gem_create_object = drm_gem_cma_create_object_default_funcs,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = baikal_vdu_debugfs_init,
+#endif
+};
+
+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);
+
+ if (!(irq = platform_get_irq(pdev, 0))) {
+ dev_err(dev, "%s no IRQ resource specified\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_init(&priv->lock);
+
+ ret = drm_irq_install(drm, irq);
+ if (ret != 0) {
+ dev_err(dev, "%s IRQ %d allocation failed\n", __func__, irq);
+ return ret;
+ }
+
+ ret = vdu_modeset_init(drm);
+ if (ret != 0) {
+ dev_err(dev, "Failed to init modeset\n");
+ goto dev_unref;
+ }
+
+ return 0;
+
+dev_unref:
+ drm_irq_uninstall(drm);
+ drm->dev_private = NULL;
+ drm_dev_put(drm);
+ return ret;
+}
+
+static int baikal_vdu_drm_remove(struct platform_device *pdev)
+{
+ struct drm_device *drm = platform_get_drvdata(pdev);
+
+ drm_dev_unregister(drm);
+ drm_mode_config_cleanup(drm);
+ drm_irq_uninstall(drm);
+ 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_encoder.c b/drivers/gpu/drm/baikal/baikal_vdu_encoder.c
new file mode 100644
index 000000000000..9081d196dac3
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_encoder.c
@@ -0,0 +1,51 @@
+/*
+ * 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_encoder.c
+ * Implementation of the encoder functions for Baikal Electronics BE-M1000 VDU driver
+ */
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drm_crtc_helper.h>
+
+#include "baikal_vdu_drm.h"
+
+const struct drm_encoder_funcs baikal_vdu_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+int baikal_vdu_encoder_init(struct drm_device *dev)
+{
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct drm_encoder *encoder = &priv->encoder;
+ int ret;
+
+ ret = drm_encoder_init(dev, encoder, &baikal_vdu_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+
+ encoder->crtc = &priv->crtc;
+ encoder->possible_crtcs = BIT(drm_crtc_index(encoder->crtc));
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_gem.c b/drivers/gpu/drm/baikal/baikal_vdu_gem.c
new file mode 100644
index 000000000000..b07566caf12c
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_gem.c
@@ -0,0 +1,37 @@
+/*
+ * 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_gem.c
+ * Implementation of the GEM functions for Baikal Electronics BE-M1000 VDU driver
+ */
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include "baikal_vdu_drm.h"
+
+int baikal_vdu_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+ args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+ return drm_gem_cma_dumb_create_internal(file_priv, dev, args);
+}
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..9817af3c6de8
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_plane.c
@@ -0,0 +1,233 @@
+/*
+ * 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/arm-smccc.h>
+#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_cma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+#define BAIKAL_SMC_VDU_UPDATE_HDMI 0x82000100
+
+void baikal_vdu_update_work(struct work_struct *work)
+{
+ struct arm_smccc_res res;
+ unsigned long flags;
+ struct baikal_vdu_private *priv = container_of(work, struct baikal_vdu_private,
+ update_work.work);
+ int count = 0;
+ u64 t1, t2;
+ t1 = read_sysreg(CNTVCT_EL0);
+ spin_lock_irqsave(&priv->lock, flags);
+ arm_smccc_smc(BAIKAL_SMC_VDU_UPDATE_HDMI, priv->fb_addr, priv->fb_end, 0, 0, 0, 0, 0, &res);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ if (res.a0 == -EBUSY)
+ priv->counters[15]++;
+ else
+ priv->counters[16]++;
+ while (res.a0 == -EBUSY && count < 10) {
+ count++;
+ usleep_range(10000, 20000);
+ res.a0 = 0;
+ spin_lock_irqsave(&priv->lock, flags);
+ arm_smccc_smc(BAIKAL_SMC_VDU_UPDATE_HDMI, priv->fb_addr, priv->fb_end, 0, 0, 0, 0, 0, &res);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ if (res.a0 == -EBUSY)
+ priv->counters[15]++;
+ else
+ priv->counters[16]++;
+ }
+ t2 = read_sysreg(CNTVCT_EL0);
+ priv->counters[17] = t2 - t1;
+ priv->counters[18] = count;
+ priv->counters[19]++;
+}
+
+static int baikal_vdu_primary_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct drm_device *dev = plane->dev;
+ struct baikal_vdu_private *priv = dev->dev_private;
+ struct drm_crtc_state *crtc_state;
+ struct drm_display_mode *mode;
+ int rate, ret;
+
+ if (!state->crtc)
+ return 0;
+
+ crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+ mode = &crtc_state->adjusted_mode;
+ rate = mode->clock * 1000;
+ if (rate == clk_get_rate(priv->clk))
+ return 0;
+
+ 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);
+ return -EINVAL;
+ }
+ clk_prepare_enable(priv->clk);
+ if (!__clk_is_enabled(priv->clk)) {
+ DRM_ERROR("PLL could not lock at desired frequency (%d Hz)\n",
+ rate);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void baikal_vdu_primary_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_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;
+ struct arm_smccc_res res;
+ u32 cntl, addr, end;
+ unsigned long flags;
+
+ if (!fb)
+ return;
+
+ addr = drm_fb_cma_get_gem_addr(fb, state, 0);
+ end = ((addr + fb->height * fb->pitches[0] - 1) & MRR_DEAR_MRR_MASK) | MRR_OUTSTND_RQ(4);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ arm_smccc_smc(BAIKAL_SMC_VDU_UPDATE_HDMI, addr, end, 0, 0, 0, 0, 0, &res);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (res.a0 == -EBUSY) {
+ priv->counters[15]++;
+ priv->fb_addr = addr;
+ priv->fb_end = end;
+ smp_wmb();
+ schedule_delayed_work(&priv->update_work, usecs_to_jiffies(250));
+ } else
+ priv->counters[16]++;
+
+ 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(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..a0d8e69eb5e6
--- /dev/null
+++ b/drivers/gpu/drm/baikal/baikal_vdu_regs.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019-2020 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
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#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_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_FMT_LDI (0 << 20)
+#define GPIOR_UHD_FMT_VESA (1 << 20)
+#define GPIOR_UHD_FMT_JEIDA (2 << 20)
+#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 GPIOR_UHD_PIX_INTLV (0 << 16)
+#define GPIOR_UHD_PIX_SQNTL (1 << 16)
+
+#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 e145cbb35bac..c9050ac41215 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -71,6 +71,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.31.1