From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on sa.local.altlinux.org X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM autolearn=unavailable autolearn_force=no version=3.4.1 X-Yandex-Fwd: 2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex.ru; s=mail; t=1653064189; bh=O4pgGkTAeF6sAbocsejYf85by/j83sV3dI74aCmPeIQ=; h=Reply-To:In-Reply-To:References:Date:Subject:Cc:To:From: Message-Id; b=b/CJogNOB6xCx82ENQUxMtSsiANVr83p+QAf3i2PMP60+nTvVkjUpWlvQ4qkSmXPh M4SJzIHQ5F7eN5rdhsKpBW22i/I0r9Gkq7kWhu1FdRFmq50d4cLppSYqctiLmT0C6E vpZnqNL4xH2ZyiNtRR3/6twdtyhO7X3FVq6czliY= Authentication-Results: sas1-5f803160584d.qloud-c.yandex.net; dkim=pass header.i=@yandex.ru From: asheplyakov@yandex.ru To: devel@lists.altlinux.org Date: Fri, 20 May 2022 20:28:28 +0400 Message-Id: <20220520162849.1554351-15-asheplyakov@yandex.ru> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220520162849.1554351-1-asheplyakov@yandex.ru> References: <20220520162849.1554351-1-asheplyakov@yandex.ru> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Alexey Sheplyakov , Alexey Sheplyakov , Evgeny Sinelnikov , Vitaly Chikunov , Igor Chudov , Pavel Parkhomenko Subject: [devel] [PATCH 14/35] drm: added Baikal-M SoC video display unit driver X-BeenThere: devel@lists.altlinux.org X-Mailman-Version: 2.1.12 Precedence: list Reply-To: ALT Linux Team development discussions List-Id: ALT Linux Team development discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 20 May 2022 16:30:11 -0000 Archived-At: List-Archive: List-Post: From: Alexey Sheplyakov 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 Signed-off-by: Alexey Sheplyakov Signed-off-by: Pavel Parkhomenko 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 | 209 ++++++++++ drivers/gpu/drm/baikal/baikal_vdu_regs.h | 139 +++++++ drivers/gpu/drm/bridge/Kconfig | 7 + 13 files changed, 1480 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 b1f22e457fd0..1d6cccc6f1fe 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -230,6 +230,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 301a44dc18e3..2d6eda7c79d2 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -135,3 +135,4 @@ obj-y += xlnx/ obj-y += gud/ obj-$(CONFIG_DRM_HYPERV) += hyperv/ 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..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..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 + * + * Parts of this file were based on sources as follows: + * + * Copyright (C) 2016 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + +#include +#include +#include + +#include + +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 "); +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 + * + * Parts of this file were based on sources as follows: + * + * Copyright (c) 2006-2008 Intel Corporation + * Copyright (c) 2007 Dave Airlie + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#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 + * + * Parts of this file were based on sources as follows: + * + * Copyright (c) 2006-2008 Intel Corporation + * Copyright (c) 2007 Dave Airlie + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include + +#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 + * + * Parts of this file were based on sources as follows: + * + * Copyright (c) 2006-2008 Intel Corporation + * Copyright (c) 2007 Dave Airlie + * 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 +#include + +#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..cedf7962daa6 --- /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 + * All bugs by Alexey Sheplyakov + * + * 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 + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_CMA_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_CMA_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 "); +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..4a3e971793d2 --- /dev/null +++ b/drivers/gpu/drm/baikal/baikal_vdu_plane.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2019-2020 Baikal Electronics JSC + * + * Author: Pavel Parkhomenko + * + * Parts of this file were based on sources as follows: + * + * Copyright (c) 2006-2008 Intel Corporation + * Copyright (c) 2007 Dave Airlie + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_cma_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 + * + * 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 3795e7020aec..000174421afc 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -135,6 +135,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.32.0