drm: Place Renesas drivers in a separate dir
authorBiju Das <biju.das.jz@bp.renesas.com>
Tue, 2 May 2023 10:09:08 +0000 (11:09 +0100)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Mon, 29 May 2023 13:41:03 +0000 (16:41 +0300)
Create vendor specific renesas directory and move renesas drivers
to that directory.

Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
Acked-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
91 files changed:
MAINTAINERS
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/rcar-du/Kconfig [deleted file]
drivers/gpu/drm/rcar-du/Makefile [deleted file]
drivers/gpu/drm/rcar-du/rcar_cmm.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_cmm.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_crtc.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_crtc.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_drv.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_drv.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_encoder.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_encoder.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_group.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_group.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_kms.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_kms.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_plane.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_plane.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_regs.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_vsp.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_vsp.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_writeback.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_du_writeback.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_lvds.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_lvds.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_lvds_regs.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c [deleted file]
drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h [deleted file]
drivers/gpu/drm/rcar-du/rcar_mipi_dsi_regs.h [deleted file]
drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi.c [deleted file]
drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi_regs.h [deleted file]
drivers/gpu/drm/renesas/Kconfig [new file with mode: 0644]
drivers/gpu/drm/renesas/Makefile [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/Kconfig [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/Makefile [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_cmm.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_group.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_regs.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_lvds.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_lvds_regs.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c [new file with mode: 0644]
drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/Kconfig [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/Makefile [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.c [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.h [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.h [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.h [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.c [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.h [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.h [new file with mode: 0644]
drivers/gpu/drm/renesas/shmobile/shmob_drm_regs.h [new file with mode: 0644]
drivers/gpu/drm/shmobile/Kconfig [deleted file]
drivers/gpu/drm/shmobile/Makefile [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_backlight.c [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_backlight.h [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_crtc.c [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_crtc.h [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_drv.c [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_drv.h [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_kms.c [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_kms.h [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_plane.c [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_plane.h [deleted file]
drivers/gpu/drm/shmobile/shmob_drm_regs.h [deleted file]

index b344e1318ac3b84085ef47f228ac473d558abb23..38dc099ad8ff427e3cc11b67e7ca0d661ed8ab70 100644 (file)
@@ -6981,8 +6981,7 @@ F:        Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
 F:     Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.yaml
 F:     Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
 F:     Documentation/devicetree/bindings/display/renesas,du.yaml
-F:     drivers/gpu/drm/rcar-du/
-F:     drivers/gpu/drm/shmobile/
+F:     drivers/gpu/drm/renesas/
 F:     include/linux/platform_data/shmob_drm.h
 
 DRM DRIVERS FOR ROCKCHIP
index ba3fb04bb6913de93af261f6ef9698d96bf4cc5d..41aa8b07252b59606111974eb98ec509555952c2 100644 (file)
@@ -295,9 +295,7 @@ source "drivers/gpu/drm/armada/Kconfig"
 
 source "drivers/gpu/drm/atmel-hlcdc/Kconfig"
 
-source "drivers/gpu/drm/rcar-du/Kconfig"
-
-source "drivers/gpu/drm/shmobile/Kconfig"
+source "drivers/gpu/drm/renesas/Kconfig"
 
 source "drivers/gpu/drm/sun4i/Kconfig"
 
index a33257d2bc7fd52f9822474917e0957cfe23dd71..982d9e06168a43c566c7c0ce649251f61526cc19 100644 (file)
@@ -156,8 +156,7 @@ obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
 obj-$(CONFIG_DRM_ARMADA) += armada/
 obj-$(CONFIG_DRM_ATMEL_HLCDC)  += atmel-hlcdc/
-obj-y                  += rcar-du/
-obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
+obj-y                  += renesas/
 obj-y                  += omapdrm/
 obj-$(CONFIG_DRM_SUN4I) += sun4i/
 obj-y                  += tilcdc/
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
deleted file mode 100644 (file)
index 53c356a..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-config DRM_RCAR_DU
-       tristate "DRM Support for R-Car Display Unit"
-       depends on DRM && OF
-       depends on ARM || ARM64
-       depends on ARCH_RENESAS || COMPILE_TEST
-       select DRM_KMS_HELPER
-       select DRM_GEM_DMA_HELPER
-       select VIDEOMODE_HELPERS
-       help
-         Choose this option if you have an R-Car chipset.
-         If M is selected the module will be called rcar-du-drm.
-
-config DRM_RCAR_USE_CMM
-       bool "R-Car DU Color Management Module (CMM) Support"
-       depends on DRM_RCAR_DU
-       default DRM_RCAR_DU
-       help
-         Enable support for R-Car Color Management Module (CMM).
-
-config DRM_RCAR_CMM
-       def_tristate DRM_RCAR_DU
-       depends on DRM_RCAR_USE_CMM
-
-config DRM_RCAR_DW_HDMI
-       tristate "R-Car Gen3 and RZ/G2 DU HDMI Encoder Support"
-       depends on DRM && OF
-       depends on DRM_RCAR_DU || COMPILE_TEST
-       select DRM_DW_HDMI
-       help
-         Enable support for R-Car Gen3 or RZ/G2 internal HDMI encoder.
-
-config DRM_RCAR_USE_LVDS
-       bool "R-Car DU LVDS Encoder Support"
-       depends on DRM_BRIDGE && OF
-       depends on DRM_RCAR_DU || COMPILE_TEST
-       default DRM_RCAR_DU
-       help
-         Enable support for the R-Car Display Unit embedded LVDS encoders.
-
-config DRM_RCAR_LVDS
-       def_tristate DRM_RCAR_DU
-       depends on DRM_RCAR_USE_LVDS
-       depends on PM
-       select DRM_KMS_HELPER
-       select DRM_PANEL
-       select RESET_CONTROLLER
-
-config DRM_RCAR_USE_MIPI_DSI
-       bool "R-Car DU MIPI DSI Encoder Support"
-       depends on DRM_BRIDGE && OF
-       depends on DRM_RCAR_DU || COMPILE_TEST
-       default DRM_RCAR_DU
-       help
-         Enable support for the R-Car Display Unit embedded MIPI DSI encoders.
-
-config DRM_RCAR_MIPI_DSI
-       def_tristate DRM_RCAR_DU
-       depends on DRM_RCAR_USE_MIPI_DSI
-       select DRM_MIPI_DSI
-       select RESET_CONTROLLER
-
-config DRM_RZG2L_MIPI_DSI
-       tristate "RZ/G2L MIPI DSI Encoder Support"
-       depends on DRM && DRM_BRIDGE && OF
-       depends on ARCH_RENESAS || COMPILE_TEST
-       select DRM_MIPI_DSI
-       help
-         Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders.
-
-config DRM_RCAR_VSP
-       bool "R-Car DU VSP Compositor Support" if ARM
-       default y if ARM64
-       depends on DRM_RCAR_DU
-       depends on VIDEO_RENESAS_VSP1=y || (VIDEO_RENESAS_VSP1 && DRM_RCAR_DU=m)
-       help
-         Enable support to expose the R-Car VSP Compositor as KMS planes.
-
-config DRM_RCAR_WRITEBACK
-       bool
-       default y if ARM64
-       depends on DRM_RCAR_DU
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
deleted file mode 100644 (file)
index b8f2c82..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-rcar-du-drm-y := rcar_du_crtc.o \
-                rcar_du_drv.o \
-                rcar_du_encoder.o \
-                rcar_du_group.o \
-                rcar_du_kms.o \
-                rcar_du_plane.o \
-
-rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)     += rcar_du_vsp.o
-rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
-
-obj-$(CONFIG_DRM_RCAR_CMM)             += rcar_cmm.o
-obj-$(CONFIG_DRM_RCAR_DU)              += rcar-du-drm.o
-obj-$(CONFIG_DRM_RCAR_DW_HDMI)         += rcar_dw_hdmi.o
-obj-$(CONFIG_DRM_RCAR_LVDS)            += rcar_lvds.o
-obj-$(CONFIG_DRM_RCAR_MIPI_DSI)                += rcar_mipi_dsi.o
-
-obj-$(CONFIG_DRM_RZG2L_MIPI_DSI)       += rzg2l_mipi_dsi.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.c b/drivers/gpu/drm/rcar-du/rcar_cmm.c
deleted file mode 100644 (file)
index e2a67dd..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit Color Management Module
- *
- * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org>
- */
-
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-
-#include <drm/drm_color_mgmt.h>
-
-#include "rcar_cmm.h"
-
-#define CM2_LUT_CTRL           0x0000
-#define CM2_LUT_CTRL_LUT_EN    BIT(0)
-#define CM2_LUT_TBL_BASE       0x0600
-#define CM2_LUT_TBL(__i)       (CM2_LUT_TBL_BASE + (__i) * 4)
-
-struct rcar_cmm {
-       void __iomem *base;
-
-       /*
-        * @lut:                1D-LUT state
-        * @lut.enabled:        1D-LUT enabled flag
-        */
-       struct {
-               bool enabled;
-       } lut;
-};
-
-static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
-{
-       return ioread32(rcmm->base + reg);
-}
-
-static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
-{
-       iowrite32(data, rcmm->base + reg);
-}
-
-/*
- * rcar_cmm_lut_write() - Scale the DRM LUT table entries to hardware precision
- *                       and write to the CMM registers
- * @rcmm: Pointer to the CMM device
- * @drm_lut: Pointer to the DRM LUT table
- */
-static void rcar_cmm_lut_write(struct rcar_cmm *rcmm,
-                              const struct drm_color_lut *drm_lut)
-{
-       unsigned int i;
-
-       for (i = 0; i < CM2_LUT_SIZE; ++i) {
-               u32 entry = drm_color_lut_extract(drm_lut[i].red, 8) << 16
-                         | drm_color_lut_extract(drm_lut[i].green, 8) << 8
-                         | drm_color_lut_extract(drm_lut[i].blue, 8);
-
-               rcar_cmm_write(rcmm, CM2_LUT_TBL(i), entry);
-       }
-}
-
-/*
- * rcar_cmm_setup() - Configure the CMM unit
- * @pdev: The platform device associated with the CMM instance
- * @config: The CMM unit configuration
- *
- * Configure the CMM unit with the given configuration. Currently enabling,
- * disabling and programming of the 1-D LUT unit is supported.
- *
- * As rcar_cmm_setup() accesses the CMM registers the unit should be powered
- * and its functional clock enabled. To guarantee this, before any call to
- * this function is made, the CMM unit has to be enabled by calling
- * rcar_cmm_enable() first.
- *
- * TODO: Add support for LUT double buffer operations to avoid updating the
- * LUT table entries while a frame is being displayed.
- */
-int rcar_cmm_setup(struct platform_device *pdev,
-                  const struct rcar_cmm_config *config)
-{
-       struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
-
-       /* Disable LUT if no table is provided. */
-       if (!config->lut.table) {
-               if (rcmm->lut.enabled) {
-                       rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
-                       rcmm->lut.enabled = false;
-               }
-
-               return 0;
-       }
-
-       /* Enable LUT and program the new gamma table values. */
-       if (!rcmm->lut.enabled) {
-               rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
-               rcmm->lut.enabled = true;
-       }
-
-       rcar_cmm_lut_write(rcmm, config->lut.table);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rcar_cmm_setup);
-
-/*
- * rcar_cmm_enable() - Enable the CMM unit
- * @pdev: The platform device associated with the CMM instance
- *
- * When the output of the corresponding DU channel is routed to the CMM unit,
- * the unit shall be enabled before the DU channel is started, and remain
- * enabled until the channel is stopped. The CMM unit shall be disabled with
- * rcar_cmm_disable().
- *
- * Calls to rcar_cmm_enable() and rcar_cmm_disable() are not reference-counted.
- * It is an error to attempt to enable an already enabled CMM unit, or to
- * attempt to disable a disabled unit.
- */
-int rcar_cmm_enable(struct platform_device *pdev)
-{
-       int ret;
-
-       ret = pm_runtime_resume_and_get(&pdev->dev);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rcar_cmm_enable);
-
-/*
- * rcar_cmm_disable() - Disable the CMM unit
- * @pdev: The platform device associated with the CMM instance
- *
- * See rcar_cmm_enable() for usage information.
- *
- * Disabling the CMM unit disable all the internal processing blocks. The CMM
- * state shall thus be restored with rcar_cmm_setup() when re-enabling the CMM
- * unit after the next rcar_cmm_enable() call.
- */
-void rcar_cmm_disable(struct platform_device *pdev)
-{
-       struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
-
-       rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
-       rcmm->lut.enabled = false;
-
-       pm_runtime_put(&pdev->dev);
-}
-EXPORT_SYMBOL_GPL(rcar_cmm_disable);
-
-/*
- * rcar_cmm_init() - Initialize the CMM unit
- * @pdev: The platform device associated with the CMM instance
- *
- * Return: 0 on success, -EPROBE_DEFER if the CMM is not available yet,
- *         -ENODEV if the DRM_RCAR_CMM config option is disabled
- */
-int rcar_cmm_init(struct platform_device *pdev)
-{
-       struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
-
-       if (!rcmm)
-               return -EPROBE_DEFER;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rcar_cmm_init);
-
-static int rcar_cmm_probe(struct platform_device *pdev)
-{
-       struct rcar_cmm *rcmm;
-
-       rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
-       if (!rcmm)
-               return -ENOMEM;
-       platform_set_drvdata(pdev, rcmm);
-
-       rcmm->base = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(rcmm->base))
-               return PTR_ERR(rcmm->base);
-
-       pm_runtime_enable(&pdev->dev);
-
-       return 0;
-}
-
-static int rcar_cmm_remove(struct platform_device *pdev)
-{
-       pm_runtime_disable(&pdev->dev);
-
-       return 0;
-}
-
-static const struct of_device_id rcar_cmm_of_table[] = {
-       { .compatible = "renesas,rcar-gen3-cmm", },
-       { .compatible = "renesas,rcar-gen2-cmm", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
-
-static struct platform_driver rcar_cmm_platform_driver = {
-       .probe          = rcar_cmm_probe,
-       .remove         = rcar_cmm_remove,
-       .driver         = {
-               .name   = "rcar-cmm",
-               .of_match_table = rcar_cmm_of_table,
-       },
-};
-
-module_platform_driver(rcar_cmm_platform_driver);
-
-MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
-MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rcar-du/rcar_cmm.h b/drivers/gpu/drm/rcar-du/rcar_cmm.h
deleted file mode 100644 (file)
index 628072a..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit Color Management Module
- *
- * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org>
- */
-
-#ifndef __RCAR_CMM_H__
-#define __RCAR_CMM_H__
-
-#define CM2_LUT_SIZE           256
-
-struct drm_color_lut;
-struct platform_device;
-
-/**
- * struct rcar_cmm_config - CMM configuration
- *
- * @lut:       1D-LUT configuration
- * @lut.table: 1D-LUT table entries. Disable LUT operations when NULL
- */
-struct rcar_cmm_config {
-       struct {
-               struct drm_color_lut *table;
-       } lut;
-};
-
-#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
-int rcar_cmm_init(struct platform_device *pdev);
-
-int rcar_cmm_enable(struct platform_device *pdev);
-void rcar_cmm_disable(struct platform_device *pdev);
-
-int rcar_cmm_setup(struct platform_device *pdev,
-                  const struct rcar_cmm_config *config);
-#else
-static inline int rcar_cmm_init(struct platform_device *pdev)
-{
-       return -ENODEV;
-}
-
-static inline int rcar_cmm_enable(struct platform_device *pdev)
-{
-       return 0;
-}
-
-static inline void rcar_cmm_disable(struct platform_device *pdev)
-{
-}
-
-static inline int rcar_cmm_setup(struct platform_device *pdev,
-                                const struct rcar_cmm_config *config)
-{
-       return 0;
-}
-#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
-
-#endif /* __RCAR_CMM_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
deleted file mode 100644 (file)
index 7e175db..0000000
+++ /dev/null
@@ -1,1338 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit CRTCs
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/platform_device.h>
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_device.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_vblank.h>
-
-#include "rcar_cmm.h"
-#include "rcar_du_crtc.h"
-#include "rcar_du_drv.h"
-#include "rcar_du_encoder.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_plane.h"
-#include "rcar_du_regs.h"
-#include "rcar_du_vsp.h"
-#include "rcar_lvds.h"
-#include "rcar_mipi_dsi.h"
-
-static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
-}
-
-static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
-}
-
-static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
-                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
-}
-
-static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
-                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
-}
-
-void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       rcrtc->dsysr = (rcrtc->dsysr & ~clr) | set;
-       rcar_du_write(rcdu, rcrtc->mmio_offset + DSYSR, rcrtc->dsysr);
-}
-
-/* -----------------------------------------------------------------------------
- * Hardware Setup
- */
-
-struct dpll_info {
-       unsigned int output;
-       unsigned int fdpll;
-       unsigned int n;
-       unsigned int m;
-};
-
-static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc,
-                                struct dpll_info *dpll,
-                                unsigned long input,
-                                unsigned long target)
-{
-       unsigned long best_diff = (unsigned long)-1;
-       unsigned long diff;
-       unsigned int fdpll;
-       unsigned int m;
-       unsigned int n;
-
-       /*
-        *   fin                                 fvco        fout       fclkout
-        * in --> [1/M] --> |PD| -> [LPF] -> [VCO] -> [1/P] -+-> [1/FDPLL] -> out
-        *              +-> |  |                             |
-        *              |                                    |
-        *              +---------------- [1/N] <------------+
-        *
-        *      fclkout = fvco / P / FDPLL -- (1)
-        *
-        * fin/M = fvco/P/N
-        *
-        *      fvco = fin * P *  N / M -- (2)
-        *
-        * (1) + (2) indicates
-        *
-        *      fclkout = fin * N / M / FDPLL
-        *
-        * NOTES
-        *      N       : (n + 1)
-        *      M       : (m + 1)
-        *      FDPLL   : (fdpll + 1)
-        *      P       : 2
-        *      2kHz < fvco < 4096MHz
-        *
-        * To minimize the jitter,
-        * N : as large as possible
-        * M : as small as possible
-        */
-       for (m = 0; m < 4; m++) {
-               for (n = 119; n > 38; n--) {
-                       /*
-                        * This code only runs on 64-bit architectures, the
-                        * unsigned long type can thus be used for 64-bit
-                        * computation. It will still compile without any
-                        * warning on 32-bit architectures.
-                        *
-                        * To optimize calculations, use fout instead of fvco
-                        * to verify the VCO frequency constraint.
-                        */
-                       unsigned long fout = input * (n + 1) / (m + 1);
-
-                       if (fout < 1000 || fout > 2048 * 1000 * 1000U)
-                               continue;
-
-                       for (fdpll = 1; fdpll < 32; fdpll++) {
-                               unsigned long output;
-
-                               output = fout / (fdpll + 1);
-                               if (output >= 400 * 1000 * 1000)
-                                       continue;
-
-                               diff = abs((long)output - (long)target);
-                               if (best_diff > diff) {
-                                       best_diff = diff;
-                                       dpll->n = n;
-                                       dpll->m = m;
-                                       dpll->fdpll = fdpll;
-                                       dpll->output = output;
-                               }
-
-                               if (diff == 0)
-                                       goto done;
-                       }
-               }
-       }
-
-done:
-       dev_dbg(rcrtc->dev->dev,
-               "output:%u, fdpll:%u, n:%u, m:%u, diff:%lu\n",
-                dpll->output, dpll->fdpll, dpll->n, dpll->m, best_diff);
-}
-
-struct du_clk_params {
-       struct clk *clk;
-       unsigned long rate;
-       unsigned long diff;
-       u32 escr;
-};
-
-static void rcar_du_escr_divider(struct clk *clk, unsigned long target,
-                                u32 escr, struct du_clk_params *params)
-{
-       unsigned long rate;
-       unsigned long diff;
-       u32 div;
-
-       /*
-        * If the target rate has already been achieved perfectly we can't do
-        * better.
-        */
-       if (params->diff == 0)
-               return;
-
-       /*
-        * Compute the input clock rate and internal divisor values to obtain
-        * the clock rate closest to the target frequency.
-        */
-       rate = clk_round_rate(clk, target);
-       div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1;
-       diff = abs(rate / (div + 1) - target);
-
-       /*
-        * Store the parameters if the resulting frequency is better than any
-        * previously calculated value.
-        */
-       if (diff < params->diff) {
-               params->clk = clk;
-               params->rate = rate;
-               params->diff = diff;
-               params->escr = escr | div;
-       }
-}
-
-static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
-{
-       const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
-       struct rcar_du_device *rcdu = rcrtc->dev;
-       unsigned long mode_clock = mode->clock * 1000;
-       unsigned int hdse_offset;
-       u32 dsmr;
-       u32 escr;
-
-       if (rcdu->info->dpll_mask & (1 << rcrtc->index)) {
-               unsigned long target = mode_clock;
-               struct dpll_info dpll = { 0 };
-               unsigned long extclk;
-               u32 dpllcr;
-               u32 div = 0;
-
-               /*
-                * DU channels that have a display PLL can't use the internal
-                * system clock, and have no internal clock divider.
-                */
-               extclk = clk_get_rate(rcrtc->extclock);
-               rcar_du_dpll_divider(rcrtc, &dpll, extclk, target);
-
-               dpllcr = DPLLCR_CODE | DPLLCR_CLKE
-                      | DPLLCR_FDPLL(dpll.fdpll)
-                      | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m)
-                      | DPLLCR_STBY;
-
-               if (rcrtc->index == 1)
-                       dpllcr |= DPLLCR_PLCS1
-                              |  DPLLCR_INCS_DOTCLKIN1;
-               else
-                       dpllcr |= DPLLCR_PLCS0
-                              |  DPLLCR_INCS_DOTCLKIN0;
-
-               rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr);
-
-               escr = ESCR_DCLKSEL_DCLKIN | div;
-       } else if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) ||
-                  rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) {
-               /*
-                * Use the external LVDS or DSI PLL output as the dot clock when
-                * outputting to the LVDS or DSI encoder on an SoC that supports
-                * this clock routing option. We use the clock directly in that
-                * case, without any additional divider.
-                */
-               escr = ESCR_DCLKSEL_DCLKIN;
-       } else {
-               struct du_clk_params params = { .diff = (unsigned long)-1 };
-
-               rcar_du_escr_divider(rcrtc->clock, mode_clock,
-                                    ESCR_DCLKSEL_CLKS, &params);
-               if (rcrtc->extclock)
-                       rcar_du_escr_divider(rcrtc->extclock, mode_clock,
-                                            ESCR_DCLKSEL_DCLKIN, &params);
-
-               dev_dbg(rcrtc->dev->dev, "mode clock %lu %s rate %lu\n",
-                       mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext",
-                       params.rate);
-
-               clk_set_rate(params.clk, params.rate);
-               escr = params.escr;
-       }
-
-       /*
-        * The ESCR register only exists in DU channels that can output to an
-        * LVDS or DPAT, and the OTAR register in DU channels that can output
-        * to a DPAD.
-        */
-       if ((rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs |
-            rcdu->info->routes[RCAR_DU_OUTPUT_DPAD1].possible_crtcs |
-            rcdu->info->routes[RCAR_DU_OUTPUT_LVDS0].possible_crtcs |
-            rcdu->info->routes[RCAR_DU_OUTPUT_LVDS1].possible_crtcs) &
-           BIT(rcrtc->index)) {
-               dev_dbg(rcrtc->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr);
-
-               rcar_du_crtc_write(rcrtc, rcrtc->index % 2 ? ESCR13 : ESCR02, escr);
-       }
-
-       if ((rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs |
-            rcdu->info->routes[RCAR_DU_OUTPUT_DPAD1].possible_crtcs) &
-           BIT(rcrtc->index))
-               rcar_du_crtc_write(rcrtc, rcrtc->index % 2 ? OTAR13 : OTAR02, 0);
-
-       /* Signal polarities */
-       dsmr = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
-            | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
-            | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0)
-            | DSMR_DIPM_DISP | DSMR_CSPM;
-       rcar_du_crtc_write(rcrtc, DSMR, dsmr);
-
-       /*
-        * When the CMM is enabled, an additional offset of 25 pixels must be
-        * subtracted from the HDS (horizontal display start) and HDE
-        * (horizontal display end) registers.
-        */
-       hdse_offset = 19;
-       if (rcrtc->group->cmms_mask & BIT(rcrtc->index % 2))
-               hdse_offset += 25;
-
-       /* Display timings */
-       rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start -
-                                       hdse_offset);
-       rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
-                                       mode->hdisplay - hdse_offset);
-       rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
-                                       mode->hsync_start - 1);
-       rcar_du_crtc_write(rcrtc, HCR,  mode->htotal - 1);
-
-       rcar_du_crtc_write(rcrtc, VDSR, mode->crtc_vtotal -
-                                       mode->crtc_vsync_end - 2);
-       rcar_du_crtc_write(rcrtc, VDER, mode->crtc_vtotal -
-                                       mode->crtc_vsync_end +
-                                       mode->crtc_vdisplay - 2);
-       rcar_du_crtc_write(rcrtc, VSPR, mode->crtc_vtotal -
-                                       mode->crtc_vsync_end +
-                                       mode->crtc_vsync_start - 1);
-       rcar_du_crtc_write(rcrtc, VCR,  mode->crtc_vtotal - 1);
-
-       rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start - 1);
-       rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
-}
-
-static unsigned int plane_zpos(struct rcar_du_plane *plane)
-{
-       return plane->plane.state->normalized_zpos;
-}
-
-static const struct rcar_du_format_info *
-plane_format(struct rcar_du_plane *plane)
-{
-       return to_rcar_plane_state(plane->plane.state)->format;
-}
-
-static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
-{
-       struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
-       struct rcar_du_device *rcdu = rcrtc->dev;
-       unsigned int num_planes = 0;
-       unsigned int dptsr_planes;
-       unsigned int hwplanes = 0;
-       unsigned int prio = 0;
-       unsigned int i;
-       u32 dspr = 0;
-
-       for (i = 0; i < rcrtc->group->num_planes; ++i) {
-               struct rcar_du_plane *plane = &rcrtc->group->planes[i];
-               unsigned int j;
-
-               if (plane->plane.state->crtc != &rcrtc->crtc ||
-                   !plane->plane.state->visible)
-                       continue;
-
-               /* Insert the plane in the sorted planes array. */
-               for (j = num_planes++; j > 0; --j) {
-                       if (plane_zpos(planes[j-1]) <= plane_zpos(plane))
-                               break;
-                       planes[j] = planes[j-1];
-               }
-
-               planes[j] = plane;
-               prio += plane_format(plane)->planes * 4;
-       }
-
-       for (i = 0; i < num_planes; ++i) {
-               struct rcar_du_plane *plane = planes[i];
-               struct drm_plane_state *state = plane->plane.state;
-               unsigned int index = to_rcar_plane_state(state)->hwindex;
-
-               prio -= 4;
-               dspr |= (index + 1) << prio;
-               hwplanes |= 1 << index;
-
-               if (plane_format(plane)->planes == 2) {
-                       index = (index + 1) % 8;
-
-                       prio -= 4;
-                       dspr |= (index + 1) << prio;
-                       hwplanes |= 1 << index;
-               }
-       }
-
-       /* If VSP+DU integration is enabled the plane assignment is fixed. */
-       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
-               if (rcdu->info->gen < 3) {
-                       dspr = (rcrtc->index % 2) + 1;
-                       hwplanes = 1 << (rcrtc->index % 2);
-               } else {
-                       dspr = (rcrtc->index % 2) ? 3 : 1;
-                       hwplanes = 1 << ((rcrtc->index % 2) ? 2 : 0);
-               }
-       }
-
-       /*
-        * Update the planes to display timing and dot clock generator
-        * associations.
-        *
-        * Updating the DPTSR register requires restarting the CRTC group,
-        * resulting in visible flicker. To mitigate the issue only update the
-        * association if needed by enabled planes. Planes being disabled will
-        * keep their current association.
-        */
-       mutex_lock(&rcrtc->group->lock);
-
-       dptsr_planes = rcrtc->index % 2 ? rcrtc->group->dptsr_planes | hwplanes
-                    : rcrtc->group->dptsr_planes & ~hwplanes;
-
-       if (dptsr_planes != rcrtc->group->dptsr_planes) {
-               rcar_du_group_write(rcrtc->group, DPTSR,
-                                   (dptsr_planes << 16) | dptsr_planes);
-               rcrtc->group->dptsr_planes = dptsr_planes;
-
-               if (rcrtc->group->used_crtcs)
-                       rcar_du_group_restart(rcrtc->group);
-       }
-
-       /* Restart the group if plane sources have changed. */
-       if (rcrtc->group->need_restart)
-               rcar_du_group_restart(rcrtc->group);
-
-       mutex_unlock(&rcrtc->group->lock);
-
-       rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR,
-                           dspr);
-}
-
-/* -----------------------------------------------------------------------------
- * Page Flip
- */
-
-void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
-{
-       struct drm_pending_vblank_event *event;
-       struct drm_device *dev = rcrtc->crtc.dev;
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->event_lock, flags);
-       event = rcrtc->event;
-       rcrtc->event = NULL;
-       spin_unlock_irqrestore(&dev->event_lock, flags);
-
-       if (event == NULL)
-               return;
-
-       spin_lock_irqsave(&dev->event_lock, flags);
-       drm_crtc_send_vblank_event(&rcrtc->crtc, event);
-       wake_up(&rcrtc->flip_wait);
-       spin_unlock_irqrestore(&dev->event_lock, flags);
-
-       drm_crtc_vblank_put(&rcrtc->crtc);
-}
-
-static bool rcar_du_crtc_page_flip_pending(struct rcar_du_crtc *rcrtc)
-{
-       struct drm_device *dev = rcrtc->crtc.dev;
-       unsigned long flags;
-       bool pending;
-
-       spin_lock_irqsave(&dev->event_lock, flags);
-       pending = rcrtc->event != NULL;
-       spin_unlock_irqrestore(&dev->event_lock, flags);
-
-       return pending;
-}
-
-static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       if (wait_event_timeout(rcrtc->flip_wait,
-                              !rcar_du_crtc_page_flip_pending(rcrtc),
-                              msecs_to_jiffies(50)))
-               return;
-
-       dev_warn(rcdu->dev, "page flip timeout\n");
-
-       rcar_du_crtc_finish_page_flip(rcrtc);
-}
-
-/* -----------------------------------------------------------------------------
- * Color Management Module (CMM)
- */
-
-static int rcar_du_cmm_check(struct drm_crtc *crtc,
-                            struct drm_crtc_state *state)
-{
-       struct drm_property_blob *drm_lut = state->gamma_lut;
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       struct device *dev = rcrtc->dev->dev;
-
-       if (!drm_lut)
-               return 0;
-
-       /* We only accept fully populated LUT tables. */
-       if (drm_color_lut_size(drm_lut) != CM2_LUT_SIZE) {
-               dev_err(dev, "invalid gamma lut size: %zu bytes\n",
-                       drm_lut->length);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static void rcar_du_cmm_setup(struct drm_crtc *crtc)
-{
-       struct drm_property_blob *drm_lut = crtc->state->gamma_lut;
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       struct rcar_cmm_config cmm_config = {};
-
-       if (!rcrtc->cmm)
-               return;
-
-       if (drm_lut)
-               cmm_config.lut.table = (struct drm_color_lut *)drm_lut->data;
-
-       rcar_cmm_setup(rcrtc->cmm, &cmm_config);
-}
-
-/* -----------------------------------------------------------------------------
- * Start/Stop and Suspend/Resume
- */
-
-static void rcar_du_crtc_setup(struct rcar_du_crtc *rcrtc)
-{
-       /* Set display off and background to black */
-       rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
-       rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
-
-       /* Configure display timings and output routing */
-       rcar_du_crtc_set_display_timing(rcrtc);
-       rcar_du_group_set_routing(rcrtc->group);
-
-       /* Start with all planes disabled. */
-       rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
-
-       /* Enable the VSP compositor. */
-       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
-               rcar_du_vsp_enable(rcrtc);
-
-       /* Turn vertical blanking interrupt reporting on. */
-       drm_crtc_vblank_on(&rcrtc->crtc);
-}
-
-static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
-{
-       int ret;
-
-       /*
-        * Guard against double-get, as the function is called from both the
-        * .atomic_enable() and .atomic_begin() handlers.
-        */
-       if (rcrtc->initialized)
-               return 0;
-
-       ret = clk_prepare_enable(rcrtc->clock);
-       if (ret < 0)
-               return ret;
-
-       ret = clk_prepare_enable(rcrtc->extclock);
-       if (ret < 0)
-               goto error_clock;
-
-       ret = rcar_du_group_get(rcrtc->group);
-       if (ret < 0)
-               goto error_group;
-
-       rcar_du_crtc_setup(rcrtc);
-       rcrtc->initialized = true;
-
-       return 0;
-
-error_group:
-       clk_disable_unprepare(rcrtc->extclock);
-error_clock:
-       clk_disable_unprepare(rcrtc->clock);
-       return ret;
-}
-
-static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
-{
-       rcar_du_group_put(rcrtc->group);
-
-       clk_disable_unprepare(rcrtc->extclock);
-       clk_disable_unprepare(rcrtc->clock);
-
-       rcrtc->initialized = false;
-}
-
-static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
-{
-       bool interlaced;
-
-       /*
-        * Select master sync mode. This enables display operation in master
-        * sync mode (with the HSYNC and VSYNC signals configured as outputs and
-        * actively driven).
-        */
-       interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE;
-       rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK | DSYSR_SCM_MASK,
-                                  (interlaced ? DSYSR_SCM_INT_VIDEO : 0) |
-                                  DSYSR_TVM_MASTER);
-
-       rcar_du_group_start_stop(rcrtc->group, true);
-}
-
-static void rcar_du_crtc_disable_planes(struct rcar_du_crtc *rcrtc)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-       struct drm_crtc *crtc = &rcrtc->crtc;
-       u32 status;
-
-       /* Make sure vblank interrupts are enabled. */
-       drm_crtc_vblank_get(crtc);
-
-       /*
-        * Disable planes and calculate how many vertical blanking interrupts we
-        * have to wait for. If a vertical blanking interrupt has been triggered
-        * but not processed yet, we don't know whether it occurred before or
-        * after the planes got disabled. We thus have to wait for two vblank
-        * interrupts in that case.
-        */
-       spin_lock_irq(&rcrtc->vblank_lock);
-       rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
-       status = rcar_du_crtc_read(rcrtc, DSSR);
-       rcrtc->vblank_count = status & DSSR_VBK ? 2 : 1;
-       spin_unlock_irq(&rcrtc->vblank_lock);
-
-       if (!wait_event_timeout(rcrtc->vblank_wait, rcrtc->vblank_count == 0,
-                               msecs_to_jiffies(100)))
-               dev_warn(rcdu->dev, "vertical blanking timeout\n");
-
-       drm_crtc_vblank_put(crtc);
-}
-
-static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
-{
-       struct drm_crtc *crtc = &rcrtc->crtc;
-
-       /*
-        * Disable all planes and wait for the change to take effect. This is
-        * required as the plane enable registers are updated on vblank, and no
-        * vblank will occur once the CRTC is stopped. Disabling planes when
-        * starting the CRTC thus wouldn't be enough as it would start scanning
-        * out immediately from old frame buffers until the next vblank.
-        *
-        * This increases the CRTC stop delay, especially when multiple CRTCs
-        * are stopped in one operation as we now wait for one vblank per CRTC.
-        * Whether this can be improved needs to be researched.
-        */
-       rcar_du_crtc_disable_planes(rcrtc);
-
-       /*
-        * Disable vertical blanking interrupt reporting. We first need to wait
-        * for page flip completion before stopping the CRTC as userspace
-        * expects page flips to eventually complete.
-        */
-       rcar_du_crtc_wait_page_flip(rcrtc);
-       drm_crtc_vblank_off(crtc);
-
-       /* Disable the VSP compositor. */
-       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
-               rcar_du_vsp_disable(rcrtc);
-
-       if (rcrtc->cmm)
-               rcar_cmm_disable(rcrtc->cmm);
-
-       /*
-        * Select switch sync mode. This stops display operation and configures
-        * the HSYNC and VSYNC signals as inputs.
-        *
-        * TODO: Find another way to stop the display for DUs that don't support
-        * TVM sync.
-        */
-       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_TVM_SYNC))
-               rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK,
-                                          DSYSR_TVM_SWITCH);
-
-       rcar_du_group_start_stop(rcrtc->group, false);
-}
-
-/* -----------------------------------------------------------------------------
- * CRTC Functions
- */
-
-static int rcar_du_crtc_atomic_check(struct drm_crtc *crtc,
-                                    struct drm_atomic_state *state)
-{
-       struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
-                                                                         crtc);
-       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc_state);
-       struct drm_encoder *encoder;
-       int ret;
-
-       ret = rcar_du_cmm_check(crtc, crtc_state);
-       if (ret)
-               return ret;
-
-       /* Store the routes from the CRTC output to the DU outputs. */
-       rstate->outputs = 0;
-
-       drm_for_each_encoder_mask(encoder, crtc->dev,
-                                 crtc_state->encoder_mask) {
-               struct rcar_du_encoder *renc;
-
-               /* Skip the writeback encoder. */
-               if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
-                       continue;
-
-               renc = to_rcar_encoder(encoder);
-               rstate->outputs |= BIT(renc->output);
-       }
-
-       return 0;
-}
-
-static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
-                                      struct drm_atomic_state *state)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state);
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       if (rcrtc->cmm)
-               rcar_cmm_enable(rcrtc->cmm);
-       rcar_du_crtc_get(rcrtc);
-
-       /*
-        * On D3/E3 the dot clock is provided by the LVDS encoder attached to
-        * the DU channel. We need to enable its clock output explicitly before
-        * starting the CRTC, as the bridge hasn't been enabled by the atomic
-        * helpers yet.
-        */
-       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
-               bool dot_clk_only = rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0);
-               struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
-               const struct drm_display_mode *mode =
-                       &crtc->state->adjusted_mode;
-
-               rcar_lvds_pclk_enable(bridge, mode->clock * 1000, dot_clk_only);
-       }
-
-       /*
-        * Similarly to LVDS, on V3U the dot clock is provided by the DSI
-        * encoder, and we need to enable the DSI clocks before enabling the CRTC.
-        */
-       if ((rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) &&
-           (rstate->outputs &
-            (BIT(RCAR_DU_OUTPUT_DSI0) | BIT(RCAR_DU_OUTPUT_DSI1)))) {
-               struct drm_bridge *bridge = rcdu->dsi[rcrtc->index];
-
-               rcar_mipi_dsi_pclk_enable(bridge, state);
-       }
-
-       rcar_du_crtc_start(rcrtc);
-
-       /*
-        * TODO: The chip manual indicates that CMM tables should be written
-        * after the DU channel has been activated. Investigate the impact
-        * of this restriction on the first displayed frame.
-        */
-       rcar_du_cmm_setup(crtc);
-}
-
-static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
-                                       struct drm_atomic_state *state)
-{
-       struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
-                                                                        crtc);
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(old_state);
-       struct rcar_du_device *rcdu = rcrtc->dev;
-
-       rcar_du_crtc_stop(rcrtc);
-       rcar_du_crtc_put(rcrtc);
-
-       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
-               bool dot_clk_only = rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0);
-               struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
-
-               /*
-                * Disable the LVDS clock output, see
-                * rcar_du_crtc_atomic_enable(). When the LVDS output is used,
-                * this also disables the LVDS encoder.
-                */
-               rcar_lvds_pclk_disable(bridge, dot_clk_only);
-       }
-
-       if ((rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) &&
-           (rstate->outputs &
-            (BIT(RCAR_DU_OUTPUT_DSI0) | BIT(RCAR_DU_OUTPUT_DSI1)))) {
-               struct drm_bridge *bridge = rcdu->dsi[rcrtc->index];
-
-               /*
-                * Disable the DSI clock output, see
-                * rcar_du_crtc_atomic_enable().
-                */
-               rcar_mipi_dsi_pclk_disable(bridge);
-       }
-
-       spin_lock_irq(&crtc->dev->event_lock);
-       if (crtc->state->event) {
-               drm_crtc_send_vblank_event(crtc, crtc->state->event);
-               crtc->state->event = NULL;
-       }
-       spin_unlock_irq(&crtc->dev->event_lock);
-}
-
-static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc,
-                                     struct drm_atomic_state *state)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-
-       WARN_ON(!crtc->state->enable);
-
-       /*
-        * If a mode set is in progress we can be called with the CRTC disabled.
-        * We thus need to first get and setup the CRTC in order to configure
-        * planes. We must *not* put the CRTC in .atomic_flush(), as it must be
-        * kept awake until the .atomic_enable() call that will follow. The get
-        * operation in .atomic_enable() will in that case be a no-op, and the
-        * CRTC will be put later in .atomic_disable().
-        *
-        * If a mode set is not in progress the CRTC is enabled, and the
-        * following get call will be a no-op. There is thus no need to balance
-        * it in .atomic_flush() either.
-        */
-       rcar_du_crtc_get(rcrtc);
-
-       /* If the active state changed, we let .atomic_enable handle CMM. */
-       if (crtc->state->color_mgmt_changed && !crtc->state->active_changed)
-               rcar_du_cmm_setup(crtc);
-
-       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
-               rcar_du_vsp_atomic_begin(rcrtc);
-}
-
-static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc,
-                                     struct drm_atomic_state *state)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       struct drm_device *dev = rcrtc->crtc.dev;
-       unsigned long flags;
-
-       rcar_du_crtc_update_planes(rcrtc);
-
-       if (crtc->state->event) {
-               WARN_ON(drm_crtc_vblank_get(crtc) != 0);
-
-               spin_lock_irqsave(&dev->event_lock, flags);
-               rcrtc->event = crtc->state->event;
-               crtc->state->event = NULL;
-               spin_unlock_irqrestore(&dev->event_lock, flags);
-       }
-
-       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
-               rcar_du_vsp_atomic_flush(rcrtc);
-}
-
-static enum drm_mode_status
-rcar_du_crtc_mode_valid(struct drm_crtc *crtc,
-                       const struct drm_display_mode *mode)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       struct rcar_du_device *rcdu = rcrtc->dev;
-       bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
-       unsigned int min_sync_porch;
-       unsigned int vbp;
-
-       if (interlaced && !rcar_du_has(rcdu, RCAR_DU_FEATURE_INTERLACED))
-               return MODE_NO_INTERLACE;
-
-       /*
-        * The hardware requires a minimum combined horizontal sync and back
-        * porch of 20 pixels (when CMM isn't used) or 45 pixels (when CMM is
-        * used), and a minimum vertical back porch of 3 lines.
-        */
-       min_sync_porch = 20;
-       if (rcrtc->group->cmms_mask & BIT(rcrtc->index % 2))
-               min_sync_porch += 25;
-
-       if (mode->htotal - mode->hsync_start < min_sync_porch)
-               return MODE_HBLANK_NARROW;
-
-       vbp = (mode->vtotal - mode->vsync_end) / (interlaced ? 2 : 1);
-       if (vbp < 3)
-               return MODE_VBLANK_NARROW;
-
-       return MODE_OK;
-}
-
-static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
-       .atomic_check = rcar_du_crtc_atomic_check,
-       .atomic_begin = rcar_du_crtc_atomic_begin,
-       .atomic_flush = rcar_du_crtc_atomic_flush,
-       .atomic_enable = rcar_du_crtc_atomic_enable,
-       .atomic_disable = rcar_du_crtc_atomic_disable,
-       .mode_valid = rcar_du_crtc_mode_valid,
-};
-
-static void rcar_du_crtc_crc_init(struct rcar_du_crtc *rcrtc)
-{
-       struct rcar_du_device *rcdu = rcrtc->dev;
-       const char **sources;
-       unsigned int count;
-       int i = -1;
-
-       /* CRC available only on Gen3 HW. */
-       if (rcdu->info->gen < 3)
-               return;
-
-       /* Reserve 1 for "auto" source. */
-       count = rcrtc->vsp->num_planes + 1;
-
-       sources = kmalloc_array(count, sizeof(*sources), GFP_KERNEL);
-       if (!sources)
-               return;
-
-       sources[0] = kstrdup("auto", GFP_KERNEL);
-       if (!sources[0])
-               goto error;
-
-       for (i = 0; i < rcrtc->vsp->num_planes; ++i) {
-               struct drm_plane *plane = &rcrtc->vsp->planes[i].plane;
-               char name[16];
-
-               sprintf(name, "plane%u", plane->base.id);
-               sources[i + 1] = kstrdup(name, GFP_KERNEL);
-               if (!sources[i + 1])
-                       goto error;
-       }
-
-       rcrtc->sources = sources;
-       rcrtc->sources_count = count;
-       return;
-
-error:
-       while (i >= 0) {
-               kfree(sources[i]);
-               i--;
-       }
-       kfree(sources);
-}
-
-static void rcar_du_crtc_crc_cleanup(struct rcar_du_crtc *rcrtc)
-{
-       unsigned int i;
-
-       if (!rcrtc->sources)
-               return;
-
-       for (i = 0; i < rcrtc->sources_count; i++)
-               kfree(rcrtc->sources[i]);
-       kfree(rcrtc->sources);
-
-       rcrtc->sources = NULL;
-       rcrtc->sources_count = 0;
-}
-
-static struct drm_crtc_state *
-rcar_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
-{
-       struct rcar_du_crtc_state *state;
-       struct rcar_du_crtc_state *copy;
-
-       if (WARN_ON(!crtc->state))
-               return NULL;
-
-       state = to_rcar_crtc_state(crtc->state);
-       copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
-       if (copy == NULL)
-               return NULL;
-
-       __drm_atomic_helper_crtc_duplicate_state(crtc, &copy->state);
-
-       return &copy->state;
-}
-
-static void rcar_du_crtc_atomic_destroy_state(struct drm_crtc *crtc,
-                                             struct drm_crtc_state *state)
-{
-       __drm_atomic_helper_crtc_destroy_state(state);
-       kfree(to_rcar_crtc_state(state));
-}
-
-static void rcar_du_crtc_cleanup(struct drm_crtc *crtc)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-
-       rcar_du_crtc_crc_cleanup(rcrtc);
-
-       return drm_crtc_cleanup(crtc);
-}
-
-static void rcar_du_crtc_reset(struct drm_crtc *crtc)
-{
-       struct rcar_du_crtc_state *state;
-
-       if (crtc->state) {
-               rcar_du_crtc_atomic_destroy_state(crtc, crtc->state);
-               crtc->state = NULL;
-       }
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (state == NULL)
-               return;
-
-       state->crc.source = VSP1_DU_CRC_NONE;
-       state->crc.index = 0;
-
-       __drm_atomic_helper_crtc_reset(crtc, &state->state);
-}
-
-static int rcar_du_crtc_enable_vblank(struct drm_crtc *crtc)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-
-       rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
-       rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
-       rcrtc->vblank_enable = true;
-
-       return 0;
-}
-
-static void rcar_du_crtc_disable_vblank(struct drm_crtc *crtc)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-
-       rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
-       rcrtc->vblank_enable = false;
-}
-
-static int rcar_du_crtc_parse_crc_source(struct rcar_du_crtc *rcrtc,
-                                        const char *source_name,
-                                        enum vsp1_du_crc_source *source)
-{
-       unsigned int index;
-       int ret;
-
-       /*
-        * Parse the source name. Supported values are "plane%u" to compute the
-        * CRC on an input plane (%u is the plane ID), and "auto" to compute the
-        * CRC on the composer (VSP) output.
-        */
-
-       if (!source_name) {
-               *source = VSP1_DU_CRC_NONE;
-               return 0;
-       } else if (!strcmp(source_name, "auto")) {
-               *source = VSP1_DU_CRC_OUTPUT;
-               return 0;
-       } else if (strstarts(source_name, "plane")) {
-               unsigned int i;
-
-               *source = VSP1_DU_CRC_PLANE;
-
-               ret = kstrtouint(source_name + strlen("plane"), 10, &index);
-               if (ret < 0)
-                       return ret;
-
-               for (i = 0; i < rcrtc->vsp->num_planes; ++i) {
-                       if (index == rcrtc->vsp->planes[i].plane.base.id)
-                               return i;
-               }
-       }
-
-       return -EINVAL;
-}
-
-static int rcar_du_crtc_verify_crc_source(struct drm_crtc *crtc,
-                                         const char *source_name,
-                                         size_t *values_cnt)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       enum vsp1_du_crc_source source;
-
-       if (rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source) < 0) {
-               DRM_DEBUG_DRIVER("unknown source %s\n", source_name);
-               return -EINVAL;
-       }
-
-       *values_cnt = 1;
-       return 0;
-}
-
-static const char *const *
-rcar_du_crtc_get_crc_sources(struct drm_crtc *crtc, size_t *count)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-
-       *count = rcrtc->sources_count;
-       return rcrtc->sources;
-}
-
-static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc,
-                                      const char *source_name)
-{
-       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-       struct drm_modeset_acquire_ctx ctx;
-       struct drm_crtc_state *crtc_state;
-       struct drm_atomic_state *state;
-       enum vsp1_du_crc_source source;
-       unsigned int index;
-       int ret;
-
-       ret = rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source);
-       if (ret < 0)
-               return ret;
-
-       index = ret;
-
-       /* Perform an atomic commit to set the CRC source. */
-       drm_modeset_acquire_init(&ctx, 0);
-
-       state = drm_atomic_state_alloc(crtc->dev);
-       if (!state) {
-               ret = -ENOMEM;
-               goto unlock;
-       }
-
-       state->acquire_ctx = &ctx;
-
-retry:
-       crtc_state = drm_atomic_get_crtc_state(state, crtc);
-       if (!IS_ERR(crtc_state)) {
-               struct rcar_du_crtc_state *rcrtc_state;
-
-               rcrtc_state = to_rcar_crtc_state(crtc_state);
-               rcrtc_state->crc.source = source;
-               rcrtc_state->crc.index = index;
-
-               ret = drm_atomic_commit(state);
-       } else {
-               ret = PTR_ERR(crtc_state);
-       }
-
-       if (ret == -EDEADLK) {
-               drm_atomic_state_clear(state);
-               drm_modeset_backoff(&ctx);
-               goto retry;
-       }
-
-       drm_atomic_state_put(state);
-
-unlock:
-       drm_modeset_drop_locks(&ctx);
-       drm_modeset_acquire_fini(&ctx);
-
-       return ret;
-}
-
-static const struct drm_crtc_funcs crtc_funcs_gen2 = {
-       .reset = rcar_du_crtc_reset,
-       .destroy = drm_crtc_cleanup,
-       .set_config = drm_atomic_helper_set_config,
-       .page_flip = drm_atomic_helper_page_flip,
-       .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state,
-       .atomic_destroy_state = rcar_du_crtc_atomic_destroy_state,
-       .enable_vblank = rcar_du_crtc_enable_vblank,
-       .disable_vblank = rcar_du_crtc_disable_vblank,
-};
-
-static const struct drm_crtc_funcs crtc_funcs_gen3 = {
-       .reset = rcar_du_crtc_reset,
-       .destroy = rcar_du_crtc_cleanup,
-       .set_config = drm_atomic_helper_set_config,
-       .page_flip = drm_atomic_helper_page_flip,
-       .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state,
-       .atomic_destroy_state = rcar_du_crtc_atomic_destroy_state,
-       .enable_vblank = rcar_du_crtc_enable_vblank,
-       .disable_vblank = rcar_du_crtc_disable_vblank,
-       .set_crc_source = rcar_du_crtc_set_crc_source,
-       .verify_crc_source = rcar_du_crtc_verify_crc_source,
-       .get_crc_sources = rcar_du_crtc_get_crc_sources,
-};
-
-/* -----------------------------------------------------------------------------
- * Interrupt Handling
- */
-
-static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
-{
-       struct rcar_du_crtc *rcrtc = arg;
-       struct rcar_du_device *rcdu = rcrtc->dev;
-       irqreturn_t ret = IRQ_NONE;
-       u32 status;
-
-       spin_lock(&rcrtc->vblank_lock);
-
-       status = rcar_du_crtc_read(rcrtc, DSSR);
-       rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
-
-       if (status & DSSR_VBK) {
-               /*
-                * Wake up the vblank wait if the counter reaches 0. This must
-                * be protected by the vblank_lock to avoid races in
-                * rcar_du_crtc_disable_planes().
-                */
-               if (rcrtc->vblank_count) {
-                       if (--rcrtc->vblank_count == 0)
-                               wake_up(&rcrtc->vblank_wait);
-               }
-       }
-
-       spin_unlock(&rcrtc->vblank_lock);
-
-       if (status & DSSR_VBK) {
-               if (rcdu->info->gen < 3) {
-                       drm_crtc_handle_vblank(&rcrtc->crtc);
-                       rcar_du_crtc_finish_page_flip(rcrtc);
-               }
-
-               ret = IRQ_HANDLED;
-       }
-
-       return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * Initialization
- */
-
-int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
-                       unsigned int hwindex)
-{
-       static const unsigned int mmio_offsets[] = {
-               DU0_REG_OFFSET, DU1_REG_OFFSET, DU2_REG_OFFSET, DU3_REG_OFFSET
-       };
-
-       struct rcar_du_device *rcdu = rgrp->dev;
-       struct platform_device *pdev = to_platform_device(rcdu->dev);
-       struct rcar_du_crtc *rcrtc = &rcdu->crtcs[swindex];
-       struct drm_crtc *crtc = &rcrtc->crtc;
-       struct drm_plane *primary;
-       unsigned int irqflags;
-       struct clk *clk;
-       char clk_name[9];
-       char *name;
-       int irq;
-       int ret;
-
-       /* Get the CRTC clock and the optional external clock. */
-       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_CLOCK)) {
-               sprintf(clk_name, "du.%u", hwindex);
-               name = clk_name;
-       } else {
-               name = NULL;
-       }
-
-       rcrtc->clock = devm_clk_get(rcdu->dev, name);
-       if (IS_ERR(rcrtc->clock)) {
-               dev_err(rcdu->dev, "no clock for DU channel %u\n", hwindex);
-               return PTR_ERR(rcrtc->clock);
-       }
-
-       sprintf(clk_name, "dclkin.%u", hwindex);
-       clk = devm_clk_get(rcdu->dev, clk_name);
-       if (!IS_ERR(clk)) {
-               rcrtc->extclock = clk;
-       } else if (PTR_ERR(clk) == -EPROBE_DEFER) {
-               return -EPROBE_DEFER;
-       } else if (rcdu->info->dpll_mask & BIT(hwindex)) {
-               /*
-                * DU channels that have a display PLL can't use the internal
-                * system clock and thus require an external clock.
-                */
-               ret = PTR_ERR(clk);
-               dev_err(rcdu->dev, "can't get dclkin.%u: %d\n", hwindex, ret);
-               return ret;
-       }
-
-       init_waitqueue_head(&rcrtc->flip_wait);
-       init_waitqueue_head(&rcrtc->vblank_wait);
-       spin_lock_init(&rcrtc->vblank_lock);
-
-       rcrtc->dev = rcdu;
-       rcrtc->group = rgrp;
-       rcrtc->mmio_offset = mmio_offsets[hwindex];
-       rcrtc->index = hwindex;
-       rcrtc->dsysr = rcrtc->index % 2 ? 0 : DSYSR_DRES;
-
-       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_TVM_SYNC))
-               rcrtc->dsysr |= DSYSR_TVM_TVSYNC;
-
-       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
-               primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane;
-       else
-               primary = &rgrp->planes[swindex % 2].plane;
-
-       ret = drm_crtc_init_with_planes(&rcdu->ddev, crtc, primary, NULL,
-                                       rcdu->info->gen <= 2 ?
-                                       &crtc_funcs_gen2 : &crtc_funcs_gen3,
-                                       NULL);
-       if (ret < 0)
-               return ret;
-
-       /* CMM might be disabled for this CRTC. */
-       if (rcdu->cmms[swindex]) {
-               rcrtc->cmm = rcdu->cmms[swindex];
-               rgrp->cmms_mask |= BIT(hwindex % 2);
-
-               drm_mode_crtc_set_gamma_size(crtc, CM2_LUT_SIZE);
-               drm_crtc_enable_color_mgmt(crtc, 0, false, CM2_LUT_SIZE);
-       }
-
-       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
-
-       /* Register the interrupt handler. */
-       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ)) {
-               /* The IRQ's are associated with the CRTC (sw)index. */
-               irq = platform_get_irq(pdev, swindex);
-               irqflags = 0;
-       } else {
-               irq = platform_get_irq(pdev, 0);
-               irqflags = IRQF_SHARED;
-       }
-
-       if (irq < 0) {
-               dev_err(rcdu->dev, "no IRQ for CRTC %u\n", swindex);
-               return irq;
-       }
-
-       ret = devm_request_irq(rcdu->dev, irq, rcar_du_crtc_irq, irqflags,
-                              dev_name(rcdu->dev), rcrtc);
-       if (ret < 0) {
-               dev_err(rcdu->dev,
-                       "failed to register IRQ for CRTC %u\n", swindex);
-               return ret;
-       }
-
-       rcar_du_crtc_crc_init(rcrtc);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
deleted file mode 100644 (file)
index d0f38a8..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit CRTCs
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_CRTC_H__
-#define __RCAR_DU_CRTC_H__
-
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_writeback.h>
-
-#include <media/vsp1.h>
-
-struct rcar_du_group;
-struct rcar_du_vsp;
-
-/**
- * struct rcar_du_crtc - the CRTC, representing a DU superposition processor
- * @crtc: base DRM CRTC
- * @dev: the DU device
- * @clock: the CRTC functional clock
- * @extclock: external pixel dot clock (optional)
- * @mmio_offset: offset of the CRTC registers in the DU MMIO block
- * @index: CRTC hardware index
- * @initialized: whether the CRTC has been initialized and clocks enabled
- * @dsysr: cached value of the DSYSR register
- * @vblank_enable: whether vblank events are enabled on this CRTC
- * @event: event to post when the pending page flip completes
- * @flip_wait: wait queue used to signal page flip completion
- * @vblank_lock: protects vblank_wait and vblank_count
- * @vblank_wait: wait queue used to signal vertical blanking
- * @vblank_count: number of vertical blanking interrupts to wait for
- * @group: CRTC group this CRTC belongs to
- * @cmm: CMM associated with this CRTC
- * @vsp: VSP feeding video to this CRTC
- * @vsp_pipe: index of the VSP pipeline feeding video to this CRTC
- * @writeback: the writeback connector
- */
-struct rcar_du_crtc {
-       struct drm_crtc crtc;
-
-       struct rcar_du_device *dev;
-       struct clk *clock;
-       struct clk *extclock;
-       unsigned int mmio_offset;
-       unsigned int index;
-       bool initialized;
-
-       u32 dsysr;
-
-       bool vblank_enable;
-       struct drm_pending_vblank_event *event;
-       wait_queue_head_t flip_wait;
-
-       spinlock_t vblank_lock;
-       wait_queue_head_t vblank_wait;
-       unsigned int vblank_count;
-
-       struct rcar_du_group *group;
-       struct platform_device *cmm;
-       struct rcar_du_vsp *vsp;
-       unsigned int vsp_pipe;
-
-       const char *const *sources;
-       unsigned int sources_count;
-
-       struct drm_writeback_connector writeback;
-};
-
-#define to_rcar_crtc(c)                container_of(c, struct rcar_du_crtc, crtc)
-#define wb_to_rcar_crtc(c)     container_of(c, struct rcar_du_crtc, writeback)
-
-/**
- * struct rcar_du_crtc_state - Driver-specific CRTC state
- * @state: base DRM CRTC state
- * @crc: CRC computation configuration
- * @outputs: bitmask of the outputs (enum rcar_du_output) driven by this CRTC
- */
-struct rcar_du_crtc_state {
-       struct drm_crtc_state state;
-
-       struct vsp1_du_crc_config crc;
-       unsigned int outputs;
-};
-
-#define to_rcar_crtc_state(s) container_of(s, struct rcar_du_crtc_state, state)
-
-int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
-                       unsigned int hwindex);
-
-void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc);
-
-void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set);
-
-#endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
deleted file mode 100644 (file)
index 1ffde19..0000000
+++ /dev/null
@@ -1,744 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit DRM driver
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/io.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-#include <linux/wait.h>
-
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_managed.h>
-#include <drm/drm_probe_helper.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_kms.h"
-
-/* -----------------------------------------------------------------------------
- * Device Information
- */
-
-static const struct rcar_du_device_info rzg1_du_r8a7743_info = {
-       .gen = 2,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A774[34] has one RGB output and one LVDS output
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(1) | BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 1,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 4,
-};
-
-static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
-       .gen = 2,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A7745 has two RGB outputs
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_DPAD1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-       },
-       .num_rpf = 4,
-};
-
-static const struct rcar_du_device_info rzg1_du_r8a77470_info = {
-       .gen = 2,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A77470 has two RGB outputs, one LVDS output, and
-                * one (currently unsupported) analog video output
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_DPAD1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0) | BIT(1),
-                       .port = 2,
-               },
-       },
-       .num_rpf = 4,
-};
-
-static const struct rcar_du_device_info rcar_du_r8a774a1_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(2) | BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A774A1 has one RGB output, one LVDS output and one HDMI
-                * output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(2),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_HDMI0] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 5,
-       .dpll_mask =  BIT(1),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a774b1_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(3) | BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A774B1 has one RGB output, one LVDS output and one HDMI
-                * output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(2),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_HDMI0] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 5,
-       .dpll_mask =  BIT(1),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a774c0_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A774C0 has one RGB output and two LVDS outputs
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0) | BIT(1),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 2,
-       .num_rpf = 4,
-       .lvds_clk_mask =  BIT(1) | BIT(0),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a774e1_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(3) | BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A774E1 has one RGB output, one LVDS output and one HDMI
-                * output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(2),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_HDMI0] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 5,
-       .dpll_mask =  BIT(1),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7779_info = {
-       .gen = 1,
-       .features = RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A7779 has two RGB outputs and one (currently unsupported)
-                * TCON output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_DPAD1] = {
-                       .possible_crtcs = BIT(1) | BIT(0),
-                       .port = 1,
-               },
-       },
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7790_info = {
-       .gen = 2,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .quirks = RCAR_DU_QUIRK_ALIGN_128B,
-       .channels_mask = BIT(2) | BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A7742 and R8A7790 each have one RGB output and two LVDS
-                * outputs. Additionally R8A7790 supports one TCON output
-                * (currently unsupported by the driver).
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(2) | BIT(1) | BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS1] = {
-                       .possible_crtcs = BIT(2) | BIT(1),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 2,
-       .num_rpf = 4,
-};
-
-/* M2-W (r8a7791) and M2-N (r8a7793) are identical */
-static const struct rcar_du_device_info rcar_du_r8a7791_info = {
-       .gen = 2,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A779[13] has one RGB output, one LVDS output and one
-                * (currently unsupported) TCON output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(1) | BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 1,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 4,
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7792_info = {
-       .gen = 2,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /* R8A7792 has two RGB outputs. */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_DPAD1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-       },
-       .num_rpf = 4,
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7794_info = {
-       .gen = 2,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A7794 has two RGB outputs and one (currently unsupported)
-                * TCON output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_DPAD1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-       },
-       .num_rpf = 4,
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7795_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A7795 has one RGB output, two HDMI outputs and one
-                * LVDS output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(3),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_HDMI0] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_HDMI1] = {
-                       .possible_crtcs = BIT(2),
-                       .port = 2,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 3,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 5,
-       .dpll_mask =  BIT(2) | BIT(1),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7796_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(2) | BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A7796 has one RGB output, one LVDS output and one HDMI
-                * output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(2),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_HDMI0] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 5,
-       .dpll_mask =  BIT(1),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a77965_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(3) | BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A77965 has one RGB output, one LVDS output and one HDMI
-                * output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(2),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_HDMI0] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 5,
-       .dpll_mask =  BIT(1),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a77970_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_INTERLACED
-                 | RCAR_DU_FEATURE_TVM_SYNC,
-       .channels_mask = BIT(0),
-       .routes = {
-               /*
-                * R8A77970 and R8A77980 have one RGB output and one LVDS
-                * output.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 1,
-               },
-       },
-       .num_lvds = 1,
-       .num_rpf = 5,
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7799x_info = {
-       .gen = 3,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_CRTC_CLOCK
-                 | RCAR_DU_FEATURE_VSP1_SOURCE,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /*
-                * R8A77990 and R8A77995 have one RGB output and two LVDS
-                * outputs.
-                */
-               [RCAR_DU_OUTPUT_DPAD0] = {
-                       .possible_crtcs = BIT(0) | BIT(1),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_LVDS0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 1,
-               },
-               [RCAR_DU_OUTPUT_LVDS1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 2,
-               },
-       },
-       .num_lvds = 2,
-       .num_rpf = 5,
-       .lvds_clk_mask =  BIT(1) | BIT(0),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a779a0_info = {
-       .gen = 4,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_NO_BLENDING,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /* R8A779A0 has two MIPI DSI outputs. */
-               [RCAR_DU_OUTPUT_DSI0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_DSI1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-       },
-       .num_rpf = 5,
-       .dsi_clk_mask =  BIT(1) | BIT(0),
-};
-
-static const struct rcar_du_device_info rcar_du_r8a779g0_info = {
-       .gen = 4,
-       .features = RCAR_DU_FEATURE_CRTC_IRQ
-                 | RCAR_DU_FEATURE_VSP1_SOURCE
-                 | RCAR_DU_FEATURE_NO_BLENDING,
-       .channels_mask = BIT(1) | BIT(0),
-       .routes = {
-               /* R8A779G0 has two MIPI DSI outputs. */
-               [RCAR_DU_OUTPUT_DSI0] = {
-                       .possible_crtcs = BIT(0),
-                       .port = 0,
-               },
-               [RCAR_DU_OUTPUT_DSI1] = {
-                       .possible_crtcs = BIT(1),
-                       .port = 1,
-               },
-       },
-       .num_rpf = 5,
-       .dsi_clk_mask =  BIT(1) | BIT(0),
-};
-
-static const struct of_device_id rcar_du_of_table[] = {
-       { .compatible = "renesas,du-r8a7742", .data = &rcar_du_r8a7790_info },
-       { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },
-       { .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info },
-       { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
-       { .compatible = "renesas,du-r8a77470", .data = &rzg1_du_r8a77470_info },
-       { .compatible = "renesas,du-r8a774a1", .data = &rcar_du_r8a774a1_info },
-       { .compatible = "renesas,du-r8a774b1", .data = &rcar_du_r8a774b1_info },
-       { .compatible = "renesas,du-r8a774c0", .data = &rcar_du_r8a774c0_info },
-       { .compatible = "renesas,du-r8a774e1", .data = &rcar_du_r8a774e1_info },
-       { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
-       { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
-       { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
-       { .compatible = "renesas,du-r8a7792", .data = &rcar_du_r8a7792_info },
-       { .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info },
-       { .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
-       { .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
-       { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
-       { .compatible = "renesas,du-r8a77961", .data = &rcar_du_r8a7796_info },
-       { .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info },
-       { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info },
-       { .compatible = "renesas,du-r8a77980", .data = &rcar_du_r8a77970_info },
-       { .compatible = "renesas,du-r8a77990", .data = &rcar_du_r8a7799x_info },
-       { .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info },
-       { .compatible = "renesas,du-r8a779a0", .data = &rcar_du_r8a779a0_info },
-       { .compatible = "renesas,du-r8a779g0", .data = &rcar_du_r8a779g0_info },
-       { }
-};
-
-MODULE_DEVICE_TABLE(of, rcar_du_of_table);
-
-const char *rcar_du_output_name(enum rcar_du_output output)
-{
-       static const char * const names[] = {
-               [RCAR_DU_OUTPUT_DPAD0] = "DPAD0",
-               [RCAR_DU_OUTPUT_DPAD1] = "DPAD1",
-               [RCAR_DU_OUTPUT_DSI0] = "DSI0",
-               [RCAR_DU_OUTPUT_DSI1] = "DSI1",
-               [RCAR_DU_OUTPUT_HDMI0] = "HDMI0",
-               [RCAR_DU_OUTPUT_HDMI1] = "HDMI1",
-               [RCAR_DU_OUTPUT_LVDS0] = "LVDS0",
-               [RCAR_DU_OUTPUT_LVDS1] = "LVDS1",
-               [RCAR_DU_OUTPUT_TCON] = "TCON",
-       };
-
-       if (output >= ARRAY_SIZE(names) || !names[output])
-               return "UNKNOWN";
-
-       return names[output];
-}
-
-/* -----------------------------------------------------------------------------
- * DRM operations
- */
-
-DEFINE_DRM_GEM_DMA_FOPS(rcar_du_fops);
-
-static const struct drm_driver rcar_du_driver = {
-       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
-       .dumb_create            = rcar_du_dumb_create,
-       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
-       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
-       .gem_prime_import_sg_table = rcar_du_gem_prime_import_sg_table,
-       .gem_prime_mmap         = drm_gem_prime_mmap,
-       .fops                   = &rcar_du_fops,
-       .name                   = "rcar-du",
-       .desc                   = "Renesas R-Car Display Unit",
-       .date                   = "20130110",
-       .major                  = 1,
-       .minor                  = 0,
-};
-
-/* -----------------------------------------------------------------------------
- * Power management
- */
-
-static int rcar_du_pm_suspend(struct device *dev)
-{
-       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
-
-       return drm_mode_config_helper_suspend(&rcdu->ddev);
-}
-
-static int rcar_du_pm_resume(struct device *dev)
-{
-       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
-
-       return drm_mode_config_helper_resume(&rcdu->ddev);
-}
-
-static DEFINE_SIMPLE_DEV_PM_OPS(rcar_du_pm_ops,
-                               rcar_du_pm_suspend, rcar_du_pm_resume);
-
-/* -----------------------------------------------------------------------------
- * Platform driver
- */
-
-static int rcar_du_remove(struct platform_device *pdev)
-{
-       struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
-       struct drm_device *ddev = &rcdu->ddev;
-
-       drm_dev_unregister(ddev);
-       drm_atomic_helper_shutdown(ddev);
-
-       drm_kms_helper_poll_fini(ddev);
-
-       return 0;
-}
-
-static void rcar_du_shutdown(struct platform_device *pdev)
-{
-       struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
-
-       drm_atomic_helper_shutdown(&rcdu->ddev);
-}
-
-static int rcar_du_probe(struct platform_device *pdev)
-{
-       struct rcar_du_device *rcdu;
-       unsigned int mask;
-       int ret;
-
-       if (drm_firmware_drivers_only())
-               return -ENODEV;
-
-       /* Allocate and initialize the R-Car device structure. */
-       rcdu = devm_drm_dev_alloc(&pdev->dev, &rcar_du_driver,
-                                 struct rcar_du_device, ddev);
-       if (IS_ERR(rcdu))
-               return PTR_ERR(rcdu);
-
-       rcdu->dev = &pdev->dev;
-
-       rcdu->info = of_device_get_match_data(rcdu->dev);
-
-       platform_set_drvdata(pdev, rcdu);
-
-       /* I/O resources */
-       rcdu->mmio = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(rcdu->mmio))
-               return PTR_ERR(rcdu->mmio);
-
-       /*
-        * Set the DMA coherent mask to reflect the DU 32-bit DMA address space
-        * limitations. When sourcing frames from a VSP the DU doesn't perform
-        * any memory access so set the mask to 40 bits to accept all buffers.
-        */
-       mask = rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE) ? 40 : 32;
-       ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(mask));
-       if (ret)
-               return ret;
-
-       /* DRM/KMS objects */
-       ret = rcar_du_modeset_init(rcdu);
-       if (ret < 0) {
-               if (ret != -EPROBE_DEFER)
-                       dev_err(&pdev->dev,
-                               "failed to initialize DRM/KMS (%d)\n", ret);
-               goto error;
-       }
-
-       /*
-        * Register the DRM device with the core and the connectors with
-        * sysfs.
-        */
-       ret = drm_dev_register(&rcdu->ddev, 0);
-       if (ret)
-               goto error;
-
-       DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
-
-       drm_fbdev_generic_setup(&rcdu->ddev, 32);
-
-       return 0;
-
-error:
-       drm_kms_helper_poll_fini(&rcdu->ddev);
-       return ret;
-}
-
-static struct platform_driver rcar_du_platform_driver = {
-       .probe          = rcar_du_probe,
-       .remove         = rcar_du_remove,
-       .shutdown       = rcar_du_shutdown,
-       .driver         = {
-               .name   = "rcar-du",
-               .pm     = pm_sleep_ptr(&rcar_du_pm_ops),
-               .of_match_table = rcar_du_of_table,
-       },
-};
-
-module_platform_driver(rcar_du_platform_driver);
-
-MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
-MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
deleted file mode 100644 (file)
index 5cfa2bb..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit DRM driver
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_DRV_H__
-#define __RCAR_DU_DRV_H__
-
-#include <linux/kernel.h>
-#include <linux/wait.h>
-
-#include <drm/drm_device.h>
-
-#include "rcar_cmm.h"
-#include "rcar_du_crtc.h"
-#include "rcar_du_group.h"
-#include "rcar_du_vsp.h"
-
-struct clk;
-struct device;
-struct drm_bridge;
-struct drm_property;
-struct rcar_du_device;
-
-#define RCAR_DU_FEATURE_CRTC_IRQ       BIT(0)  /* Per-CRTC IRQ */
-#define RCAR_DU_FEATURE_CRTC_CLOCK     BIT(1)  /* Per-CRTC clock */
-#define RCAR_DU_FEATURE_VSP1_SOURCE    BIT(2)  /* Has inputs from VSP1 */
-#define RCAR_DU_FEATURE_INTERLACED     BIT(3)  /* HW supports interlaced */
-#define RCAR_DU_FEATURE_TVM_SYNC       BIT(4)  /* Has TV switch/sync modes */
-#define RCAR_DU_FEATURE_NO_BLENDING    BIT(5)  /* PnMR.SPIM does not have ALP nor EOR bits */
-
-#define RCAR_DU_QUIRK_ALIGN_128B       BIT(0)  /* Align pitches to 128 bytes */
-
-enum rcar_du_output {
-       RCAR_DU_OUTPUT_DPAD0,
-       RCAR_DU_OUTPUT_DPAD1,
-       RCAR_DU_OUTPUT_DSI0,
-       RCAR_DU_OUTPUT_DSI1,
-       RCAR_DU_OUTPUT_HDMI0,
-       RCAR_DU_OUTPUT_HDMI1,
-       RCAR_DU_OUTPUT_LVDS0,
-       RCAR_DU_OUTPUT_LVDS1,
-       RCAR_DU_OUTPUT_TCON,
-       RCAR_DU_OUTPUT_MAX,
-};
-
-/*
- * struct rcar_du_output_routing - Output routing specification
- * @possible_crtcs: bitmask of possible CRTCs for the output
- * @port: device tree port number corresponding to this output route
- *
- * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
- * specify the valid SoC outputs, which CRTCs can drive the output, and the type
- * of in-SoC encoder for the output.
- */
-struct rcar_du_output_routing {
-       unsigned int possible_crtcs;
-       unsigned int port;
-};
-
-/*
- * struct rcar_du_device_info - DU model-specific information
- * @gen: device generation (2 or 3)
- * @features: device features (RCAR_DU_FEATURE_*)
- * @quirks: device quirks (RCAR_DU_QUIRK_*)
- * @channels_mask: bit mask of available DU channels
- * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
- * @num_lvds: number of internal LVDS encoders
- * @num_rpf: number of RPFs in VSP
- * @dpll_mask: bit mask of DU channels equipped with a DPLL
- * @dsi_clk_mask: bitmask of channels that can use the DSI clock as dot clock
- * @lvds_clk_mask: bitmask of channels that can use the LVDS clock as dot clock
- */
-struct rcar_du_device_info {
-       unsigned int gen;
-       unsigned int features;
-       unsigned int quirks;
-       unsigned int channels_mask;
-       struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
-       unsigned int num_lvds;
-       unsigned int num_rpf;
-       unsigned int dpll_mask;
-       unsigned int dsi_clk_mask;
-       unsigned int lvds_clk_mask;
-};
-
-#define RCAR_DU_MAX_CRTCS              4
-#define RCAR_DU_MAX_GROUPS             DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
-#define RCAR_DU_MAX_VSPS               4
-#define RCAR_DU_MAX_LVDS               2
-#define RCAR_DU_MAX_DSI                        2
-
-struct rcar_du_device {
-       struct device *dev;
-       const struct rcar_du_device_info *info;
-
-       void __iomem *mmio;
-
-       struct drm_device ddev;
-
-       struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS];
-       unsigned int num_crtcs;
-
-       struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
-       struct platform_device *cmms[RCAR_DU_MAX_CRTCS];
-       struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
-       struct drm_bridge *lvds[RCAR_DU_MAX_LVDS];
-       struct drm_bridge *dsi[RCAR_DU_MAX_DSI];
-
-       struct {
-               struct drm_property *colorkey;
-       } props;
-
-       unsigned int dpad0_source;
-       unsigned int dpad1_source;
-       unsigned int vspd1_sink;
-};
-
-static inline struct rcar_du_device *to_rcar_du_device(struct drm_device *dev)
-{
-       return container_of(dev, struct rcar_du_device, ddev);
-}
-
-static inline bool rcar_du_has(struct rcar_du_device *rcdu,
-                              unsigned int feature)
-{
-       return rcdu->info->features & feature;
-}
-
-static inline bool rcar_du_needs(struct rcar_du_device *rcdu,
-                                unsigned int quirk)
-{
-       return rcdu->info->quirks & quirk;
-}
-
-static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
-{
-       return ioread32(rcdu->mmio + reg);
-}
-
-static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data)
-{
-       iowrite32(data, rcdu->mmio + reg);
-}
-
-const char *rcar_du_output_name(enum rcar_du_output output);
-
-#endif /* __RCAR_DU_DRV_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
deleted file mode 100644 (file)
index 7ecec7b..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit Encoder
- *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/export.h>
-#include <linux/of.h>
-
-#include <drm/drm_bridge.h>
-#include <drm/drm_bridge_connector.h>
-#include <drm/drm_panel.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_encoder.h"
-#include "rcar_lvds.h"
-
-/* -----------------------------------------------------------------------------
- * Encoder
- */
-
-static unsigned int rcar_du_encoder_count_ports(struct device_node *node)
-{
-       struct device_node *ports;
-       struct device_node *port;
-       unsigned int num_ports = 0;
-
-       ports = of_get_child_by_name(node, "ports");
-       if (!ports)
-               ports = of_node_get(node);
-
-       for_each_child_of_node(ports, port) {
-               if (of_node_name_eq(port, "port"))
-                       num_ports++;
-       }
-
-       of_node_put(ports);
-
-       return num_ports;
-}
-
-static const struct drm_encoder_funcs rcar_du_encoder_funcs = {
-};
-
-int rcar_du_encoder_init(struct rcar_du_device *rcdu,
-                        enum rcar_du_output output,
-                        struct device_node *enc_node)
-{
-       struct rcar_du_encoder *renc;
-       struct drm_connector *connector;
-       struct drm_bridge *bridge;
-       int ret;
-
-       /*
-        * Locate the DRM bridge from the DT node. For the DPAD outputs, if the
-        * DT node has a single port, assume that it describes a panel and
-        * create a panel bridge.
-        */
-       if ((output == RCAR_DU_OUTPUT_DPAD0 ||
-            output == RCAR_DU_OUTPUT_DPAD1) &&
-           rcar_du_encoder_count_ports(enc_node) == 1) {
-               struct drm_panel *panel = of_drm_find_panel(enc_node);
-
-               if (IS_ERR(panel))
-                       return PTR_ERR(panel);
-
-               bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
-                                                        DRM_MODE_CONNECTOR_DPI);
-               if (IS_ERR(bridge))
-                       return PTR_ERR(bridge);
-       } else {
-               bridge = of_drm_find_bridge(enc_node);
-               if (!bridge)
-                       return -EPROBE_DEFER;
-
-               if (output == RCAR_DU_OUTPUT_LVDS0 ||
-                   output == RCAR_DU_OUTPUT_LVDS1)
-                       rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
-
-               if (output == RCAR_DU_OUTPUT_DSI0 ||
-                   output == RCAR_DU_OUTPUT_DSI1)
-                       rcdu->dsi[output - RCAR_DU_OUTPUT_DSI0] = bridge;
-       }
-
-       /*
-        * Create and initialize the encoder. On Gen3, skip the LVDS1 output if
-        * the LVDS1 encoder is used as a companion for LVDS0 in dual-link
-        * mode, or any LVDS output if it isn't connected. The latter may happen
-        * on D3 or E3 as the LVDS encoders are needed to provide the pixel
-        * clock to the DU, even when the LVDS outputs are not used.
-        */
-       if (rcdu->info->gen >= 3) {
-               if (output == RCAR_DU_OUTPUT_LVDS1 &&
-                   rcar_lvds_dual_link(bridge))
-                       return -ENOLINK;
-
-               if ((output == RCAR_DU_OUTPUT_LVDS0 ||
-                    output == RCAR_DU_OUTPUT_LVDS1) &&
-                   !rcar_lvds_is_connected(bridge))
-                       return -ENOLINK;
-       }
-
-       dev_dbg(rcdu->dev, "initializing encoder %pOF for output %s\n",
-               enc_node, rcar_du_output_name(output));
-
-       renc = drmm_encoder_alloc(&rcdu->ddev, struct rcar_du_encoder, base,
-                                 &rcar_du_encoder_funcs, DRM_MODE_ENCODER_NONE,
-                                 NULL);
-       if (IS_ERR(renc))
-               return PTR_ERR(renc);
-
-       renc->output = output;
-
-       /* Attach the bridge to the encoder. */
-       ret = drm_bridge_attach(&renc->base, bridge, NULL,
-                               DRM_BRIDGE_ATTACH_NO_CONNECTOR);
-       if (ret) {
-               dev_err(rcdu->dev,
-                       "failed to attach bridge %pOF for output %s (%d)\n",
-                       bridge->of_node, rcar_du_output_name(output), ret);
-               return ret;
-       }
-
-       /* Create the connector for the chain of bridges. */
-       connector = drm_bridge_connector_init(&rcdu->ddev, &renc->base);
-       if (IS_ERR(connector)) {
-               dev_err(rcdu->dev,
-                       "failed to created connector for output %s (%ld)\n",
-                       rcar_du_output_name(output), PTR_ERR(connector));
-               return PTR_ERR(connector);
-       }
-
-       return drm_connector_attach_encoder(connector, &renc->base);
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
deleted file mode 100644 (file)
index e5ec8fb..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit Encoder
- *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_ENCODER_H__
-#define __RCAR_DU_ENCODER_H__
-
-#include <drm/drm_encoder.h>
-
-struct rcar_du_device;
-
-struct rcar_du_encoder {
-       struct drm_encoder base;
-       enum rcar_du_output output;
-};
-
-#define to_rcar_encoder(e) \
-       container_of(e, struct rcar_du_encoder, base)
-
-int rcar_du_encoder_init(struct rcar_du_device *rcdu,
-                        enum rcar_du_output output,
-                        struct device_node *enc_node);
-
-#endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
deleted file mode 100644 (file)
index 2ccd258..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit Channels Pair
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-/*
- * The R8A7779 DU is split in per-CRTC resources (scan-out engine, blending
- * unit, timings generator, ...) and device-global resources (start/stop
- * control, planes, ...) shared between the two CRTCs.
- *
- * The R8A7790 introduced a third CRTC with its own set of global resources.
- * This would be modeled as two separate DU device instances if it wasn't for
- * a handful or resources that are shared between the three CRTCs (mostly
- * related to input and output routing). For this reason the R8A7790 DU must be
- * modeled as a single device with three CRTCs, two sets of "semi-global"
- * resources, and a few device-global resources.
- *
- * The rcar_du_group object is a driver specific object, without any real
- * counterpart in the DU documentation, that models those semi-global resources.
- */
-
-#include <linux/clk.h>
-#include <linux/io.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_group.h"
-#include "rcar_du_regs.h"
-
-u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg)
-{
-       return rcar_du_read(rgrp->dev, rgrp->mmio_offset + reg);
-}
-
-void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data)
-{
-       rcar_du_write(rgrp->dev, rgrp->mmio_offset + reg, data);
-}
-
-static void rcar_du_group_setup_pins(struct rcar_du_group *rgrp)
-{
-       u32 defr6 = DEFR6_CODE;
-
-       if (rgrp->channels_mask & BIT(0))
-               defr6 |= DEFR6_ODPM02_DISP;
-
-       if (rgrp->channels_mask & BIT(1))
-               defr6 |= DEFR6_ODPM12_DISP;
-
-       rcar_du_group_write(rgrp, DEFR6, defr6);
-}
-
-static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-       u32 defr8 = DEFR8_CODE;
-
-       if (rcdu->info->gen < 3) {
-               defr8 |= DEFR8_DEFE8;
-
-               /*
-                * On Gen2 the DEFR8 register for the first group also controls
-                * RGB output routing to DPAD0 and VSPD1 routing to DU0/1/2 for
-                * DU instances that support it.
-                */
-               if (rgrp->index == 0) {
-                       defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
-                       if (rgrp->dev->vspd1_sink == 2)
-                               defr8 |= DEFR8_VSCS;
-               }
-       } else {
-               /*
-                * On Gen3 VSPD routing can't be configured, and DPAD routing
-                * is set in the group corresponding to the DPAD output (no Gen3
-                * SoC has multiple DPAD sources belonging to separate groups).
-                */
-               if (rgrp->index == rcdu->dpad0_source / 2)
-                       defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
-       }
-
-       rcar_du_group_write(rgrp, DEFR8, defr8);
-}
-
-static void rcar_du_group_setup_didsr(struct rcar_du_group *rgrp)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-       struct rcar_du_crtc *rcrtc;
-       unsigned int num_crtcs = 0;
-       unsigned int i;
-       u32 didsr;
-
-       /*
-        * Configure input dot clock routing with a hardcoded configuration. If
-        * the DU channel can use the LVDS encoder output clock as the dot
-        * clock, do so. Otherwise route DU_DOTCLKINn signal to DUn.
-        *
-        * Each channel can then select between the dot clock configured here
-        * and the clock provided by the CPG through the ESCR register.
-        */
-       if (rcdu->info->gen < 3 && rgrp->index == 0) {
-               /*
-                * On Gen2 a single register in the first group controls dot
-                * clock selection for all channels.
-                */
-               rcrtc = rcdu->crtcs;
-               num_crtcs = rcdu->num_crtcs;
-       } else if (rcdu->info->gen >= 3 && rgrp->num_crtcs > 1) {
-               /*
-                * On Gen3 dot clocks are setup through per-group registers,
-                * only available when the group has two channels.
-                */
-               rcrtc = &rcdu->crtcs[rgrp->index * 2];
-               num_crtcs = rgrp->num_crtcs;
-       }
-
-       if (!num_crtcs)
-               return;
-
-       didsr = DIDSR_CODE;
-       for (i = 0; i < num_crtcs; ++i, ++rcrtc) {
-               if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index))
-                       didsr |= DIDSR_LDCS_LVDS0(i)
-                             |  DIDSR_PDCS_CLK(i, 0);
-               else if (rcdu->info->dsi_clk_mask & BIT(rcrtc->index))
-                       didsr |= DIDSR_LDCS_DSI(i);
-               else
-                       didsr |= DIDSR_LDCS_DCLKIN(i)
-                             |  DIDSR_PDCS_CLK(i, 0);
-       }
-
-       rcar_du_group_write(rgrp, DIDSR, didsr);
-}
-
-static void rcar_du_group_setup(struct rcar_du_group *rgrp)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-       u32 defr7 = DEFR7_CODE;
-       u32 dorcr;
-
-       /* Enable extended features */
-       rcar_du_group_write(rgrp, DEFR, DEFR_CODE | DEFR_DEFE);
-       if (rcdu->info->gen < 3) {
-               rcar_du_group_write(rgrp, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
-               rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
-               rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE);
-       }
-       rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
-
-       if (rcdu->info->gen < 4)
-               rcar_du_group_setup_pins(rgrp);
-
-       if (rcdu->info->gen < 4) {
-               /*
-                * TODO: Handle routing of the DU output to CMM dynamically, as
-                * we should bypass CMM completely when no color management
-                * feature is used.
-                */
-               defr7 |= (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0) |
-                        (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
-               rcar_du_group_write(rgrp, DEFR7, defr7);
-       }
-
-       if (rcdu->info->gen >= 2) {
-               if (rcdu->info->gen < 4)
-                       rcar_du_group_setup_defr8(rgrp);
-               rcar_du_group_setup_didsr(rgrp);
-       }
-
-       if (rcdu->info->gen >= 3)
-               rcar_du_group_write(rgrp, DEFR10, DEFR10_CODE | DEFR10_DEFE10);
-
-       /*
-        * Use DS1PR and DS2PR to configure planes priorities and connects the
-        * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
-        *
-        * Groups that have a single channel have a hardcoded configuration. On
-        * Gen3 and newer, the documentation requires PG1T, DK1S and PG1D_DS1 to
-        * always be set in this case.
-        */
-       dorcr = DORCR_PG0D_DS0 | DORCR_DPRS;
-       if (rcdu->info->gen >= 3 && rgrp->num_crtcs == 1)
-               dorcr |= DORCR_PG1T | DORCR_DK1S | DORCR_PG1D_DS1;
-       rcar_du_group_write(rgrp, DORCR, dorcr);
-
-       /* Apply planes to CRTCs association. */
-       mutex_lock(&rgrp->lock);
-       rcar_du_group_write(rgrp, DPTSR, (rgrp->dptsr_planes << 16) |
-                           rgrp->dptsr_planes);
-       mutex_unlock(&rgrp->lock);
-}
-
-/*
- * rcar_du_group_get - Acquire a reference to the DU channels group
- *
- * Acquiring the first reference setups core registers. A reference must be held
- * before accessing any hardware registers.
- *
- * This function must be called with the DRM mode_config lock held.
- *
- * Return 0 in case of success or a negative error code otherwise.
- */
-int rcar_du_group_get(struct rcar_du_group *rgrp)
-{
-       if (rgrp->use_count)
-               goto done;
-
-       rcar_du_group_setup(rgrp);
-
-done:
-       rgrp->use_count++;
-       return 0;
-}
-
-/*
- * rcar_du_group_put - Release a reference to the DU
- *
- * This function must be called with the DRM mode_config lock held.
- */
-void rcar_du_group_put(struct rcar_du_group *rgrp)
-{
-       --rgrp->use_count;
-}
-
-static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-
-       /*
-        * Group start/stop is controlled by the DRES and DEN bits of DSYSR0
-        * for the first group and DSYSR2 for the second group. On most DU
-        * instances, this maps to the first CRTC of the group, and we can just
-        * use rcar_du_crtc_dsysr_clr_set() to access the correct DSYSR. On
-        * M3-N, however, DU2 doesn't exist, but DSYSR2 does. We thus need to
-        * access the register directly using group read/write.
-        */
-       if (rcdu->info->channels_mask & BIT(rgrp->index * 2)) {
-               struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2];
-
-               rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_DRES | DSYSR_DEN,
-                                          start ? DSYSR_DEN : DSYSR_DRES);
-       } else {
-               rcar_du_group_write(rgrp, DSYSR,
-                                   start ? DSYSR_DEN : DSYSR_DRES);
-       }
-}
-
-void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
-{
-       /*
-        * Many of the configuration bits are only updated when the display
-        * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some
-        * of those bits could be pre-configured, but others (especially the
-        * bits related to plane assignment to display timing controllers) need
-        * to be modified at runtime.
-        *
-        * Restart the display controller if a start is requested. Sorry for the
-        * flicker. It should be possible to move most of the "DRES-update" bits
-        * setup to driver initialization time and minimize the number of cases
-        * when the display controller will have to be restarted.
-        */
-       if (start) {
-               if (rgrp->used_crtcs++ != 0)
-                       __rcar_du_group_start_stop(rgrp, false);
-               __rcar_du_group_start_stop(rgrp, true);
-       } else {
-               if (--rgrp->used_crtcs == 0)
-                       __rcar_du_group_start_stop(rgrp, false);
-       }
-}
-
-void rcar_du_group_restart(struct rcar_du_group *rgrp)
-{
-       rgrp->need_restart = false;
-
-       __rcar_du_group_start_stop(rgrp, false);
-       __rcar_du_group_start_stop(rgrp, true);
-}
-
-int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu)
-{
-       struct rcar_du_group *rgrp;
-       struct rcar_du_crtc *crtc;
-       unsigned int index;
-       int ret;
-
-       if (rcdu->info->gen < 2)
-               return 0;
-
-       /*
-        * RGB output routing to DPAD0 and VSP1D routing to DU0/1/2 are
-        * configured in the DEFR8 register of the first group on Gen2 and the
-        * last group on Gen3. As this function can be called with the DU
-        * channels of the corresponding CRTCs disabled, we need to enable the
-        * group clock before accessing the register.
-        */
-       index = rcdu->info->gen < 3 ? 0 : DIV_ROUND_UP(rcdu->num_crtcs, 2) - 1;
-       rgrp = &rcdu->groups[index];
-       crtc = &rcdu->crtcs[index * 2];
-
-       ret = clk_prepare_enable(crtc->clock);
-       if (ret < 0)
-               return ret;
-
-       rcar_du_group_setup_defr8(rgrp);
-
-       clk_disable_unprepare(crtc->clock);
-
-       return 0;
-}
-
-static void rcar_du_group_set_dpad_levels(struct rcar_du_group *rgrp)
-{
-       static const u32 doflr_values[2] = {
-               DOFLR_HSYCFL0 | DOFLR_VSYCFL0 | DOFLR_ODDFL0 |
-               DOFLR_DISPFL0 | DOFLR_CDEFL0  | DOFLR_RGBFL0,
-               DOFLR_HSYCFL1 | DOFLR_VSYCFL1 | DOFLR_ODDFL1 |
-               DOFLR_DISPFL1 | DOFLR_CDEFL1  | DOFLR_RGBFL1,
-       };
-       static const u32 dpad_mask = BIT(RCAR_DU_OUTPUT_DPAD1)
-                                  | BIT(RCAR_DU_OUTPUT_DPAD0);
-       struct rcar_du_device *rcdu = rgrp->dev;
-       u32 doflr = DOFLR_CODE;
-       unsigned int i;
-
-       if (rcdu->info->gen < 2)
-               return;
-
-       /*
-        * The DPAD outputs can't be controlled directly. However, the parallel
-        * output of the DU channels routed to DPAD can be set to fixed levels
-        * through the DOFLR group register. Use this to turn the DPAD on or off
-        * by driving fixed low-level signals at the output of any DU channel
-        * not routed to a DPAD output. This doesn't affect the DU output
-        * signals going to other outputs, such as the internal LVDS and HDMI
-        * encoders.
-        */
-
-       for (i = 0; i < rgrp->num_crtcs; ++i) {
-               struct rcar_du_crtc_state *rstate;
-               struct rcar_du_crtc *rcrtc;
-
-               rcrtc = &rcdu->crtcs[rgrp->index * 2 + i];
-               rstate = to_rcar_crtc_state(rcrtc->crtc.state);
-
-               if (!(rstate->outputs & dpad_mask))
-                       doflr |= doflr_values[i];
-       }
-
-       rcar_du_group_write(rgrp, DOFLR, doflr);
-}
-
-int rcar_du_group_set_routing(struct rcar_du_group *rgrp)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-       u32 dorcr = rcar_du_group_read(rgrp, DORCR);
-
-       dorcr &= ~(DORCR_PG1T | DORCR_DK1S | DORCR_PG1D_MASK);
-
-       /*
-        * Set the DPAD1 pins sources. Select CRTC 0 if explicitly requested and
-        * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1
-        * by default.
-        */
-       if (rcdu->dpad1_source == rgrp->index * 2)
-               dorcr |= DORCR_PG1D_DS0;
-       else
-               dorcr |= DORCR_PG1T | DORCR_DK1S | DORCR_PG1D_DS1;
-
-       rcar_du_group_write(rgrp, DORCR, dorcr);
-
-       rcar_du_group_set_dpad_levels(rgrp);
-
-       return rcar_du_set_dpad0_vsp1_routing(rgrp->dev);
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h
deleted file mode 100644 (file)
index 55649ad..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit Planes and CRTCs Group
- *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_GROUP_H__
-#define __RCAR_DU_GROUP_H__
-
-#include <linux/mutex.h>
-
-#include "rcar_du_plane.h"
-
-struct rcar_du_device;
-
-/*
- * struct rcar_du_group - CRTCs and planes group
- * @dev: the DU device
- * @mmio_offset: registers offset in the device memory map
- * @index: group index
- * @channels_mask: bitmask of populated DU channels in this group
- * @cmms_mask: bitmask of available CMMs in this group
- * @num_crtcs: number of CRTCs in this group (1 or 2)
- * @use_count: number of users of the group (rcar_du_group_(get|put))
- * @used_crtcs: number of CRTCs currently in use
- * @lock: protects the dptsr_planes field and the DPTSR register
- * @dptsr_planes: bitmask of planes driven by dot-clock and timing generator 1
- * @num_planes: number of planes in the group
- * @planes: planes handled by the group
- * @need_restart: the group needs to be restarted due to a configuration change
- */
-struct rcar_du_group {
-       struct rcar_du_device *dev;
-       unsigned int mmio_offset;
-       unsigned int index;
-
-       unsigned int channels_mask;
-       unsigned int cmms_mask;
-       unsigned int num_crtcs;
-       unsigned int use_count;
-       unsigned int used_crtcs;
-
-       struct mutex lock;
-       unsigned int dptsr_planes;
-
-       unsigned int num_planes;
-       struct rcar_du_plane planes[RCAR_DU_NUM_KMS_PLANES];
-       bool need_restart;
-};
-
-u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg);
-void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data);
-
-int rcar_du_group_get(struct rcar_du_group *rgrp);
-void rcar_du_group_put(struct rcar_du_group *rgrp);
-void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start);
-void rcar_du_group_restart(struct rcar_du_group *rgrp);
-int rcar_du_group_set_routing(struct rcar_du_group *rgrp);
-
-int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu);
-
-#endif /* __RCAR_DU_GROUP_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
deleted file mode 100644 (file)
index adfb36b..0000000
+++ /dev/null
@@ -1,1006 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit Mode Setting
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_device.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_managed.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_vblank.h>
-
-#include <linux/device.h>
-#include <linux/dma-buf.h>
-#include <linux/of_graph.h>
-#include <linux/of_platform.h>
-#include <linux/wait.h>
-
-#include "rcar_du_crtc.h"
-#include "rcar_du_drv.h"
-#include "rcar_du_encoder.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_regs.h"
-#include "rcar_du_vsp.h"
-#include "rcar_du_writeback.h"
-
-/* -----------------------------------------------------------------------------
- * Format helpers
- */
-
-static const struct rcar_du_format_info rcar_du_format_infos[] = {
-       {
-               .fourcc = DRM_FORMAT_RGB565,
-               .v4l2 = V4L2_PIX_FMT_RGB565,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
-               .edf = PnDDCR4_EDF_NONE,
-       }, {
-               .fourcc = DRM_FORMAT_ARGB1555,
-               .v4l2 = V4L2_PIX_FMT_ARGB555,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
-               .edf = PnDDCR4_EDF_NONE,
-       }, {
-               .fourcc = DRM_FORMAT_XRGB1555,
-               .v4l2 = V4L2_PIX_FMT_XRGB555,
-               .bpp = 16,
-               .planes = 1,
-               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
-               .edf = PnDDCR4_EDF_NONE,
-       }, {
-               .fourcc = DRM_FORMAT_XRGB8888,
-               .v4l2 = V4L2_PIX_FMT_XBGR32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
-               .edf = PnDDCR4_EDF_RGB888,
-       }, {
-               .fourcc = DRM_FORMAT_ARGB8888,
-               .v4l2 = V4L2_PIX_FMT_ABGR32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
-               .edf = PnDDCR4_EDF_ARGB8888,
-       }, {
-               .fourcc = DRM_FORMAT_UYVY,
-               .v4l2 = V4L2_PIX_FMT_UYVY,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 2,
-               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
-               .edf = PnDDCR4_EDF_NONE,
-       }, {
-               .fourcc = DRM_FORMAT_YUYV,
-               .v4l2 = V4L2_PIX_FMT_YUYV,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 2,
-               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
-               .edf = PnDDCR4_EDF_NONE,
-       }, {
-               .fourcc = DRM_FORMAT_NV12,
-               .v4l2 = V4L2_PIX_FMT_NV12M,
-               .bpp = 12,
-               .planes = 2,
-               .hsub = 2,
-               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
-               .edf = PnDDCR4_EDF_NONE,
-       }, {
-               .fourcc = DRM_FORMAT_NV21,
-               .v4l2 = V4L2_PIX_FMT_NV21M,
-               .bpp = 12,
-               .planes = 2,
-               .hsub = 2,
-               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
-               .edf = PnDDCR4_EDF_NONE,
-       }, {
-               .fourcc = DRM_FORMAT_NV16,
-               .v4l2 = V4L2_PIX_FMT_NV16M,
-               .bpp = 16,
-               .planes = 2,
-               .hsub = 2,
-               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
-               .edf = PnDDCR4_EDF_NONE,
-       },
-       /*
-        * The following formats are not supported on Gen2 and thus have no
-        * associated .pnmr or .edf settings.
-        */
-       {
-               .fourcc = DRM_FORMAT_RGB332,
-               .v4l2 = V4L2_PIX_FMT_RGB332,
-               .bpp = 8,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_ARGB4444,
-               .v4l2 = V4L2_PIX_FMT_ARGB444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_XRGB4444,
-               .v4l2 = V4L2_PIX_FMT_XRGB444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBA4444,
-               .v4l2 = V4L2_PIX_FMT_RGBA444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBX4444,
-               .v4l2 = V4L2_PIX_FMT_RGBX444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_ABGR4444,
-               .v4l2 = V4L2_PIX_FMT_ABGR444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_XBGR4444,
-               .v4l2 = V4L2_PIX_FMT_XBGR444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_BGRA4444,
-               .v4l2 = V4L2_PIX_FMT_BGRA444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_BGRX4444,
-               .v4l2 = V4L2_PIX_FMT_BGRX444,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBA5551,
-               .v4l2 = V4L2_PIX_FMT_RGBA555,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBX5551,
-               .v4l2 = V4L2_PIX_FMT_RGBX555,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_ABGR1555,
-               .v4l2 = V4L2_PIX_FMT_ABGR555,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_XBGR1555,
-               .v4l2 = V4L2_PIX_FMT_XBGR555,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_BGRA5551,
-               .v4l2 = V4L2_PIX_FMT_BGRA555,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_BGRX5551,
-               .v4l2 = V4L2_PIX_FMT_BGRX555,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_BGR888,
-               .v4l2 = V4L2_PIX_FMT_RGB24,
-               .bpp = 24,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGB888,
-               .v4l2 = V4L2_PIX_FMT_BGR24,
-               .bpp = 24,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBA8888,
-               .v4l2 = V4L2_PIX_FMT_BGRA32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBX8888,
-               .v4l2 = V4L2_PIX_FMT_BGRX32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_ABGR8888,
-               .v4l2 = V4L2_PIX_FMT_RGBA32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_XBGR8888,
-               .v4l2 = V4L2_PIX_FMT_RGBX32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_BGRA8888,
-               .v4l2 = V4L2_PIX_FMT_ARGB32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_BGRX8888,
-               .v4l2 = V4L2_PIX_FMT_XRGB32,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBX1010102,
-               .v4l2 = V4L2_PIX_FMT_RGBX1010102,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_RGBA1010102,
-               .v4l2 = V4L2_PIX_FMT_RGBA1010102,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_ARGB2101010,
-               .v4l2 = V4L2_PIX_FMT_ARGB2101010,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_YVYU,
-               .v4l2 = V4L2_PIX_FMT_YVYU,
-               .bpp = 16,
-               .planes = 1,
-               .hsub = 2,
-       }, {
-               .fourcc = DRM_FORMAT_NV61,
-               .v4l2 = V4L2_PIX_FMT_NV61M,
-               .bpp = 16,
-               .planes = 2,
-               .hsub = 2,
-       }, {
-               .fourcc = DRM_FORMAT_YUV420,
-               .v4l2 = V4L2_PIX_FMT_YUV420M,
-               .bpp = 12,
-               .planes = 3,
-               .hsub = 2,
-       }, {
-               .fourcc = DRM_FORMAT_YVU420,
-               .v4l2 = V4L2_PIX_FMT_YVU420M,
-               .bpp = 12,
-               .planes = 3,
-               .hsub = 2,
-       }, {
-               .fourcc = DRM_FORMAT_YUV422,
-               .v4l2 = V4L2_PIX_FMT_YUV422M,
-               .bpp = 16,
-               .planes = 3,
-               .hsub = 2,
-       }, {
-               .fourcc = DRM_FORMAT_YVU422,
-               .v4l2 = V4L2_PIX_FMT_YVU422M,
-               .bpp = 16,
-               .planes = 3,
-               .hsub = 2,
-       }, {
-               .fourcc = DRM_FORMAT_YUV444,
-               .v4l2 = V4L2_PIX_FMT_YUV444M,
-               .bpp = 24,
-               .planes = 3,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_YVU444,
-               .v4l2 = V4L2_PIX_FMT_YVU444M,
-               .bpp = 24,
-               .planes = 3,
-               .hsub = 1,
-       }, {
-               .fourcc = DRM_FORMAT_Y210,
-               .v4l2 = V4L2_PIX_FMT_Y210,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 2,
-       }, {
-               .fourcc = DRM_FORMAT_Y212,
-               .v4l2 = V4L2_PIX_FMT_Y212,
-               .bpp = 32,
-               .planes = 1,
-               .hsub = 2,
-       },
-};
-
-const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
-{
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
-               if (rcar_du_format_infos[i].fourcc == fourcc)
-                       return &rcar_du_format_infos[i];
-       }
-
-       return NULL;
-}
-
-/* -----------------------------------------------------------------------------
- * Frame buffer
- */
-
-static const struct drm_gem_object_funcs rcar_du_gem_funcs = {
-       .free = drm_gem_dma_object_free,
-       .print_info = drm_gem_dma_object_print_info,
-       .get_sg_table = drm_gem_dma_object_get_sg_table,
-       .vmap = drm_gem_dma_object_vmap,
-       .mmap = drm_gem_dma_object_mmap,
-       .vm_ops = &drm_gem_dma_vm_ops,
-};
-
-struct drm_gem_object *rcar_du_gem_prime_import_sg_table(struct drm_device *dev,
-                               struct dma_buf_attachment *attach,
-                               struct sg_table *sgt)
-{
-       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
-       struct drm_gem_dma_object *dma_obj;
-       struct drm_gem_object *gem_obj;
-       int ret;
-
-       if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
-               return drm_gem_dma_prime_import_sg_table(dev, attach, sgt);
-
-       /* Create a DMA GEM buffer. */
-       dma_obj = kzalloc(sizeof(*dma_obj), GFP_KERNEL);
-       if (!dma_obj)
-               return ERR_PTR(-ENOMEM);
-
-       gem_obj = &dma_obj->base;
-       gem_obj->funcs = &rcar_du_gem_funcs;
-
-       drm_gem_private_object_init(dev, gem_obj, attach->dmabuf->size);
-       dma_obj->map_noncoherent = false;
-
-       ret = drm_gem_create_mmap_offset(gem_obj);
-       if (ret) {
-               drm_gem_object_release(gem_obj);
-               kfree(dma_obj);
-               return ERR_PTR(ret);
-       }
-
-       dma_obj->dma_addr = 0;
-       dma_obj->sgt = sgt;
-
-       return gem_obj;
-}
-
-int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
-                       struct drm_mode_create_dumb *args)
-{
-       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
-       unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
-       unsigned int align;
-
-       /*
-        * The R8A7779 DU requires a 16 pixels pitch alignment as documented,
-        * but the R8A7790 DU seems to require a 128 bytes pitch alignment.
-        */
-       if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B))
-               align = 128;
-       else
-               align = 16 * args->bpp / 8;
-
-       args->pitch = roundup(min_pitch, align);
-
-       return drm_gem_dma_dumb_create_internal(file, dev, args);
-}
-
-static struct drm_framebuffer *
-rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
-                 const struct drm_mode_fb_cmd2 *mode_cmd)
-{
-       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
-       const struct rcar_du_format_info *format;
-       unsigned int chroma_pitch;
-       unsigned int max_pitch;
-       unsigned int align;
-       unsigned int i;
-
-       format = rcar_du_format_info(mode_cmd->pixel_format);
-       if (format == NULL) {
-               dev_dbg(dev->dev, "unsupported pixel format %p4cc\n",
-                       &mode_cmd->pixel_format);
-               return ERR_PTR(-EINVAL);
-       }
-
-       if (rcdu->info->gen < 3) {
-               /*
-                * On Gen2 the DU limits the pitch to 4095 pixels and requires
-                * buffers to be aligned to a 16 pixels boundary (or 128 bytes
-                * on some platforms).
-                */
-               unsigned int bpp = format->planes == 1 ? format->bpp / 8 : 1;
-
-               max_pitch = 4095 * bpp;
-
-               if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B))
-                       align = 128;
-               else
-                       align = 16 * bpp;
-       } else {
-               /*
-                * On Gen3 the memory interface is handled by the VSP that
-                * limits the pitch to 65535 bytes and has no alignment
-                * constraint.
-                */
-               max_pitch = 65535;
-               align = 1;
-       }
-
-       if (mode_cmd->pitches[0] & (align - 1) ||
-           mode_cmd->pitches[0] > max_pitch) {
-               dev_dbg(dev->dev, "invalid pitch value %u\n",
-                       mode_cmd->pitches[0]);
-               return ERR_PTR(-EINVAL);
-       }
-
-       /*
-        * Calculate the chroma plane(s) pitch using the horizontal subsampling
-        * factor. For semi-planar formats, the U and V planes are combined, the
-        * pitch must thus be doubled.
-        */
-       chroma_pitch = mode_cmd->pitches[0] / format->hsub;
-       if (format->planes == 2)
-               chroma_pitch *= 2;
-
-       for (i = 1; i < format->planes; ++i) {
-               if (mode_cmd->pitches[i] != chroma_pitch) {
-                       dev_dbg(dev->dev,
-                               "luma and chroma pitches are not compatible\n");
-                       return ERR_PTR(-EINVAL);
-               }
-       }
-
-       return drm_gem_fb_create(dev, file_priv, mode_cmd);
-}
-
-/* -----------------------------------------------------------------------------
- * Atomic Check and Update
- */
-
-static int rcar_du_atomic_check(struct drm_device *dev,
-                               struct drm_atomic_state *state)
-{
-       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
-       int ret;
-
-       ret = drm_atomic_helper_check(dev, state);
-       if (ret)
-               return ret;
-
-       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
-               return 0;
-
-       return rcar_du_atomic_check_planes(dev, state);
-}
-
-static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state)
-{
-       struct drm_device *dev = old_state->dev;
-       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
-       struct drm_crtc_state *crtc_state;
-       struct drm_crtc *crtc;
-       unsigned int i;
-
-       /*
-        * Store RGB routing to DPAD0 and DPAD1, the hardware will be configured
-        * when starting the CRTCs.
-        */
-       rcdu->dpad1_source = -1;
-
-       for_each_new_crtc_in_state(old_state, crtc, crtc_state, i) {
-               struct rcar_du_crtc_state *rcrtc_state =
-                       to_rcar_crtc_state(crtc_state);
-               struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-
-               if (rcrtc_state->outputs & BIT(RCAR_DU_OUTPUT_DPAD0))
-                       rcdu->dpad0_source = rcrtc->index;
-
-               if (rcrtc_state->outputs & BIT(RCAR_DU_OUTPUT_DPAD1))
-                       rcdu->dpad1_source = rcrtc->index;
-       }
-
-       /* Apply the atomic update. */
-       drm_atomic_helper_commit_modeset_disables(dev, old_state);
-       drm_atomic_helper_commit_planes(dev, old_state,
-                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
-       drm_atomic_helper_commit_modeset_enables(dev, old_state);
-
-       drm_atomic_helper_commit_hw_done(old_state);
-       drm_atomic_helper_wait_for_flip_done(dev, old_state);
-
-       drm_atomic_helper_cleanup_planes(dev, old_state);
-}
-
-/* -----------------------------------------------------------------------------
- * Initialization
- */
-
-static const struct drm_mode_config_helper_funcs rcar_du_mode_config_helper = {
-       .atomic_commit_tail = rcar_du_atomic_commit_tail,
-};
-
-static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
-       .fb_create = rcar_du_fb_create,
-       .atomic_check = rcar_du_atomic_check,
-       .atomic_commit = drm_atomic_helper_commit,
-};
-
-static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
-                                    enum rcar_du_output output,
-                                    struct of_endpoint *ep)
-{
-       struct device_node *entity;
-       int ret;
-
-       /* Locate the connected entity and initialize the encoder. */
-       entity = of_graph_get_remote_port_parent(ep->local_node);
-       if (!entity) {
-               dev_dbg(rcdu->dev, "unconnected endpoint %pOF, skipping\n",
-                       ep->local_node);
-               return -ENODEV;
-       }
-
-       if (!of_device_is_available(entity)) {
-               dev_dbg(rcdu->dev,
-                       "connected entity %pOF is disabled, skipping\n",
-                       entity);
-               of_node_put(entity);
-               return -ENODEV;
-       }
-
-       ret = rcar_du_encoder_init(rcdu, output, entity);
-       if (ret && ret != -EPROBE_DEFER && ret != -ENOLINK)
-               dev_warn(rcdu->dev,
-                        "failed to initialize encoder %pOF on output %s (%d), skipping\n",
-                        entity, rcar_du_output_name(output), ret);
-
-       of_node_put(entity);
-
-       return ret;
-}
-
-static int rcar_du_encoders_init(struct rcar_du_device *rcdu)
-{
-       struct device_node *np = rcdu->dev->of_node;
-       struct device_node *ep_node;
-       unsigned int num_encoders = 0;
-
-       /*
-        * Iterate over the endpoints and create one encoder for each output
-        * pipeline.
-        */
-       for_each_endpoint_of_node(np, ep_node) {
-               enum rcar_du_output output;
-               struct of_endpoint ep;
-               unsigned int i;
-               int ret;
-
-               ret = of_graph_parse_endpoint(ep_node, &ep);
-               if (ret < 0) {
-                       of_node_put(ep_node);
-                       return ret;
-               }
-
-               /* Find the output route corresponding to the port number. */
-               for (i = 0; i < RCAR_DU_OUTPUT_MAX; ++i) {
-                       if (rcdu->info->routes[i].possible_crtcs &&
-                           rcdu->info->routes[i].port == ep.port) {
-                               output = i;
-                               break;
-                       }
-               }
-
-               if (i == RCAR_DU_OUTPUT_MAX) {
-                       dev_warn(rcdu->dev,
-                                "port %u references unexisting output, skipping\n",
-                                ep.port);
-                       continue;
-               }
-
-               /* Process the output pipeline. */
-               ret = rcar_du_encoders_init_one(rcdu, output, &ep);
-               if (ret < 0) {
-                       if (ret == -EPROBE_DEFER) {
-                               of_node_put(ep_node);
-                               return ret;
-                       }
-
-                       continue;
-               }
-
-               num_encoders++;
-       }
-
-       return num_encoders;
-}
-
-static int rcar_du_properties_init(struct rcar_du_device *rcdu)
-{
-       /*
-        * The color key is expressed as an RGB888 triplet stored in a 32-bit
-        * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
-        * or enable source color keying (1).
-        */
-       rcdu->props.colorkey =
-               drm_property_create_range(&rcdu->ddev, 0, "colorkey",
-                                         0, 0x01ffffff);
-       if (rcdu->props.colorkey == NULL)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static int rcar_du_vsps_init(struct rcar_du_device *rcdu)
-{
-       const struct device_node *np = rcdu->dev->of_node;
-       const char *vsps_prop_name = "renesas,vsps";
-       struct of_phandle_args args;
-       struct {
-               struct device_node *np;
-               unsigned int crtcs_mask;
-       } vsps[RCAR_DU_MAX_VSPS] = { { NULL, }, };
-       unsigned int vsps_count = 0;
-       unsigned int cells;
-       unsigned int i;
-       int ret;
-
-       /*
-        * First parse the DT vsps property to populate the list of VSPs. Each
-        * entry contains a pointer to the VSP DT node and a bitmask of the
-        * connected DU CRTCs.
-        */
-       ret = of_property_count_u32_elems(np, vsps_prop_name);
-       if (ret < 0) {
-               /* Backward compatibility with old DTBs. */
-               vsps_prop_name = "vsps";
-               ret = of_property_count_u32_elems(np, vsps_prop_name);
-       }
-       cells = ret / rcdu->num_crtcs - 1;
-       if (cells > 1)
-               return -EINVAL;
-
-       for (i = 0; i < rcdu->num_crtcs; ++i) {
-               unsigned int j;
-
-               ret = of_parse_phandle_with_fixed_args(np, vsps_prop_name,
-                                                      cells, i, &args);
-               if (ret < 0)
-                       goto error;
-
-               /*
-                * Add the VSP to the list or update the corresponding existing
-                * entry if the VSP has already been added.
-                */
-               for (j = 0; j < vsps_count; ++j) {
-                       if (vsps[j].np == args.np)
-                               break;
-               }
-
-               if (j < vsps_count)
-                       of_node_put(args.np);
-               else
-                       vsps[vsps_count++].np = args.np;
-
-               vsps[j].crtcs_mask |= BIT(i);
-
-               /*
-                * Store the VSP pointer and pipe index in the CRTC. If the
-                * second cell of the 'renesas,vsps' specifier isn't present,
-                * default to 0 to remain compatible with older DT bindings.
-                */
-               rcdu->crtcs[i].vsp = &rcdu->vsps[j];
-               rcdu->crtcs[i].vsp_pipe = cells >= 1 ? args.args[0] : 0;
-       }
-
-       /*
-        * Then initialize all the VSPs from the node pointers and CRTCs bitmask
-        * computed previously.
-        */
-       for (i = 0; i < vsps_count; ++i) {
-               struct rcar_du_vsp *vsp = &rcdu->vsps[i];
-
-               vsp->index = i;
-               vsp->dev = rcdu;
-
-               ret = rcar_du_vsp_init(vsp, vsps[i].np, vsps[i].crtcs_mask);
-               if (ret < 0)
-                       goto error;
-       }
-
-       return 0;
-
-error:
-       for (i = 0; i < ARRAY_SIZE(vsps); ++i)
-               of_node_put(vsps[i].np);
-
-       return ret;
-}
-
-static int rcar_du_cmm_init(struct rcar_du_device *rcdu)
-{
-       const struct device_node *np = rcdu->dev->of_node;
-       unsigned int i;
-       int cells;
-
-       cells = of_property_count_u32_elems(np, "renesas,cmms");
-       if (cells == -EINVAL)
-               return 0;
-
-       if (cells > rcdu->num_crtcs) {
-               dev_err(rcdu->dev,
-                       "Invalid number of entries in 'renesas,cmms'\n");
-               return -EINVAL;
-       }
-
-       for (i = 0; i < cells; ++i) {
-               struct platform_device *pdev;
-               struct device_link *link;
-               struct device_node *cmm;
-               int ret;
-
-               cmm = of_parse_phandle(np, "renesas,cmms", i);
-               if (!cmm) {
-                       dev_err(rcdu->dev,
-                               "Failed to parse 'renesas,cmms' property\n");
-                       return -EINVAL;
-               }
-
-               if (!of_device_is_available(cmm)) {
-                       /* It's fine to have a phandle to a non-enabled CMM. */
-                       of_node_put(cmm);
-                       continue;
-               }
-
-               pdev = of_find_device_by_node(cmm);
-               if (!pdev) {
-                       dev_err(rcdu->dev, "No device found for CMM%u\n", i);
-                       of_node_put(cmm);
-                       return -EINVAL;
-               }
-
-               of_node_put(cmm);
-
-               /*
-                * -ENODEV is used to report that the CMM config option is
-                * disabled: return 0 and let the DU continue probing.
-                */
-               ret = rcar_cmm_init(pdev);
-               if (ret) {
-                       platform_device_put(pdev);
-                       return ret == -ENODEV ? 0 : ret;
-               }
-
-               rcdu->cmms[i] = pdev;
-
-               /*
-                * Enforce suspend/resume ordering by making the CMM a provider
-                * of the DU: CMM is suspended after and resumed before the DU.
-                */
-               link = device_link_add(rcdu->dev, &pdev->dev, DL_FLAG_STATELESS);
-               if (!link) {
-                       dev_err(rcdu->dev,
-                               "Failed to create device link to CMM%u\n", i);
-                       return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-
-static void rcar_du_modeset_cleanup(struct drm_device *dev, void *res)
-{
-       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(rcdu->cmms); ++i)
-               platform_device_put(rcdu->cmms[i]);
-}
-
-int rcar_du_modeset_init(struct rcar_du_device *rcdu)
-{
-       static const unsigned int mmio_offsets[] = {
-               DU0_REG_OFFSET, DU2_REG_OFFSET
-       };
-
-       struct drm_device *dev = &rcdu->ddev;
-       struct drm_encoder *encoder;
-       unsigned int dpad0_sources;
-       unsigned int num_encoders;
-       unsigned int num_groups;
-       unsigned int swindex;
-       unsigned int hwindex;
-       unsigned int i;
-       int ret;
-
-       ret = drmm_mode_config_init(dev);
-       if (ret)
-               return ret;
-
-       ret = drmm_add_action(&rcdu->ddev, rcar_du_modeset_cleanup, NULL);
-       if (ret)
-               return ret;
-
-       dev->mode_config.min_width = 0;
-       dev->mode_config.min_height = 0;
-       dev->mode_config.normalize_zpos = true;
-       dev->mode_config.funcs = &rcar_du_mode_config_funcs;
-       dev->mode_config.helper_private = &rcar_du_mode_config_helper;
-
-       if (rcdu->info->gen < 3) {
-               dev->mode_config.max_width = 4095;
-               dev->mode_config.max_height = 2047;
-       } else {
-               /*
-                * The Gen3 DU uses the VSP1 for memory access, and is limited
-                * to frame sizes of 8190x8190.
-                */
-               dev->mode_config.max_width = 8190;
-               dev->mode_config.max_height = 8190;
-       }
-
-       rcdu->num_crtcs = hweight8(rcdu->info->channels_mask);
-
-       ret = rcar_du_properties_init(rcdu);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * Initialize vertical blanking interrupts handling. Start with vblank
-        * disabled for all CRTCs.
-        */
-       ret = drm_vblank_init(dev, rcdu->num_crtcs);
-       if (ret < 0)
-               return ret;
-
-       /* Initialize the groups. */
-       num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2);
-
-       for (i = 0; i < num_groups; ++i) {
-               struct rcar_du_group *rgrp = &rcdu->groups[i];
-
-               mutex_init(&rgrp->lock);
-
-               rgrp->dev = rcdu;
-               rgrp->mmio_offset = mmio_offsets[i];
-               rgrp->index = i;
-               /* Extract the channel mask for this group only. */
-               rgrp->channels_mask = (rcdu->info->channels_mask >> (2 * i))
-                                  & GENMASK(1, 0);
-               rgrp->num_crtcs = hweight8(rgrp->channels_mask);
-
-               /*
-                * If we have more than one CRTCs in this group pre-associate
-                * the low-order planes with CRTC 0 and the high-order planes
-                * with CRTC 1 to minimize flicker occurring when the
-                * association is changed.
-                */
-               rgrp->dptsr_planes = rgrp->num_crtcs > 1
-                                  ? (rcdu->info->gen >= 3 ? 0x04 : 0xf0)
-                                  : 0;
-
-               if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
-                       ret = rcar_du_planes_init(rgrp);
-                       if (ret < 0)
-                               return ret;
-               }
-       }
-
-       /* Initialize the compositors. */
-       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
-               ret = rcar_du_vsps_init(rcdu);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* Initialize the Color Management Modules. */
-       ret = rcar_du_cmm_init(rcdu);
-       if (ret)
-               return ret;
-
-       /* Create the CRTCs. */
-       for (swindex = 0, hwindex = 0; swindex < rcdu->num_crtcs; ++hwindex) {
-               struct rcar_du_group *rgrp;
-
-               /* Skip unpopulated DU channels. */
-               if (!(rcdu->info->channels_mask & BIT(hwindex)))
-                       continue;
-
-               rgrp = &rcdu->groups[hwindex / 2];
-
-               ret = rcar_du_crtc_create(rgrp, swindex++, hwindex);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* Initialize the encoders. */
-       ret = rcar_du_encoders_init(rcdu);
-       if (ret < 0)
-               return ret;
-
-       if (ret == 0) {
-               dev_err(rcdu->dev, "error: no encoder could be initialized\n");
-               return -EINVAL;
-       }
-
-       num_encoders = ret;
-
-       /*
-        * Set the possible CRTCs and possible clones. There's always at least
-        * one way for all encoders to clone each other, set all bits in the
-        * possible clones field.
-        */
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
-               const struct rcar_du_output_routing *route =
-                       &rcdu->info->routes[renc->output];
-
-               encoder->possible_crtcs = route->possible_crtcs;
-               encoder->possible_clones = (1 << num_encoders) - 1;
-       }
-
-       /* Create the writeback connectors. */
-       if (rcdu->info->gen >= 3) {
-               for (i = 0; i < rcdu->num_crtcs; ++i) {
-                       struct rcar_du_crtc *rcrtc = &rcdu->crtcs[i];
-
-                       ret = rcar_du_writeback_init(rcdu, rcrtc);
-                       if (ret < 0)
-                               return ret;
-               }
-       }
-
-       /*
-        * Initialize the default DPAD0 source to the index of the first DU
-        * channel that can be connected to DPAD0. The exact value doesn't
-        * matter as it should be overwritten by mode setting for the RGB
-        * output, but it is nonetheless required to ensure a valid initial
-        * hardware configuration on Gen3 where DU0 can't always be connected to
-        * DPAD0.
-        */
-       dpad0_sources = rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs;
-       rcdu->dpad0_source = ffs(dpad0_sources) - 1;
-
-       drm_mode_config_reset(dev);
-
-       drm_kms_helper_poll_init(dev);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
deleted file mode 100644 (file)
index f31afee..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit Mode Setting
- *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_KMS_H__
-#define __RCAR_DU_KMS_H__
-
-#include <linux/types.h>
-
-struct dma_buf_attachment;
-struct drm_file;
-struct drm_device;
-struct drm_gem_object;
-struct drm_mode_create_dumb;
-struct rcar_du_device;
-struct sg_table;
-
-struct rcar_du_format_info {
-       u32 fourcc;
-       u32 v4l2;
-       unsigned int bpp;
-       unsigned int planes;
-       unsigned int hsub;
-       unsigned int pnmr;
-       unsigned int edf;
-};
-
-const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
-
-int rcar_du_modeset_init(struct rcar_du_device *rcdu);
-
-int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
-                       struct drm_mode_create_dumb *args);
-
-struct drm_gem_object *rcar_du_gem_prime_import_sg_table(struct drm_device *dev,
-                               struct dma_buf_attachment *attach,
-                               struct sg_table *sgt);
-
-#endif /* __RCAR_DU_KMS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
deleted file mode 100644 (file)
index d759e01..0000000
+++ /dev/null
@@ -1,831 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit Planes
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_blend.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_device.h>
-#include <drm/drm_fb_dma_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_dma_helper.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_group.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_plane.h"
-#include "rcar_du_regs.h"
-
-/* -----------------------------------------------------------------------------
- * Atomic hardware plane allocator
- *
- * The hardware plane allocator is solely based on the atomic plane states
- * without keeping any external state to avoid races between .atomic_check()
- * and .atomic_commit().
- *
- * The core idea is to avoid using a free planes bitmask that would need to be
- * shared between check and commit handlers with a collective knowledge based on
- * the allocated hardware plane(s) for each KMS plane. The allocator then loops
- * over all plane states to compute the free planes bitmask, allocates hardware
- * planes based on that bitmask, and stores the result back in the plane states.
- *
- * For this to work we need to access the current state of planes not touched by
- * the atomic update. To ensure that it won't be modified, we need to lock all
- * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
- * updates from .atomic_check() up to completion (when swapping the states if
- * the check step has succeeded) or rollback (when freeing the states if the
- * check step has failed).
- *
- * Allocation is performed in the .atomic_check() handler and applied
- * automatically when the core swaps the old and new states.
- */
-
-static bool rcar_du_plane_needs_realloc(
-                               const struct rcar_du_plane_state *old_state,
-                               const struct rcar_du_plane_state *new_state)
-{
-       /*
-        * Lowering the number of planes doesn't strictly require reallocation
-        * as the extra hardware plane will be freed when committing, but doing
-        * so could lead to more fragmentation.
-        */
-       if (!old_state->format ||
-           old_state->format->planes != new_state->format->planes)
-               return true;
-
-       /* Reallocate hardware planes if the source has changed. */
-       if (old_state->source != new_state->source)
-               return true;
-
-       return false;
-}
-
-static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
-{
-       unsigned int mask;
-
-       if (state->hwindex == -1)
-               return 0;
-
-       mask = 1 << state->hwindex;
-       if (state->format->planes == 2)
-               mask |= 1 << ((state->hwindex + 1) % 8);
-
-       return mask;
-}
-
-/*
- * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
- * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
- * DU0/1 plane 1.
- *
- * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
- * and allocate planes in reverse index order otherwise to ensure maximum
- * availability of planes 0 and 1.
- *
- * The caller is responsible for ensuring that the requested source is
- * compatible with the DU revision.
- */
-static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
-                                struct rcar_du_plane_state *state,
-                                unsigned int free)
-{
-       unsigned int num_planes = state->format->planes;
-       int fixed = -1;
-       int i;
-
-       if (state->source == RCAR_DU_PLANE_VSPD0) {
-               /* VSPD0 feeds plane 0 on DU0/1. */
-               if (plane->group->index != 0)
-                       return -EINVAL;
-
-               fixed = 0;
-       } else if (state->source == RCAR_DU_PLANE_VSPD1) {
-               /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
-               fixed = plane->group->index == 0 ? 1 : 0;
-       }
-
-       if (fixed >= 0)
-               return free & (1 << fixed) ? fixed : -EBUSY;
-
-       for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
-               if (!(free & (1 << i)))
-                       continue;
-
-               if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
-                       break;
-       }
-
-       return i < 0 ? -EBUSY : i;
-}
-
-int rcar_du_atomic_check_planes(struct drm_device *dev,
-                               struct drm_atomic_state *state)
-{
-       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
-       unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
-       unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
-       bool needs_realloc = false;
-       unsigned int groups = 0;
-       unsigned int i;
-       struct drm_plane *drm_plane;
-       struct drm_plane_state *old_drm_plane_state;
-       struct drm_plane_state *new_drm_plane_state;
-
-       /* Check if hardware planes need to be reallocated. */
-       for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
-                                      new_drm_plane_state, i) {
-               struct rcar_du_plane_state *old_plane_state;
-               struct rcar_du_plane_state *new_plane_state;
-               struct rcar_du_plane *plane;
-               unsigned int index;
-
-               plane = to_rcar_plane(drm_plane);
-               old_plane_state = to_rcar_plane_state(old_drm_plane_state);
-               new_plane_state = to_rcar_plane_state(new_drm_plane_state);
-
-               dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__,
-                       plane->group->index, plane - plane->group->planes);
-
-               /*
-                * If the plane is being disabled we don't need to go through
-                * the full reallocation procedure. Just mark the hardware
-                * plane(s) as freed.
-                */
-               if (!new_plane_state->format) {
-                       dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
-                               __func__);
-                       index = plane - plane->group->planes;
-                       group_freed_planes[plane->group->index] |= 1 << index;
-                       new_plane_state->hwindex = -1;
-                       continue;
-               }
-
-               /*
-                * If the plane needs to be reallocated mark it as such, and
-                * mark the hardware plane(s) as free.
-                */
-               if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) {
-                       dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
-                               __func__);
-                       groups |= 1 << plane->group->index;
-                       needs_realloc = true;
-
-                       index = plane - plane->group->planes;
-                       group_freed_planes[plane->group->index] |= 1 << index;
-                       new_plane_state->hwindex = -1;
-               }
-       }
-
-       if (!needs_realloc)
-               return 0;
-
-       /*
-        * Grab all plane states for the groups that need reallocation to ensure
-        * locking and avoid racy updates. This serializes the update operation,
-        * but there's not much we can do about it as that's the hardware
-        * design.
-        *
-        * Compute the used planes mask for each group at the same time to avoid
-        * looping over the planes separately later.
-        */
-       while (groups) {
-               unsigned int index = ffs(groups) - 1;
-               struct rcar_du_group *group = &rcdu->groups[index];
-               unsigned int used_planes = 0;
-
-               dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
-                       __func__, index);
-
-               for (i = 0; i < group->num_planes; ++i) {
-                       struct rcar_du_plane *plane = &group->planes[i];
-                       struct rcar_du_plane_state *new_plane_state;
-                       struct drm_plane_state *s;
-
-                       s = drm_atomic_get_plane_state(state, &plane->plane);
-                       if (IS_ERR(s))
-                               return PTR_ERR(s);
-
-                       /*
-                        * If the plane has been freed in the above loop its
-                        * hardware planes must not be added to the used planes
-                        * bitmask. However, the current state doesn't reflect
-                        * the free state yet, as we've modified the new state
-                        * above. Use the local freed planes list to check for
-                        * that condition instead.
-                        */
-                       if (group_freed_planes[index] & (1 << i)) {
-                               dev_dbg(rcdu->dev,
-                                       "%s: plane (%u,%tu) has been freed, skipping\n",
-                                       __func__, plane->group->index,
-                                       plane - plane->group->planes);
-                               continue;
-                       }
-
-                       new_plane_state = to_rcar_plane_state(s);
-                       used_planes |= rcar_du_plane_hwmask(new_plane_state);
-
-                       dev_dbg(rcdu->dev,
-                               "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n",
-                               __func__, plane->group->index,
-                               plane - plane->group->planes,
-                               new_plane_state->format ?
-                               new_plane_state->format->planes : 0,
-                               new_plane_state->hwindex);
-               }
-
-               group_free_planes[index] = 0xff & ~used_planes;
-               groups &= ~(1 << index);
-
-               dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
-                       __func__, index, group_free_planes[index]);
-       }
-
-       /* Reallocate hardware planes for each plane that needs it. */
-       for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
-                                      new_drm_plane_state, i) {
-               struct rcar_du_plane_state *old_plane_state;
-               struct rcar_du_plane_state *new_plane_state;
-               struct rcar_du_plane *plane;
-               unsigned int crtc_planes;
-               unsigned int free;
-               int idx;
-
-               plane = to_rcar_plane(drm_plane);
-               old_plane_state = to_rcar_plane_state(old_drm_plane_state);
-               new_plane_state = to_rcar_plane_state(new_drm_plane_state);
-
-               dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__,
-                       plane->group->index, plane - plane->group->planes);
-
-               /*
-                * Skip planes that are being disabled or don't need to be
-                * reallocated.
-                */
-               if (!new_plane_state->format ||
-                   !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state))
-                       continue;
-
-               /*
-                * Try to allocate the plane from the free planes currently
-                * associated with the target CRTC to avoid restarting the CRTC
-                * group and thus minimize flicker. If it fails fall back to
-                * allocating from all free planes.
-                */
-               crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2
-                           ? plane->group->dptsr_planes
-                           : ~plane->group->dptsr_planes;
-               free = group_free_planes[plane->group->index];
-
-               idx = rcar_du_plane_hwalloc(plane, new_plane_state,
-                                           free & crtc_planes);
-               if (idx < 0)
-                       idx = rcar_du_plane_hwalloc(plane, new_plane_state,
-                                                   free);
-               if (idx < 0) {
-                       dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
-                               __func__);
-                       return idx;
-               }
-
-               dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
-                       __func__, new_plane_state->format->planes, idx);
-
-               new_plane_state->hwindex = idx;
-
-               group_free_planes[plane->group->index] &=
-                       ~rcar_du_plane_hwmask(new_plane_state);
-
-               dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
-                       __func__, plane->group->index,
-                       group_free_planes[plane->group->index]);
-       }
-
-       return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Plane Setup
- */
-
-#define RCAR_DU_COLORKEY_NONE          (0 << 24)
-#define RCAR_DU_COLORKEY_SOURCE                (1 << 24)
-#define RCAR_DU_COLORKEY_MASK          (1 << 24)
-
-static void rcar_du_plane_write(struct rcar_du_group *rgrp,
-                               unsigned int index, u32 reg, u32 data)
-{
-       rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
-                     data);
-}
-
-static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp,
-                                       const struct rcar_du_plane_state *state)
-{
-       unsigned int src_x = state->state.src.x1 >> 16;
-       unsigned int src_y = state->state.src.y1 >> 16;
-       unsigned int index = state->hwindex;
-       unsigned int pitch;
-       bool interlaced;
-       u32 dma[2];
-
-       interlaced = state->state.crtc->state->adjusted_mode.flags
-                  & DRM_MODE_FLAG_INTERLACE;
-
-       if (state->source == RCAR_DU_PLANE_MEMORY) {
-               struct drm_framebuffer *fb = state->state.fb;
-               struct drm_gem_dma_object *gem;
-               unsigned int i;
-
-               if (state->format->planes == 2)
-                       pitch = fb->pitches[0];
-               else
-                       pitch = fb->pitches[0] * 8 / state->format->bpp;
-
-               for (i = 0; i < state->format->planes; ++i) {
-                       gem = drm_fb_dma_get_gem_obj(fb, i);
-                       dma[i] = gem->dma_addr + fb->offsets[i];
-               }
-       } else {
-               pitch = drm_rect_width(&state->state.src) >> 16;
-               dma[0] = 0;
-               dma[1] = 0;
-       }
-
-       /*
-        * Memory pitch (expressed in pixels). Must be doubled for interlaced
-        * operation with 32bpp formats.
-        */
-       rcar_du_plane_write(rgrp, index, PnMWR,
-                           (interlaced && state->format->bpp == 32) ?
-                           pitch * 2 : pitch);
-
-       /*
-        * The Y position is expressed in raster line units and must be doubled
-        * for 32bpp formats, according to the R8A7790 datasheet. No mention of
-        * doubling the Y position is found in the R8A7779 datasheet, but the
-        * rule seems to apply there as well.
-        *
-        * Despite not being documented, doubling seem not to be needed when
-        * operating in interlaced mode.
-        *
-        * Similarly, for the second plane, NV12 and NV21 formats seem to
-        * require a halved Y position value, in both progressive and interlaced
-        * modes.
-        */
-       rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
-       rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
-                           (!interlaced && state->format->bpp == 32 ? 2 : 1));
-
-       rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);
-
-       if (state->format->planes == 2) {
-               index = (index + 1) % 8;
-
-               rcar_du_plane_write(rgrp, index, PnMWR, pitch);
-
-               rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
-               rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
-                                   (state->format->bpp == 16 ? 2 : 1) / 2);
-
-               rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);
-       }
-}
-
-static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,
-                                    unsigned int index,
-                                    const struct rcar_du_plane_state *state)
-{
-       u32 colorkey;
-       u32 pnmr;
-
-       /*
-        * The PnALPHAR register controls alpha-blending in 16bpp formats
-        * (ARGB1555 and XRGB1555).
-        *
-        * For ARGB, set the alpha value to 0, and enable alpha-blending when
-        * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
-        *
-        * For XRGB, set the alpha value to the plane-wide alpha value and
-        * enable alpha-blending regardless of the X bit value.
-        */
-       if (state->format->fourcc != DRM_FORMAT_XRGB1555)
-               rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
-       else
-               rcar_du_plane_write(rgrp, index, PnALPHAR,
-                                   PnALPHAR_ABIT_X | state->state.alpha >> 8);
-
-       pnmr = PnMR_BM_MD | state->format->pnmr;
-
-       /*
-        * Disable color keying when requested. YUV formats have the
-        * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
-        * automatically.
-        */
-       if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
-               pnmr |= PnMR_SPIM_TP_OFF;
-
-       /* For packed YUV formats we need to select the U/V order. */
-       if (state->format->fourcc == DRM_FORMAT_YUYV)
-               pnmr |= PnMR_YCDF_YUYV;
-
-       rcar_du_plane_write(rgrp, index, PnMR, pnmr);
-
-       switch (state->format->fourcc) {
-       case DRM_FORMAT_RGB565:
-               colorkey = ((state->colorkey & 0xf80000) >> 8)
-                        | ((state->colorkey & 0x00fc00) >> 5)
-                        | ((state->colorkey & 0x0000f8) >> 3);
-               rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
-               break;
-
-       case DRM_FORMAT_ARGB1555:
-       case DRM_FORMAT_XRGB1555:
-               colorkey = ((state->colorkey & 0xf80000) >> 9)
-                        | ((state->colorkey & 0x00f800) >> 6)
-                        | ((state->colorkey & 0x0000f8) >> 3);
-               rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
-               break;
-
-       case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_ARGB8888:
-               rcar_du_plane_write(rgrp, index, PnTC3R,
-                                   PnTC3R_CODE | (state->colorkey & 0xffffff));
-               break;
-       }
-}
-
-static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp,
-                                           unsigned int index,
-                                           const struct rcar_du_plane_state *state)
-{
-       u32 ddcr2 = PnDDCR2_CODE;
-       u32 ddcr4;
-
-       /*
-        * Data format
-        *
-        * The data format is selected by the DDDF field in PnMR and the EDF
-        * field in DDCR4.
-        */
-
-       rcar_du_plane_setup_mode(rgrp, index, state);
-
-       if (state->format->planes == 2) {
-               if (state->hwindex != index) {
-                       if (state->format->fourcc == DRM_FORMAT_NV12 ||
-                           state->format->fourcc == DRM_FORMAT_NV21)
-                               ddcr2 |= PnDDCR2_Y420;
-
-                       if (state->format->fourcc == DRM_FORMAT_NV21)
-                               ddcr2 |= PnDDCR2_NV21;
-
-                       ddcr2 |= PnDDCR2_DIVU;
-               } else {
-                       ddcr2 |= PnDDCR2_DIVY;
-               }
-       }
-
-       rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
-
-       ddcr4 = state->format->edf | PnDDCR4_CODE;
-       if (state->source != RCAR_DU_PLANE_MEMORY)
-               ddcr4 |= PnDDCR4_VSPS;
-
-       rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
-}
-
-static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp,
-                                           unsigned int index,
-                                           const struct rcar_du_plane_state *state)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-       u32 pnmr = state->format->pnmr | PnMR_SPIM_TP_OFF;
-
-       if (rcdu->info->features & RCAR_DU_FEATURE_NO_BLENDING) {
-               /* No blending. ALP and EOR are not supported. */
-               pnmr &= ~(PnMR_SPIM_ALP | PnMR_SPIM_EOR);
-       }
-
-       rcar_du_plane_write(rgrp, index, PnMR, pnmr);
-
-       rcar_du_plane_write(rgrp, index, PnDDCR4,
-                           state->format->edf | PnDDCR4_CODE);
-
-       /*
-        * On Gen3, some DU channels have two planes, each being wired to a
-        * separate VSPD instance. The DU can then blend two planes. While
-        * this feature isn't used by the driver, issues related to alpha
-        * blending (such as incorrect colors or planes being invisible) may
-        * still occur if the PnALPHAR register has a stale value. Set the
-        * register to 0 to avoid this.
-        */
-
-       rcar_du_plane_write(rgrp, index, PnALPHAR, 0);
-}
-
-static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,
-                                      unsigned int index,
-                                      const struct rcar_du_plane_state *state)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-       const struct drm_rect *dst = &state->state.dst;
-
-       if (rcdu->info->gen < 3)
-               rcar_du_plane_setup_format_gen2(rgrp, index, state);
-       else
-               rcar_du_plane_setup_format_gen3(rgrp, index, state);
-
-       /* Destination position and size */
-       rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst));
-       rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst));
-       rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1);
-       rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1);
-
-       if (rcdu->info->gen < 3) {
-               /* Wrap-around and blinking, disabled */
-               rcar_du_plane_write(rgrp, index, PnWASPR, 0);
-               rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
-               rcar_du_plane_write(rgrp, index, PnBTR, 0);
-               rcar_du_plane_write(rgrp, index, PnMLR, 0);
-       }
-}
-
-void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
-                          const struct rcar_du_plane_state *state)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-
-       rcar_du_plane_setup_format(rgrp, state->hwindex, state);
-       if (state->format->planes == 2)
-               rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
-                                          state);
-
-       if (rcdu->info->gen >= 3)
-               return;
-
-       rcar_du_plane_setup_scanout(rgrp, state);
-
-       if (state->source == RCAR_DU_PLANE_VSPD1) {
-               unsigned int vspd1_sink = rgrp->index ? 2 : 0;
-
-               if (rcdu->vspd1_sink != vspd1_sink) {
-                       rcdu->vspd1_sink = vspd1_sink;
-                       rcar_du_set_dpad0_vsp1_routing(rcdu);
-
-                       /*
-                        * Changes to the VSP1 sink take effect on DRES and thus
-                        * need a restart of the group.
-                        */
-                       rgrp->need_restart = true;
-               }
-       }
-}
-
-int __rcar_du_plane_atomic_check(struct drm_plane *plane,
-                                struct drm_plane_state *state,
-                                const struct rcar_du_format_info **format)
-{
-       struct drm_device *dev = plane->dev;
-       struct drm_crtc_state *crtc_state;
-       int ret;
-
-       if (!state->crtc) {
-               /*
-                * The visible field is not reset by the DRM core but only
-                * updated by drm_plane_helper_check_state(), set it manually.
-                */
-               state->visible = false;
-               *format = NULL;
-               return 0;
-       }
-
-       crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
-       if (IS_ERR(crtc_state))
-               return PTR_ERR(crtc_state);
-
-       ret = drm_atomic_helper_check_plane_state(state, crtc_state,
-                                                 DRM_PLANE_NO_SCALING,
-                                                 DRM_PLANE_NO_SCALING,
-                                                 true, true);
-       if (ret < 0)
-               return ret;
-
-       if (!state->visible) {
-               *format = NULL;
-               return 0;
-       }
-
-       *format = rcar_du_format_info(state->fb->format->format);
-       if (*format == NULL) {
-               dev_dbg(dev->dev, "%s: unsupported format %p4cc\n", __func__,
-                       &state->fb->format->format);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int rcar_du_plane_atomic_check(struct drm_plane *plane,
-                                     struct drm_atomic_state *state)
-{
-       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
-                                                                                plane);
-       struct rcar_du_plane_state *rstate = to_rcar_plane_state(new_plane_state);
-
-       return __rcar_du_plane_atomic_check(plane, new_plane_state,
-                                           &rstate->format);
-}
-
-static void rcar_du_plane_atomic_update(struct drm_plane *plane,
-                                       struct drm_atomic_state *state)
-{
-       struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
-       struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
-       struct rcar_du_plane *rplane = to_rcar_plane(plane);
-       struct rcar_du_plane_state *old_rstate;
-       struct rcar_du_plane_state *new_rstate;
-
-       if (!new_state->visible)
-               return;
-
-       rcar_du_plane_setup(rplane);
-
-       /*
-        * Check whether the source has changed from memory to live source or
-        * from live source to memory. The source has been configured by the
-        * VSPS bit in the PnDDCR4 register. Although the datasheet states that
-        * the bit is updated during vertical blanking, it seems that updates
-        * only occur when the DU group is held in reset through the DSYSR.DRES
-        * bit. We thus need to restart the group if the source changes.
-        */
-       old_rstate = to_rcar_plane_state(old_state);
-       new_rstate = to_rcar_plane_state(new_state);
-
-       if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) !=
-           (new_rstate->source == RCAR_DU_PLANE_MEMORY))
-               rplane->group->need_restart = true;
-}
-
-static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
-       .atomic_check = rcar_du_plane_atomic_check,
-       .atomic_update = rcar_du_plane_atomic_update,
-};
-
-static struct drm_plane_state *
-rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
-{
-       struct rcar_du_plane_state *state;
-       struct rcar_du_plane_state *copy;
-
-       if (WARN_ON(!plane->state))
-               return NULL;
-
-       state = to_rcar_plane_state(plane->state);
-       copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
-       if (copy == NULL)
-               return NULL;
-
-       __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
-
-       return &copy->state;
-}
-
-static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
-                                              struct drm_plane_state *state)
-{
-       __drm_atomic_helper_plane_destroy_state(state);
-       kfree(to_rcar_plane_state(state));
-}
-
-static void rcar_du_plane_reset(struct drm_plane *plane)
-{
-       struct rcar_du_plane_state *state;
-
-       if (plane->state) {
-               rcar_du_plane_atomic_destroy_state(plane, plane->state);
-               plane->state = NULL;
-       }
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (state == NULL)
-               return;
-
-       __drm_atomic_helper_plane_reset(plane, &state->state);
-
-       state->hwindex = -1;
-       state->source = RCAR_DU_PLANE_MEMORY;
-       state->colorkey = RCAR_DU_COLORKEY_NONE;
-}
-
-static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
-                                            struct drm_plane_state *state,
-                                            struct drm_property *property,
-                                            uint64_t val)
-{
-       struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
-       struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
-
-       if (property == rcdu->props.colorkey)
-               rstate->colorkey = val;
-       else
-               return -EINVAL;
-
-       return 0;
-}
-
-static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
-       const struct drm_plane_state *state, struct drm_property *property,
-       uint64_t *val)
-{
-       const struct rcar_du_plane_state *rstate =
-               container_of(state, const struct rcar_du_plane_state, state);
-       struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
-
-       if (property == rcdu->props.colorkey)
-               *val = rstate->colorkey;
-       else
-               return -EINVAL;
-
-       return 0;
-}
-
-static const struct drm_plane_funcs rcar_du_plane_funcs = {
-       .update_plane = drm_atomic_helper_update_plane,
-       .disable_plane = drm_atomic_helper_disable_plane,
-       .reset = rcar_du_plane_reset,
-       .destroy = drm_plane_cleanup,
-       .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
-       .atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
-       .atomic_set_property = rcar_du_plane_atomic_set_property,
-       .atomic_get_property = rcar_du_plane_atomic_get_property,
-};
-
-static const uint32_t formats[] = {
-       DRM_FORMAT_RGB565,
-       DRM_FORMAT_ARGB1555,
-       DRM_FORMAT_XRGB1555,
-       DRM_FORMAT_XRGB8888,
-       DRM_FORMAT_ARGB8888,
-       DRM_FORMAT_UYVY,
-       DRM_FORMAT_YUYV,
-       DRM_FORMAT_NV12,
-       DRM_FORMAT_NV21,
-       DRM_FORMAT_NV16,
-};
-
-int rcar_du_planes_init(struct rcar_du_group *rgrp)
-{
-       struct rcar_du_device *rcdu = rgrp->dev;
-       unsigned int crtcs;
-       unsigned int i;
-       int ret;
-
-        /*
-         * Create one primary plane per CRTC in this group and seven overlay
-         * planes.
-         */
-       rgrp->num_planes = rgrp->num_crtcs + 7;
-
-       crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
-
-       for (i = 0; i < rgrp->num_planes; ++i) {
-               enum drm_plane_type type = i < rgrp->num_crtcs
-                                        ? DRM_PLANE_TYPE_PRIMARY
-                                        : DRM_PLANE_TYPE_OVERLAY;
-               struct rcar_du_plane *plane = &rgrp->planes[i];
-
-               plane->group = rgrp;
-
-               ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
-                                              crtcs, &rcar_du_plane_funcs,
-                                              formats, ARRAY_SIZE(formats),
-                                              NULL, type, NULL);
-               if (ret < 0)
-                       return ret;
-
-               drm_plane_helper_add(&plane->plane,
-                                    &rcar_du_plane_helper_funcs);
-
-               drm_plane_create_alpha_property(&plane->plane);
-
-               if (type == DRM_PLANE_TYPE_PRIMARY) {
-                       drm_plane_create_zpos_immutable_property(&plane->plane,
-                                                                0);
-               } else {
-                       drm_object_attach_property(&plane->plane.base,
-                                                  rcdu->props.colorkey,
-                                                  RCAR_DU_COLORKEY_NONE);
-                       drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
-               }
-       }
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
deleted file mode 100644 (file)
index f9893d7..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit Planes
- *
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_PLANE_H__
-#define __RCAR_DU_PLANE_H__
-
-#include <drm/drm_plane.h>
-
-struct rcar_du_format_info;
-struct rcar_du_group;
-
-/*
- * The RCAR DU has 8 hardware planes, shared between primary and overlay planes.
- * As using overlay planes requires at least one of the CRTCs being enabled, no
- * more than 7 overlay planes can be available. We thus create 1 primary plane
- * per CRTC and 7 overlay planes, for a total of up to 9 KMS planes.
- */
-#define RCAR_DU_NUM_KMS_PLANES         9
-#define RCAR_DU_NUM_HW_PLANES          8
-
-enum rcar_du_plane_source {
-       RCAR_DU_PLANE_MEMORY,
-       RCAR_DU_PLANE_VSPD0,
-       RCAR_DU_PLANE_VSPD1,
-};
-
-struct rcar_du_plane {
-       struct drm_plane plane;
-       struct rcar_du_group *group;
-};
-
-static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
-{
-       return container_of(plane, struct rcar_du_plane, plane);
-}
-
-/**
- * struct rcar_du_plane_state - Driver-specific plane state
- * @state: base DRM plane state
- * @format: information about the pixel format used by the plane
- * @hwindex: 0-based hardware plane index, -1 means unused
- * @colorkey: value of the plane colorkey property
- */
-struct rcar_du_plane_state {
-       struct drm_plane_state state;
-
-       const struct rcar_du_format_info *format;
-       int hwindex;
-       enum rcar_du_plane_source source;
-
-       unsigned int colorkey;
-};
-
-static inline struct rcar_du_plane_state *
-to_rcar_plane_state(struct drm_plane_state *state)
-{
-       return container_of(state, struct rcar_du_plane_state, state);
-}
-
-int rcar_du_atomic_check_planes(struct drm_device *dev,
-                               struct drm_atomic_state *state);
-
-int __rcar_du_plane_atomic_check(struct drm_plane *plane,
-                                struct drm_plane_state *state,
-                                const struct rcar_du_format_info **format);
-
-int rcar_du_planes_init(struct rcar_du_group *rgrp);
-
-void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
-                          const struct rcar_du_plane_state *state);
-
-static inline void rcar_du_plane_setup(struct rcar_du_plane *plane)
-{
-       struct rcar_du_plane_state *state =
-               to_rcar_plane_state(plane->plane.state);
-
-       return __rcar_du_plane_setup(plane->group, state);
-}
-
-#endif /* __RCAR_DU_PLANE_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
deleted file mode 100644 (file)
index 391de66..0000000
+++ /dev/null
@@ -1,553 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * R-Car Display Unit Registers Definitions
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_REGS_H__
-#define __RCAR_DU_REGS_H__
-
-#define DU0_REG_OFFSET         0x00000
-#define DU1_REG_OFFSET         0x30000
-#define DU2_REG_OFFSET         0x40000
-#define DU3_REG_OFFSET         0x70000
-
-/* -----------------------------------------------------------------------------
- * Display Control Registers
- */
-
-#define DSYSR                  0x00000 /* display 1 */
-#define DSYSR_ILTS             (1 << 29)
-#define DSYSR_DSEC             (1 << 20)
-#define DSYSR_IUPD             (1 << 16)
-#define DSYSR_DRES             (1 << 9)
-#define DSYSR_DEN              (1 << 8)
-#define DSYSR_TVM_MASTER       (0 << 6)
-#define DSYSR_TVM_SWITCH       (1 << 6)
-#define DSYSR_TVM_TVSYNC       (2 << 6)
-#define DSYSR_TVM_MASK         (3 << 6)
-#define DSYSR_SCM_INT_NONE     (0 << 4)
-#define DSYSR_SCM_INT_SYNC     (2 << 4)
-#define DSYSR_SCM_INT_VIDEO    (3 << 4)
-#define DSYSR_SCM_MASK         (3 << 4)
-
-#define DSMR                   0x00004
-#define DSMR_VSPM              (1 << 28)
-#define DSMR_ODPM              (1 << 27)
-#define DSMR_DIPM_DISP         (0 << 25)
-#define DSMR_DIPM_CSYNC                (1 << 25)
-#define DSMR_DIPM_DE           (3 << 25)
-#define DSMR_DIPM_MASK         (3 << 25)
-#define DSMR_CSPM              (1 << 24)
-#define DSMR_DIL               (1 << 19)
-#define DSMR_VSL               (1 << 18)
-#define DSMR_HSL               (1 << 17)
-#define DSMR_DDIS              (1 << 16)
-#define DSMR_CDEL              (1 << 15)
-#define DSMR_CDEM_CDE          (0 << 13)
-#define DSMR_CDEM_LOW          (2 << 13)
-#define DSMR_CDEM_HIGH         (3 << 13)
-#define DSMR_CDEM_MASK         (3 << 13)
-#define DSMR_CDED              (1 << 12)
-#define DSMR_ODEV              (1 << 8)
-#define DSMR_CSY_VH_OR         (0 << 6)
-#define DSMR_CSY_333           (2 << 6)
-#define DSMR_CSY_222           (3 << 6)
-#define DSMR_CSY_MASK          (3 << 6)
-
-#define DSSR                   0x00008
-#define DSSR_VC1FB_DSA0                (0 << 30)
-#define DSSR_VC1FB_DSA1                (1 << 30)
-#define DSSR_VC1FB_DSA2                (2 << 30)
-#define DSSR_VC1FB_INIT                (3 << 30)
-#define DSSR_VC1FB_MASK                (3 << 30)
-#define DSSR_VC0FB_DSA0                (0 << 28)
-#define DSSR_VC0FB_DSA1                (1 << 28)
-#define DSSR_VC0FB_DSA2                (2 << 28)
-#define DSSR_VC0FB_INIT                (3 << 28)
-#define DSSR_VC0FB_MASK                (3 << 28)
-#define DSSR_DFB(n)            (1 << ((n)+15))
-#define DSSR_TVR               (1 << 15)
-#define DSSR_FRM               (1 << 14)
-#define DSSR_VBK               (1 << 11)
-#define DSSR_RINT              (1 << 9)
-#define DSSR_HBK               (1 << 8)
-#define DSSR_ADC(n)            (1 << ((n)-1))
-
-#define DSRCR                  0x0000c
-#define DSRCR_TVCL             (1 << 15)
-#define DSRCR_FRCL             (1 << 14)
-#define DSRCR_VBCL             (1 << 11)
-#define DSRCR_RICL             (1 << 9)
-#define DSRCR_HBCL             (1 << 8)
-#define DSRCR_ADCL(n)          (1 << ((n)-1))
-#define DSRCR_MASK             0x0000cbff
-
-#define DIER                   0x00010
-#define DIER_TVE               (1 << 15)
-#define DIER_FRE               (1 << 14)
-#define DIER_VBE               (1 << 11)
-#define DIER_RIE               (1 << 9)
-#define DIER_HBE               (1 << 8)
-#define DIER_ADCE(n)           (1 << ((n)-1))
-
-#define CPCR                   0x00014
-#define CPCR_CP4CE             (1 << 19)
-#define CPCR_CP3CE             (1 << 18)
-#define CPCR_CP2CE             (1 << 17)
-#define CPCR_CP1CE             (1 << 16)
-
-#define DPPR                   0x00018
-#define DPPR_DPE(n)            (1 << ((n)*4-1))
-#define DPPR_DPS(n, p)         (((p)-1) << DPPR_DPS_SHIFT(n))
-#define DPPR_DPS_SHIFT(n)      (((n)-1)*4)
-#define DPPR_BPP16             (DPPR_DPE(8) | DPPR_DPS(8, 1))  /* plane1 */
-#define DPPR_BPP32_P1          (DPPR_DPE(7) | DPPR_DPS(7, 1))
-#define DPPR_BPP32_P2          (DPPR_DPE(8) | DPPR_DPS(8, 2))
-#define DPPR_BPP32             (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */
-
-#define DEFR                   0x00020
-#define DEFR_CODE              (0x7773 << 16)
-#define DEFR_EXSL              (1 << 12)
-#define DEFR_EXVL              (1 << 11)
-#define DEFR_EXUP              (1 << 5)
-#define DEFR_VCUP              (1 << 4)
-#define DEFR_DEFE              (1 << 0)
-
-#define DAPCR                  0x00024
-#define DAPCR_CODE             (0x7773 << 16)
-#define DAPCR_AP2E             (1 << 4)
-#define DAPCR_AP1E             (1 << 0)
-
-#define DCPCR                  0x00028
-#define DCPCR_CODE             (0x7773 << 16)
-#define DCPCR_CA2B             (1 << 13)
-#define DCPCR_CD2F             (1 << 12)
-#define DCPCR_DC2E             (1 << 8)
-#define DCPCR_CAB              (1 << 5)
-#define DCPCR_CDF              (1 << 4)
-#define DCPCR_DCE              (1 << 0)
-
-#define DEFR2                  0x00034
-#define DEFR2_CODE             (0x7775 << 16)
-#define DEFR2_DEFE2G           (1 << 0)
-
-#define DEFR3                  0x00038
-#define DEFR3_CODE             (0x7776 << 16)
-#define DEFR3_EVDA             (1 << 14)
-#define DEFR3_EVDM_1           (1 << 12)
-#define DEFR3_EVDM_2           (2 << 12)
-#define DEFR3_EVDM_3           (3 << 12)
-#define DEFR3_VMSM2_EMA                (1 << 6)
-#define DEFR3_VMSM1_ENA                (1 << 4)
-#define DEFR3_DEFE3            (1 << 0)
-
-#define DEFR4                  0x0003c
-#define DEFR4_CODE             (0x7777 << 16)
-#define DEFR4_LRUO             (1 << 5)
-#define DEFR4_SPCE             (1 << 4)
-
-#define DVCSR                  0x000d0
-#define DVCSR_VCnFB2_DSA0(n)   (0 << ((n)*2+16))
-#define DVCSR_VCnFB2_DSA1(n)   (1 << ((n)*2+16))
-#define DVCSR_VCnFB2_DSA2(n)   (2 << ((n)*2+16))
-#define DVCSR_VCnFB2_INIT(n)   (3 << ((n)*2+16))
-#define DVCSR_VCnFB2_MASK(n)   (3 << ((n)*2+16))
-#define DVCSR_VCnFB_DSA0(n)    (0 << ((n)*2))
-#define DVCSR_VCnFB_DSA1(n)    (1 << ((n)*2))
-#define DVCSR_VCnFB_DSA2(n)    (2 << ((n)*2))
-#define DVCSR_VCnFB_INIT(n)    (3 << ((n)*2))
-#define DVCSR_VCnFB_MASK(n)    (3 << ((n)*2))
-
-#define DEFR5                  0x000e0
-#define DEFR5_CODE             (0x66 << 24)
-#define DEFR5_YCRGB2_DIS       (0 << 14)
-#define DEFR5_YCRGB2_PRI1      (1 << 14)
-#define DEFR5_YCRGB2_PRI2      (2 << 14)
-#define DEFR5_YCRGB2_PRI3      (3 << 14)
-#define DEFR5_YCRGB2_MASK      (3 << 14)
-#define DEFR5_YCRGB1_DIS       (0 << 12)
-#define DEFR5_YCRGB1_PRI1      (1 << 12)
-#define DEFR5_YCRGB1_PRI2      (2 << 12)
-#define DEFR5_YCRGB1_PRI3      (3 << 12)
-#define DEFR5_YCRGB1_MASK      (3 << 12)
-#define DEFR5_DEFE5            (1 << 0)
-
-#define DDLTR                  0x000e4
-#define DDLTR_CODE             (0x7766 << 16)
-#define DDLTR_DLAR2            (1 << 6)
-#define DDLTR_DLAY2            (1 << 5)
-#define DDLTR_DLAY1            (1 << 1)
-
-#define DEFR6                  0x000e8
-#define DEFR6_CODE             (0x7778 << 16)
-#define DEFR6_ODPM12_DSMR      (0 << 10)
-#define DEFR6_ODPM12_DISP      (2 << 10)
-#define DEFR6_ODPM12_CDE       (3 << 10)
-#define DEFR6_ODPM12_MASK      (3 << 10)
-#define DEFR6_ODPM02_DSMR      (0 << 8)
-#define DEFR6_ODPM02_DISP      (2 << 8)
-#define DEFR6_ODPM02_CDE       (3 << 8)
-#define DEFR6_ODPM02_MASK      (3 << 8)
-#define DEFR6_TCNE1            (1 << 6)
-#define DEFR6_TCNE0            (1 << 4)
-#define DEFR6_MLOS1            (1 << 2)
-#define DEFR6_DEFAULT          (DEFR6_CODE | DEFR6_TCNE1)
-
-#define DEFR7                  0x000ec
-#define DEFR7_CODE             (0x7779 << 16)
-#define DEFR7_CMME1            BIT(6)
-#define DEFR7_CMME0            BIT(4)
-
-/* -----------------------------------------------------------------------------
- * R8A7790-only Control Registers
- */
-
-#define DD1SSR                 0x20008
-#define DD1SSR_TVR             (1 << 15)
-#define DD1SSR_FRM             (1 << 14)
-#define DD1SSR_BUF             (1 << 12)
-#define DD1SSR_VBK             (1 << 11)
-#define DD1SSR_RINT            (1 << 9)
-#define DD1SSR_HBK             (1 << 8)
-#define DD1SSR_ADC(n)          (1 << ((n)-1))
-
-#define DD1SRCR                        0x2000c
-#define DD1SRCR_TVR            (1 << 15)
-#define DD1SRCR_FRM            (1 << 14)
-#define DD1SRCR_BUF            (1 << 12)
-#define DD1SRCR_VBK            (1 << 11)
-#define DD1SRCR_RINT           (1 << 9)
-#define DD1SRCR_HBK            (1 << 8)
-#define DD1SRCR_ADC(n)         (1 << ((n)-1))
-
-#define DD1IER                 0x20010
-#define DD1IER_TVR             (1 << 15)
-#define DD1IER_FRM             (1 << 14)
-#define DD1IER_BUF             (1 << 12)
-#define DD1IER_VBK             (1 << 11)
-#define DD1IER_RINT            (1 << 9)
-#define DD1IER_HBK             (1 << 8)
-#define DD1IER_ADC(n)          (1 << ((n)-1))
-
-#define DEFR8                  0x20020
-#define DEFR8_CODE             (0x7790 << 16)
-#define DEFR8_VSCS             (1 << 6)
-#define DEFR8_DRGBS_DU(n)      ((n) << 4)
-#define DEFR8_DRGBS_MASK       (3 << 4)
-#define DEFR8_DEFE8            (1 << 0)
-
-#define DOFLR                  0x20024
-#define DOFLR_CODE             (0x7790 << 16)
-#define DOFLR_HSYCFL1          (1 << 13)
-#define DOFLR_VSYCFL1          (1 << 12)
-#define DOFLR_ODDFL1           (1 << 11)
-#define DOFLR_DISPFL1          (1 << 10)
-#define DOFLR_CDEFL1           (1 << 9)
-#define DOFLR_RGBFL1           (1 << 8)
-#define DOFLR_HSYCFL0          (1 << 5)
-#define DOFLR_VSYCFL0          (1 << 4)
-#define DOFLR_ODDFL0           (1 << 3)
-#define DOFLR_DISPFL0          (1 << 2)
-#define DOFLR_CDEFL0           (1 << 1)
-#define DOFLR_RGBFL0           (1 << 0)
-
-#define DIDSR                  0x20028
-#define DIDSR_CODE             (0x7790 << 16)
-#define DIDSR_LDCS_DCLKIN(n)   (0 << (8 + (n) * 2))
-#define DIDSR_LDCS_DSI(n)      (2 << (8 + (n) * 2))    /* V3U only */
-#define DIDSR_LDCS_LVDS0(n)    (2 << (8 + (n) * 2))
-#define DIDSR_LDCS_LVDS1(n)    (3 << (8 + (n) * 2))
-#define DIDSR_LDCS_MASK(n)     (3 << (8 + (n) * 2))
-#define DIDSR_PDCS_CLK(n, clk) (clk << ((n) * 2))
-#define DIDSR_PDCS_MASK(n)     (3 << ((n) * 2))
-
-#define DEFR10                 0x20038
-#define DEFR10_CODE            (0x7795 << 16)
-#define DEFR10_VSPF1_RGB       (0 << 14)
-#define DEFR10_VSPF1_YC                (1 << 14)
-#define DEFR10_DOCF1_RGB       (0 << 12)
-#define DEFR10_DOCF1_YC                (1 << 12)
-#define DEFR10_YCDF0_YCBCR444  (0 << 11)
-#define DEFR10_YCDF0_YCBCR422  (1 << 11)
-#define DEFR10_VSPF0_RGB       (0 << 10)
-#define DEFR10_VSPF0_YC                (1 << 10)
-#define DEFR10_DOCF0_RGB       (0 << 8)
-#define DEFR10_DOCF0_YC                (1 << 8)
-#define DEFR10_TSEL_H3_TCON1   (0 << 1) /* DEFR102 register only (DU2/DU3) */
-#define DEFR10_DEFE10          (1 << 0)
-
-#define DPLLCR                 0x20044
-#define DPLLCR_CODE            (0x95 << 24)
-#define DPLLCR_PLCS1           (1 << 23)
-#define DPLLCR_PLCS0           (1 << 21)
-#define DPLLCR_CLKE            (1 << 18)
-#define DPLLCR_FDPLL(n)                ((n) << 12)
-#define DPLLCR_N(n)            ((n) << 5)
-#define DPLLCR_M(n)            ((n) << 3)
-#define DPLLCR_STBY            (1 << 2)
-#define DPLLCR_INCS_DOTCLKIN0  (0 << 0)
-#define DPLLCR_INCS_DOTCLKIN1  (1 << 1)
-
-#define DPLLC2R                        0x20048
-#define DPLLC2R_CODE           (0x95 << 24)
-#define DPLLC2R_SELC           (1 << 12)
-#define DPLLC2R_M(n)           ((n) << 8)
-#define DPLLC2R_FDPLL(n)       ((n) << 0)
-
-/* -----------------------------------------------------------------------------
- * Display Timing Generation Registers
- */
-
-#define HDSR                   0x00040
-#define HDER                   0x00044
-#define VDSR                   0x00048
-#define VDER                   0x0004c
-#define HCR                    0x00050
-#define HSWR                   0x00054
-#define VCR                    0x00058
-#define VSPR                   0x0005c
-#define EQWR                   0x00060
-#define SPWR                   0x00064
-#define CLAMPSR                        0x00070
-#define CLAMPWR                        0x00074
-#define DESR                   0x00078
-#define DEWR                   0x0007c
-
-/* -----------------------------------------------------------------------------
- * Display Attribute Registers
- */
-
-#define CP1TR                  0x00080
-#define CP2TR                  0x00084
-#define CP3TR                  0x00088
-#define CP4TR                  0x0008c
-
-#define DOOR                   0x00090
-#define DOOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
-#define CDER                   0x00094
-#define CDER_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
-#define BPOR                   0x00098
-#define BPOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
-
-#define RINTOFSR               0x0009c
-
-#define DSHPR                  0x000c8
-#define DSHPR_CODE             (0x7776 << 16)
-#define DSHPR_PRIH             (0xa << 4)
-#define DSHPR_PRIL_BPP16       (0x8 << 0)
-#define DSHPR_PRIL_BPP32       (0x9 << 0)
-
-/* -----------------------------------------------------------------------------
- * Display Plane Registers
- */
-
-#define PLANE_OFF              0x00100
-
-#define PnMR                   0x00100 /* plane 1 */
-#define PnMR_VISL_VIN0         (0 << 26)       /* use Video Input 0 */
-#define PnMR_VISL_VIN1         (1 << 26)       /* use Video Input 1 */
-#define PnMR_VISL_VIN2         (2 << 26)       /* use Video Input 2 */
-#define PnMR_VISL_VIN3         (3 << 26)       /* use Video Input 3 */
-#define PnMR_YCDF_YUYV         (1 << 20)       /* YUYV format */
-#define PnMR_TC_R              (0 << 17)       /* Tranparent color is PnTC1R */
-#define PnMR_TC_CP             (1 << 17)       /* Tranparent color is color palette */
-#define PnMR_WAE               (1 << 16)       /* Wrap around Enable */
-#define PnMR_SPIM_TP           (0 << 12)       /* Transparent Color */
-#define PnMR_SPIM_ALP          (1 << 12)       /* Alpha Blending */
-#define PnMR_SPIM_EOR          (2 << 12)       /* EOR */
-#define PnMR_SPIM_TP_OFF       (1 << 14)       /* No Transparent Color */
-#define PnMR_CPSL_CP1          (0 << 8)        /* Color Palette selected 1 */
-#define PnMR_CPSL_CP2          (1 << 8)        /* Color Palette selected 2 */
-#define PnMR_CPSL_CP3          (2 << 8)        /* Color Palette selected 3 */
-#define PnMR_CPSL_CP4          (3 << 8)        /* Color Palette selected 4 */
-#define PnMR_DC                        (1 << 7)        /* Display Area Change */
-#define PnMR_BM_MD             (0 << 4)        /* Manual Display Change Mode */
-#define PnMR_BM_AR             (1 << 4)        /* Auto Rendering Mode */
-#define PnMR_BM_AD             (2 << 4)        /* Auto Display Change Mode */
-#define PnMR_BM_VC             (3 << 4)        /* Video Capture Mode */
-#define PnMR_DDDF_8BPP         (0 << 0)        /* 8bit */
-#define PnMR_DDDF_16BPP                (1 << 0)        /* 16bit or 32bit */
-#define PnMR_DDDF_ARGB         (2 << 0)        /* ARGB */
-#define PnMR_DDDF_YC           (3 << 0)        /* YC */
-#define PnMR_DDDF_MASK         (3 << 0)
-
-#define PnMWR                  0x00104
-
-#define PnALPHAR               0x00108
-#define PnALPHAR_ABIT_1                (0 << 12)
-#define PnALPHAR_ABIT_0                (1 << 12)
-#define PnALPHAR_ABIT_X                (2 << 12)
-
-#define PnDSXR                 0x00110
-#define PnDSYR                 0x00114
-#define PnDPXR                 0x00118
-#define PnDPYR                 0x0011c
-
-#define PnDSA0R                        0x00120
-#define PnDSA1R                        0x00124
-#define PnDSA2R                        0x00128
-#define PnDSA_MASK             0xfffffff0
-
-#define PnSPXR                 0x00130
-#define PnSPYR                 0x00134
-#define PnWASPR                        0x00138
-#define PnWAMWR                        0x0013c
-
-#define PnBTR                  0x00140
-
-#define PnTC1R                 0x00144
-#define PnTC2R                 0x00148
-#define PnTC3R                 0x0014c
-#define PnTC3R_CODE            (0x66 << 24)
-
-#define PnMLR                  0x00150
-
-#define PnSWAPR                        0x00180
-#define PnSWAPR_DIGN           (1 << 4)
-#define PnSWAPR_SPQW           (1 << 3)
-#define PnSWAPR_SPLW           (1 << 2)
-#define PnSWAPR_SPWD           (1 << 1)
-#define PnSWAPR_SPBY           (1 << 0)
-
-#define PnDDCR                 0x00184
-#define PnDDCR_CODE            (0x7775 << 16)
-#define PnDDCR_LRGB1           (1 << 11)
-#define PnDDCR_LRGB0           (1 << 10)
-
-#define PnDDCR2                        0x00188
-#define PnDDCR2_CODE           (0x7776 << 16)
-#define PnDDCR2_NV21           (1 << 5)
-#define PnDDCR2_Y420           (1 << 4)
-#define PnDDCR2_DIVU           (1 << 1)
-#define PnDDCR2_DIVY           (1 << 0)
-
-#define PnDDCR4                        0x00190
-#define PnDDCR4_CODE           (0x7766 << 16)
-#define PnDDCR4_VSPS           (1 << 13)
-#define PnDDCR4_SDFS_RGB       (0 << 4)
-#define PnDDCR4_SDFS_YC                (5 << 4)
-#define PnDDCR4_SDFS_MASK      (7 << 4)
-#define PnDDCR4_EDF_NONE       (0 << 0)
-#define PnDDCR4_EDF_ARGB8888   (1 << 0)
-#define PnDDCR4_EDF_RGB888     (2 << 0)
-#define PnDDCR4_EDF_RGB666     (3 << 0)
-#define PnDDCR4_EDF_MASK       (7 << 0)
-
-#define APnMR                  0x0a100
-#define APnMR_WAE              (1 << 16)       /* Wrap around Enable */
-#define APnMR_DC               (1 << 7)        /* Display Area Change */
-#define APnMR_BM_MD            (0 << 4)        /* Manual Display Change Mode */
-#define APnMR_BM_AD            (2 << 4)        /* Auto Display Change Mode */
-
-#define APnMWR                 0x0a104
-
-#define APnDSXR                        0x0a110
-#define APnDSYR                        0x0a114
-#define APnDPXR                        0x0a118
-#define APnDPYR                        0x0a11c
-
-#define APnDSA0R               0x0a120
-#define APnDSA1R               0x0a124
-#define APnDSA2R               0x0a128
-
-#define APnSPXR                        0x0a130
-#define APnSPYR                        0x0a134
-#define APnWASPR               0x0a138
-#define APnWAMWR               0x0a13c
-
-#define APnBTR                 0x0a140
-
-#define APnMLR                 0x0a150
-#define APnSWAPR               0x0a180
-
-/* -----------------------------------------------------------------------------
- * Display Capture Registers
- */
-
-#define DCMR                   0x0c100
-#define DCMWR                  0x0c104
-#define DCSAR                  0x0c120
-#define DCMLR                  0x0c150
-
-/* -----------------------------------------------------------------------------
- * Color Palette Registers
- */
-
-#define CP1_000R               0x01000
-#define CP1_255R               0x013fc
-#define CP2_000R               0x02000
-#define CP2_255R               0x023fc
-#define CP3_000R               0x03000
-#define CP3_255R               0x033fc
-#define CP4_000R               0x04000
-#define CP4_255R               0x043fc
-
-/* -----------------------------------------------------------------------------
- * External Synchronization Control Registers
- */
-
-#define ESCR02                 0x10000
-#define ESCR13                 0x01000
-#define ESCR_DCLKOINV          (1 << 25)
-#define ESCR_DCLKSEL_DCLKIN    (0 << 20)
-#define ESCR_DCLKSEL_CLKS      (1 << 20)
-#define ESCR_DCLKSEL_MASK      (1 << 20)
-#define ESCR_DCLKDIS           (1 << 16)
-#define ESCR_SYNCSEL_OFF       (0 << 8)
-#define ESCR_SYNCSEL_EXVSYNC   (2 << 8)
-#define ESCR_SYNCSEL_EXHSYNC   (3 << 8)
-#define ESCR_FRQSEL_MASK       (0x3f << 0)
-
-#define OTAR02                 0x10004
-#define OTAR13                 0x01004
-
-/* -----------------------------------------------------------------------------
- * Dual Display Output Control Registers
- */
-
-#define DORCR                  0x11000
-#define DORCR_PG1T             (1 << 30)
-#define DORCR_DK1S             (1 << 28)
-#define DORCR_PG1D_DS0         (0 << 24)
-#define DORCR_PG1D_DS1         (1 << 24)
-#define DORCR_PG1D_FIX0                (2 << 24)
-#define DORCR_PG1D_DOOR                (3 << 24)
-#define DORCR_PG1D_MASK                (3 << 24)
-#define DORCR_DR0D             (1 << 21)
-#define DORCR_PG0D_DS0         (0 << 16)
-#define DORCR_PG0D_DS1         (1 << 16)
-#define DORCR_PG0D_FIX0                (2 << 16)
-#define DORCR_PG0D_DOOR                (3 << 16)
-#define DORCR_PG0D_MASK                (3 << 16)
-#define DORCR_RGPV             (1 << 4)
-#define DORCR_DPRS             (1 << 0)
-
-#define DPTSR                  0x11004
-#define DPTSR_PnDK(n)          (1 << ((n) + 16))
-#define DPTSR_PnTS(n)          (1 << (n))
-
-#define DAPTSR                 0x11008
-#define DAPTSR_APnDK(n)                (1 << ((n) + 16))
-#define DAPTSR_APnTS(n)                (1 << (n))
-
-#define DS1PR                  0x11020
-#define DS2PR                  0x11024
-
-/* -----------------------------------------------------------------------------
- * YC-RGB Conversion Coefficient Registers
- */
-
-#define YNCR                   0x11080
-#define YNOR                   0x11084
-#define CRNOR                  0x11088
-#define CBNOR                  0x1108c
-#define RCRCR                  0x11090
-#define GCRCR                  0x11094
-#define GCBCR                  0x11098
-#define BCBCR                  0x1109c
-
-#endif /* __RCAR_DU_REGS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
deleted file mode 100644 (file)
index 45c05d0..0000000
+++ /dev/null
@@ -1,513 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Display Unit VSP-Based Compositor
- *
- * Copyright (C) 2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_blend.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_fb_dma_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_managed.h>
-#include <drm/drm_vblank.h>
-
-#include <linux/bitops.h>
-#include <linux/dma-mapping.h>
-#include <linux/of_platform.h>
-#include <linux/scatterlist.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-
-#include <media/vsp1.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_vsp.h"
-#include "rcar_du_writeback.h"
-
-static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
-{
-       struct rcar_du_crtc *crtc = private;
-
-       if (crtc->vblank_enable)
-               drm_crtc_handle_vblank(&crtc->crtc);
-
-       if (status & VSP1_DU_STATUS_COMPLETE)
-               rcar_du_crtc_finish_page_flip(crtc);
-       if (status & VSP1_DU_STATUS_WRITEBACK)
-               rcar_du_writeback_complete(crtc);
-
-       drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
-}
-
-void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
-{
-       const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
-       struct rcar_du_device *rcdu = crtc->dev;
-       struct vsp1_du_lif_config cfg = {
-               .width = mode->hdisplay,
-               .height = mode->vdisplay,
-               .interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE,
-               .callback = rcar_du_vsp_complete,
-               .callback_data = crtc,
-       };
-       struct rcar_du_plane_state state = {
-               .state = {
-                       .alpha = DRM_BLEND_ALPHA_OPAQUE,
-                       .crtc = &crtc->crtc,
-                       .dst.x1 = 0,
-                       .dst.y1 = 0,
-                       .dst.x2 = mode->hdisplay,
-                       .dst.y2 = mode->vdisplay,
-                       .src.x1 = 0,
-                       .src.y1 = 0,
-                       .src.x2 = mode->hdisplay << 16,
-                       .src.y2 = mode->vdisplay << 16,
-                       .zpos = 0,
-               },
-               .format = rcar_du_format_info(DRM_FORMAT_XRGB8888),
-               .source = RCAR_DU_PLANE_VSPD1,
-               .colorkey = 0,
-       };
-
-       if (rcdu->info->gen >= 3)
-               state.hwindex = (crtc->index % 2) ? 2 : 0;
-       else
-               state.hwindex = crtc->index % 2;
-
-       __rcar_du_plane_setup(crtc->group, &state);
-
-       vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
-}
-
-void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
-{
-       vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
-}
-
-void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
-{
-       vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
-}
-
-void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
-{
-       struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
-       struct rcar_du_crtc_state *state;
-
-       state = to_rcar_crtc_state(crtc->crtc.state);
-       cfg.crc = state->crc;
-
-       rcar_du_writeback_setup(crtc, &cfg.writeback);
-
-       vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
-}
-
-static const u32 rcar_du_vsp_formats[] = {
-       DRM_FORMAT_RGB332,
-       DRM_FORMAT_ARGB4444,
-       DRM_FORMAT_XRGB4444,
-       DRM_FORMAT_ARGB1555,
-       DRM_FORMAT_XRGB1555,
-       DRM_FORMAT_RGB565,
-       DRM_FORMAT_BGR888,
-       DRM_FORMAT_RGB888,
-       DRM_FORMAT_BGRA8888,
-       DRM_FORMAT_BGRX8888,
-       DRM_FORMAT_ARGB8888,
-       DRM_FORMAT_XRGB8888,
-       DRM_FORMAT_UYVY,
-       DRM_FORMAT_YUYV,
-       DRM_FORMAT_YVYU,
-       DRM_FORMAT_NV12,
-       DRM_FORMAT_NV21,
-       DRM_FORMAT_NV16,
-       DRM_FORMAT_NV61,
-       DRM_FORMAT_YUV420,
-       DRM_FORMAT_YVU420,
-       DRM_FORMAT_YUV422,
-       DRM_FORMAT_YVU422,
-       DRM_FORMAT_YUV444,
-       DRM_FORMAT_YVU444,
-};
-
-/*
- * Gen4 supports the same formats as above, and additionally 2-10-10-10 RGB
- * formats and Y210 & Y212 formats.
- */
-static const u32 rcar_du_vsp_formats_gen4[] = {
-       DRM_FORMAT_RGB332,
-       DRM_FORMAT_ARGB4444,
-       DRM_FORMAT_XRGB4444,
-       DRM_FORMAT_ARGB1555,
-       DRM_FORMAT_XRGB1555,
-       DRM_FORMAT_RGB565,
-       DRM_FORMAT_BGR888,
-       DRM_FORMAT_RGB888,
-       DRM_FORMAT_BGRA8888,
-       DRM_FORMAT_BGRX8888,
-       DRM_FORMAT_ARGB8888,
-       DRM_FORMAT_XRGB8888,
-       DRM_FORMAT_RGBX1010102,
-       DRM_FORMAT_RGBA1010102,
-       DRM_FORMAT_ARGB2101010,
-       DRM_FORMAT_UYVY,
-       DRM_FORMAT_YUYV,
-       DRM_FORMAT_YVYU,
-       DRM_FORMAT_NV12,
-       DRM_FORMAT_NV21,
-       DRM_FORMAT_NV16,
-       DRM_FORMAT_NV61,
-       DRM_FORMAT_YUV420,
-       DRM_FORMAT_YVU420,
-       DRM_FORMAT_YUV422,
-       DRM_FORMAT_YVU422,
-       DRM_FORMAT_YUV444,
-       DRM_FORMAT_YVU444,
-       DRM_FORMAT_Y210,
-       DRM_FORMAT_Y212,
-};
-
-static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
-{
-       struct rcar_du_vsp_plane_state *state =
-               to_rcar_vsp_plane_state(plane->plane.state);
-       struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
-       struct drm_framebuffer *fb = plane->plane.state->fb;
-       const struct rcar_du_format_info *format;
-       struct vsp1_du_atomic_config cfg = {
-               .pixelformat = 0,
-               .pitch = fb->pitches[0],
-               .alpha = state->state.alpha >> 8,
-               .zpos = state->state.zpos,
-       };
-       u32 fourcc = state->format->fourcc;
-       unsigned int i;
-
-       cfg.src.left = state->state.src.x1 >> 16;
-       cfg.src.top = state->state.src.y1 >> 16;
-       cfg.src.width = drm_rect_width(&state->state.src) >> 16;
-       cfg.src.height = drm_rect_height(&state->state.src) >> 16;
-
-       cfg.dst.left = state->state.dst.x1;
-       cfg.dst.top = state->state.dst.y1;
-       cfg.dst.width = drm_rect_width(&state->state.dst);
-       cfg.dst.height = drm_rect_height(&state->state.dst);
-
-       for (i = 0; i < state->format->planes; ++i)
-               cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
-                          + fb->offsets[i];
-
-       if (state->state.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE) {
-               switch (fourcc) {
-               case DRM_FORMAT_ARGB1555:
-                       fourcc = DRM_FORMAT_XRGB1555;
-                       break;
-
-               case DRM_FORMAT_ARGB4444:
-                       fourcc = DRM_FORMAT_XRGB4444;
-                       break;
-
-               case DRM_FORMAT_ARGB8888:
-                       fourcc = DRM_FORMAT_XRGB8888;
-                       break;
-               }
-       }
-
-       format = rcar_du_format_info(fourcc);
-       cfg.pixelformat = format->v4l2;
-
-       cfg.premult = state->state.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI;
-
-       vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
-                             plane->index, &cfg);
-}
-
-int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
-                      struct sg_table sg_tables[3])
-{
-       struct rcar_du_device *rcdu = vsp->dev;
-       unsigned int i, j;
-       int ret;
-
-       for (i = 0; i < fb->format->num_planes; ++i) {
-               struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);
-               struct sg_table *sgt = &sg_tables[i];
-
-               if (gem->sgt) {
-                       struct scatterlist *src;
-                       struct scatterlist *dst;
-
-                       /*
-                        * If the GEM buffer has a scatter gather table, it has
-                        * been imported from a dma-buf and has no physical
-                        * address as it might not be physically contiguous.
-                        * Copy the original scatter gather table to map it to
-                        * the VSP.
-                        */
-                       ret = sg_alloc_table(sgt, gem->sgt->orig_nents,
-                                            GFP_KERNEL);
-                       if (ret)
-                               goto fail;
-
-                       src = gem->sgt->sgl;
-                       dst = sgt->sgl;
-                       for (j = 0; j < gem->sgt->orig_nents; ++j) {
-                               sg_set_page(dst, sg_page(src), src->length,
-                                           src->offset);
-                               src = sg_next(src);
-                               dst = sg_next(dst);
-                       }
-               } else {
-                       ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr,
-                                             gem->dma_addr, gem->base.size);
-                       if (ret)
-                               goto fail;
-               }
-
-               ret = vsp1_du_map_sg(vsp->vsp, sgt);
-               if (ret) {
-                       sg_free_table(sgt);
-                       goto fail;
-               }
-       }
-
-       return 0;
-
-fail:
-       while (i--) {
-               struct sg_table *sgt = &sg_tables[i];
-
-               vsp1_du_unmap_sg(vsp->vsp, sgt);
-               sg_free_table(sgt);
-       }
-
-       return ret;
-}
-
-static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
-                                       struct drm_plane_state *state)
-{
-       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
-       struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
-       int ret;
-
-       /*
-        * There's no need to prepare (and unprepare) the framebuffer when the
-        * plane is not visible, as it will not be displayed.
-        */
-       if (!state->visible)
-               return 0;
-
-       ret = rcar_du_vsp_map_fb(vsp, state->fb, rstate->sg_tables);
-       if (ret < 0)
-               return ret;
-
-       return drm_gem_plane_helper_prepare_fb(plane, state);
-}
-
-void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
-                         struct sg_table sg_tables[3])
-{
-       unsigned int i;
-
-       for (i = 0; i < fb->format->num_planes; ++i) {
-               struct sg_table *sgt = &sg_tables[i];
-
-               vsp1_du_unmap_sg(vsp->vsp, sgt);
-               sg_free_table(sgt);
-       }
-}
-
-static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
-                                        struct drm_plane_state *state)
-{
-       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
-       struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
-
-       if (!state->visible)
-               return;
-
-       rcar_du_vsp_unmap_fb(vsp, state->fb, rstate->sg_tables);
-}
-
-static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
-                                         struct drm_atomic_state *state)
-{
-       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
-                                                                                plane);
-       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(new_plane_state);
-
-       return __rcar_du_plane_atomic_check(plane, new_plane_state,
-                                           &rstate->format);
-}
-
-static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
-                                       struct drm_atomic_state *state)
-{
-       struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
-       struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
-       struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
-       struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
-
-       if (new_state->visible)
-               rcar_du_vsp_plane_setup(rplane);
-       else if (old_state->crtc)
-               vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
-                                     rplane->index, NULL);
-}
-
-static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
-       .prepare_fb = rcar_du_vsp_plane_prepare_fb,
-       .cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
-       .atomic_check = rcar_du_vsp_plane_atomic_check,
-       .atomic_update = rcar_du_vsp_plane_atomic_update,
-};
-
-static struct drm_plane_state *
-rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
-{
-       struct rcar_du_vsp_plane_state *copy;
-
-       if (WARN_ON(!plane->state))
-               return NULL;
-
-       copy = kzalloc(sizeof(*copy), GFP_KERNEL);
-       if (copy == NULL)
-               return NULL;
-
-       __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
-
-       return &copy->state;
-}
-
-static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
-                                                  struct drm_plane_state *state)
-{
-       __drm_atomic_helper_plane_destroy_state(state);
-       kfree(to_rcar_vsp_plane_state(state));
-}
-
-static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
-{
-       struct rcar_du_vsp_plane_state *state;
-
-       if (plane->state) {
-               rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
-               plane->state = NULL;
-       }
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (state == NULL)
-               return;
-
-       __drm_atomic_helper_plane_reset(plane, &state->state);
-}
-
-static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
-       .update_plane = drm_atomic_helper_update_plane,
-       .disable_plane = drm_atomic_helper_disable_plane,
-       .reset = rcar_du_vsp_plane_reset,
-       .destroy = drm_plane_cleanup,
-       .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
-       .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
-};
-
-static void rcar_du_vsp_cleanup(struct drm_device *dev, void *res)
-{
-       struct rcar_du_vsp *vsp = res;
-       unsigned int i;
-
-       for (i = 0; i < vsp->num_planes; ++i) {
-               struct rcar_du_vsp_plane *plane = &vsp->planes[i];
-
-               drm_plane_cleanup(&plane->plane);
-       }
-
-       kfree(vsp->planes);
-
-       put_device(vsp->vsp);
-}
-
-int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
-                    unsigned int crtcs)
-{
-       struct rcar_du_device *rcdu = vsp->dev;
-       struct platform_device *pdev;
-       unsigned int num_crtcs = hweight32(crtcs);
-       unsigned int num_planes;
-       unsigned int i;
-       int ret;
-
-       /* Find the VSP device and initialize it. */
-       pdev = of_find_device_by_node(np);
-       if (!pdev)
-               return -ENXIO;
-
-       vsp->vsp = &pdev->dev;
-
-       ret = drmm_add_action_or_reset(&rcdu->ddev, rcar_du_vsp_cleanup, vsp);
-       if (ret < 0)
-               return ret;
-
-       ret = vsp1_du_init(vsp->vsp);
-       if (ret < 0)
-               return ret;
-
-       num_planes = rcdu->info->num_rpf;
-
-       vsp->planes = kcalloc(num_planes, sizeof(*vsp->planes), GFP_KERNEL);
-       if (!vsp->planes)
-               return -ENOMEM;
-
-       for (i = 0; i < num_planes; ++i) {
-               enum drm_plane_type type = i < num_crtcs
-                                        ? DRM_PLANE_TYPE_PRIMARY
-                                        : DRM_PLANE_TYPE_OVERLAY;
-               struct rcar_du_vsp_plane *plane = &vsp->planes[i];
-               unsigned int num_formats;
-               const u32 *formats;
-
-               if (rcdu->info->gen < 4) {
-                       num_formats = ARRAY_SIZE(rcar_du_vsp_formats);
-                       formats = rcar_du_vsp_formats;
-               } else {
-                       num_formats = ARRAY_SIZE(rcar_du_vsp_formats_gen4);
-                       formats = rcar_du_vsp_formats_gen4;
-               }
-
-               plane->vsp = vsp;
-               plane->index = i;
-
-               ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
-                                              crtcs, &rcar_du_vsp_plane_funcs,
-                                              formats, num_formats,
-                                              NULL, type, NULL);
-               if (ret < 0)
-                       return ret;
-
-               drm_plane_helper_add(&plane->plane,
-                                    &rcar_du_vsp_plane_helper_funcs);
-
-               drm_plane_create_alpha_property(&plane->plane);
-               drm_plane_create_zpos_property(&plane->plane, i, 0,
-                                              num_planes - 1);
-
-               drm_plane_create_blend_mode_property(&plane->plane,
-                                       BIT(DRM_MODE_BLEND_PIXEL_NONE) |
-                                       BIT(DRM_MODE_BLEND_PREMULTI) |
-                                       BIT(DRM_MODE_BLEND_COVERAGE));
-
-               vsp->num_planes++;
-       }
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
deleted file mode 100644 (file)
index 67630f0..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit VSP-Based Compositor
- *
- * Copyright (C) 2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_DU_VSP_H__
-#define __RCAR_DU_VSP_H__
-
-#include <drm/drm_plane.h>
-
-struct drm_framebuffer;
-struct rcar_du_format_info;
-struct rcar_du_vsp;
-struct sg_table;
-
-struct rcar_du_vsp_plane {
-       struct drm_plane plane;
-       struct rcar_du_vsp *vsp;
-       unsigned int index;
-};
-
-struct rcar_du_vsp {
-       unsigned int index;
-       struct device *vsp;
-       struct rcar_du_device *dev;
-       struct rcar_du_vsp_plane *planes;
-       unsigned int num_planes;
-};
-
-static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
-{
-       return container_of(p, struct rcar_du_vsp_plane, plane);
-}
-
-/**
- * struct rcar_du_vsp_plane_state - Driver-specific plane state
- * @state: base DRM plane state
- * @format: information about the pixel format used by the plane
- * @sg_tables: scatter-gather tables for the frame buffer memory
- */
-struct rcar_du_vsp_plane_state {
-       struct drm_plane_state state;
-
-       const struct rcar_du_format_info *format;
-       struct sg_table sg_tables[3];
-};
-
-static inline struct rcar_du_vsp_plane_state *
-to_rcar_vsp_plane_state(struct drm_plane_state *state)
-{
-       return container_of(state, struct rcar_du_vsp_plane_state, state);
-}
-
-#ifdef CONFIG_DRM_RCAR_VSP
-int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
-                    unsigned int crtcs);
-void rcar_du_vsp_enable(struct rcar_du_crtc *crtc);
-void rcar_du_vsp_disable(struct rcar_du_crtc *crtc);
-void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc);
-void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc);
-int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
-                      struct sg_table sg_tables[3]);
-void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
-                         struct sg_table sg_tables[3]);
-#else
-static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp,
-                                  struct device_node *np,
-                                  unsigned int crtcs)
-{
-       return -ENXIO;
-}
-static inline void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) { };
-static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { };
-static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { };
-static inline void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) { };
-static inline int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp,
-                                    struct drm_framebuffer *fb,
-                                    struct sg_table sg_tables[3])
-{
-       return -ENXIO;
-}
-static inline void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp,
-                                       struct drm_framebuffer *fb,
-                                       struct sg_table sg_tables[3])
-{
-}
-#endif
-
-#endif /* __RCAR_DU_VSP_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_writeback.c b/drivers/gpu/drm/rcar-du/rcar_du_writeback.c
deleted file mode 100644 (file)
index 8cd37d7..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * R-Car Display Unit Writeback Support
- *
- * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- */
-
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_device.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_writeback.h>
-
-#include "rcar_du_crtc.h"
-#include "rcar_du_drv.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_writeback.h"
-
-/**
- * struct rcar_du_wb_conn_state - Driver-specific writeback connector state
- * @state: base DRM connector state
- * @format: format of the writeback framebuffer
- */
-struct rcar_du_wb_conn_state {
-       struct drm_connector_state state;
-       const struct rcar_du_format_info *format;
-};
-
-#define to_rcar_wb_conn_state(s) \
-       container_of(s, struct rcar_du_wb_conn_state, state)
-
-/**
- * struct rcar_du_wb_job - Driver-private data for writeback jobs
- * @sg_tables: scatter-gather tables for the framebuffer memory
- */
-struct rcar_du_wb_job {
-       struct sg_table sg_tables[3];
-};
-
-static int rcar_du_wb_conn_get_modes(struct drm_connector *connector)
-{
-       struct drm_device *dev = connector->dev;
-
-       return drm_add_modes_noedid(connector, dev->mode_config.max_width,
-                                   dev->mode_config.max_height);
-}
-
-static int rcar_du_wb_prepare_job(struct drm_writeback_connector *connector,
-                                 struct drm_writeback_job *job)
-{
-       struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
-       struct rcar_du_wb_job *rjob;
-       int ret;
-
-       if (!job->fb)
-               return 0;
-
-       rjob = kzalloc(sizeof(*rjob), GFP_KERNEL);
-       if (!rjob)
-               return -ENOMEM;
-
-       /* Map the framebuffer to the VSP. */
-       ret = rcar_du_vsp_map_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
-       if (ret < 0) {
-               kfree(rjob);
-               return ret;
-       }
-
-       job->priv = rjob;
-       return 0;
-}
-
-static void rcar_du_wb_cleanup_job(struct drm_writeback_connector *connector,
-                                  struct drm_writeback_job *job)
-{
-       struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
-       struct rcar_du_wb_job *rjob = job->priv;
-
-       if (!job->fb)
-               return;
-
-       rcar_du_vsp_unmap_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
-       kfree(rjob);
-}
-
-static const struct drm_connector_helper_funcs rcar_du_wb_conn_helper_funcs = {
-       .get_modes = rcar_du_wb_conn_get_modes,
-       .prepare_writeback_job = rcar_du_wb_prepare_job,
-       .cleanup_writeback_job = rcar_du_wb_cleanup_job,
-};
-
-static struct drm_connector_state *
-rcar_du_wb_conn_duplicate_state(struct drm_connector *connector)
-{
-       struct rcar_du_wb_conn_state *copy;
-
-       if (WARN_ON(!connector->state))
-               return NULL;
-
-       copy = kzalloc(sizeof(*copy), GFP_KERNEL);
-       if (!copy)
-               return NULL;
-
-       __drm_atomic_helper_connector_duplicate_state(connector, &copy->state);
-
-       return &copy->state;
-}
-
-static void rcar_du_wb_conn_destroy_state(struct drm_connector *connector,
-                                         struct drm_connector_state *state)
-{
-       __drm_atomic_helper_connector_destroy_state(state);
-       kfree(to_rcar_wb_conn_state(state));
-}
-
-static void rcar_du_wb_conn_reset(struct drm_connector *connector)
-{
-       struct rcar_du_wb_conn_state *state;
-
-       if (connector->state) {
-               rcar_du_wb_conn_destroy_state(connector, connector->state);
-               connector->state = NULL;
-       }
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (state == NULL)
-               return;
-
-       __drm_atomic_helper_connector_reset(connector, &state->state);
-}
-
-static const struct drm_connector_funcs rcar_du_wb_conn_funcs = {
-       .reset = rcar_du_wb_conn_reset,
-       .fill_modes = drm_helper_probe_single_connector_modes,
-       .destroy = drm_connector_cleanup,
-       .atomic_duplicate_state = rcar_du_wb_conn_duplicate_state,
-       .atomic_destroy_state = rcar_du_wb_conn_destroy_state,
-};
-
-static int rcar_du_wb_enc_atomic_check(struct drm_encoder *encoder,
-                                      struct drm_crtc_state *crtc_state,
-                                      struct drm_connector_state *conn_state)
-{
-       struct rcar_du_wb_conn_state *wb_state =
-               to_rcar_wb_conn_state(conn_state);
-       const struct drm_display_mode *mode = &crtc_state->mode;
-       struct drm_device *dev = encoder->dev;
-       struct drm_framebuffer *fb;
-
-       if (!conn_state->writeback_job)
-               return 0;
-
-       fb = conn_state->writeback_job->fb;
-
-       /*
-        * Verify that the framebuffer format is supported and that its size
-        * matches the current mode.
-        */
-       if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) {
-               dev_dbg(dev->dev, "%s: invalid framebuffer size %ux%u\n",
-                       __func__, fb->width, fb->height);
-               return -EINVAL;
-       }
-
-       wb_state->format = rcar_du_format_info(fb->format->format);
-       if (wb_state->format == NULL) {
-               dev_dbg(dev->dev, "%s: unsupported format %p4cc\n", __func__,
-                       &fb->format->format);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static const struct drm_encoder_helper_funcs rcar_du_wb_enc_helper_funcs = {
-       .atomic_check = rcar_du_wb_enc_atomic_check,
-};
-
-/*
- * Only RGB formats are currently supported as the VSP outputs RGB to the DU
- * and can't convert to YUV separately for writeback.
- */
-static const u32 writeback_formats[] = {
-       DRM_FORMAT_RGB332,
-       DRM_FORMAT_ARGB4444,
-       DRM_FORMAT_XRGB4444,
-       DRM_FORMAT_ARGB1555,
-       DRM_FORMAT_XRGB1555,
-       DRM_FORMAT_RGB565,
-       DRM_FORMAT_BGR888,
-       DRM_FORMAT_RGB888,
-       DRM_FORMAT_BGRA8888,
-       DRM_FORMAT_BGRX8888,
-       DRM_FORMAT_ARGB8888,
-       DRM_FORMAT_XRGB8888,
-};
-
-int rcar_du_writeback_init(struct rcar_du_device *rcdu,
-                          struct rcar_du_crtc *rcrtc)
-{
-       struct drm_writeback_connector *wb_conn = &rcrtc->writeback;
-
-       drm_connector_helper_add(&wb_conn->base,
-                                &rcar_du_wb_conn_helper_funcs);
-
-       return drm_writeback_connector_init(&rcdu->ddev, wb_conn,
-                                           &rcar_du_wb_conn_funcs,
-                                           &rcar_du_wb_enc_helper_funcs,
-                                           writeback_formats,
-                                           ARRAY_SIZE(writeback_formats),
-                                           1 << drm_crtc_index(&rcrtc->crtc));
-}
-
-void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
-                            struct vsp1_du_writeback_config *cfg)
-{
-       struct rcar_du_wb_conn_state *wb_state;
-       struct drm_connector_state *state;
-       struct rcar_du_wb_job *rjob;
-       struct drm_framebuffer *fb;
-       unsigned int i;
-
-       state = rcrtc->writeback.base.state;
-       if (!state || !state->writeback_job)
-               return;
-
-       fb = state->writeback_job->fb;
-       rjob = state->writeback_job->priv;
-       wb_state = to_rcar_wb_conn_state(state);
-
-       cfg->pixelformat = wb_state->format->v4l2;
-       cfg->pitch = fb->pitches[0];
-
-       for (i = 0; i < wb_state->format->planes; ++i)
-               cfg->mem[i] = sg_dma_address(rjob->sg_tables[i].sgl)
-                           + fb->offsets[i];
-
-       drm_writeback_queue_job(&rcrtc->writeback, state);
-}
-
-void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc)
-{
-       drm_writeback_signal_completion(&rcrtc->writeback, 0);
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_writeback.h b/drivers/gpu/drm/rcar-du/rcar_du_writeback.h
deleted file mode 100644 (file)
index a71c9c0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * R-Car Display Unit Writeback Support
- *
- * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- */
-
-#ifndef __RCAR_DU_WRITEBACK_H__
-#define __RCAR_DU_WRITEBACK_H__
-
-#include <drm/drm_plane.h>
-
-struct rcar_du_crtc;
-struct rcar_du_device;
-struct vsp1_du_atomic_pipe_config;
-
-#ifdef CONFIG_DRM_RCAR_WRITEBACK
-int rcar_du_writeback_init(struct rcar_du_device *rcdu,
-                          struct rcar_du_crtc *rcrtc);
-void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
-                            struct vsp1_du_writeback_config *cfg);
-void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc);
-#else
-static inline int rcar_du_writeback_init(struct rcar_du_device *rcdu,
-                                        struct rcar_du_crtc *rcrtc)
-{
-       return -ENXIO;
-}
-static inline void
-rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
-                       struct vsp1_du_writeback_config *cfg)
-{
-}
-static inline void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc)
-{
-}
-#endif
-
-#endif /* __RCAR_DU_WRITEBACK_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
deleted file mode 100644 (file)
index 18ed149..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * R-Car Gen3 HDMI PHY
- *
- * Copyright (C) 2016 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/mod_devicetable.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include <drm/bridge/dw_hdmi.h>
-#include <drm/drm_modes.h>
-
-#define RCAR_HDMI_PHY_OPMODE_PLLCFG    0x06    /* Mode of operation and PLL dividers */
-#define RCAR_HDMI_PHY_PLLCURRGMPCTRL   0x10    /* PLL current and Gmp (conductance) */
-#define RCAR_HDMI_PHY_PLLDIVCTRL       0x11    /* PLL dividers */
-
-struct rcar_hdmi_phy_params {
-       unsigned long mpixelclock;
-       u16 opmode_div; /* Mode of operation and PLL dividers */
-       u16 curr_gmp;   /* PLL current and Gmp (conductance) */
-       u16 div;        /* PLL dividers */
-};
-
-static const struct rcar_hdmi_phy_params rcar_hdmi_phy_params[] = {
-       { 35500000,  0x0003, 0x0344, 0x0328 },
-       { 44900000,  0x0003, 0x0285, 0x0128 },
-       { 71000000,  0x0002, 0x1184, 0x0314 },
-       { 90000000,  0x0002, 0x1144, 0x0114 },
-       { 140250000, 0x0001, 0x20c4, 0x030a },
-       { 182750000, 0x0001, 0x2084, 0x010a },
-       { 281250000, 0x0000, 0x0084, 0x0305 },
-       { 297000000, 0x0000, 0x0084, 0x0105 },
-       { ~0UL,      0x0000, 0x0000, 0x0000 },
-};
-
-static enum drm_mode_status
-rcar_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
-                    const struct drm_display_info *info,
-                    const struct drm_display_mode *mode)
-{
-       /*
-        * The maximum supported clock frequency is 297 MHz, as shown in the PHY
-        * parameters table.
-        */
-       if (mode->clock > 297000)
-               return MODE_CLOCK_HIGH;
-
-       return MODE_OK;
-}
-
-static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, void *data,
-                                  unsigned long mpixelclock)
-{
-       const struct rcar_hdmi_phy_params *params = rcar_hdmi_phy_params;
-
-       for (; params->mpixelclock != ~0UL; ++params) {
-               if (mpixelclock <= params->mpixelclock)
-                       break;
-       }
-
-       if (params->mpixelclock == ~0UL)
-               return -EINVAL;
-
-       dw_hdmi_phy_i2c_write(hdmi, params->opmode_div,
-                             RCAR_HDMI_PHY_OPMODE_PLLCFG);
-       dw_hdmi_phy_i2c_write(hdmi, params->curr_gmp,
-                             RCAR_HDMI_PHY_PLLCURRGMPCTRL);
-       dw_hdmi_phy_i2c_write(hdmi, params->div, RCAR_HDMI_PHY_PLLDIVCTRL);
-
-       return 0;
-}
-
-static const struct dw_hdmi_plat_data rcar_dw_hdmi_plat_data = {
-       .output_port = 1,
-       .mode_valid = rcar_hdmi_mode_valid,
-       .configure_phy  = rcar_hdmi_phy_configure,
-};
-
-static int rcar_dw_hdmi_probe(struct platform_device *pdev)
-{
-       struct dw_hdmi *hdmi;
-
-       hdmi = dw_hdmi_probe(pdev, &rcar_dw_hdmi_plat_data);
-       if (IS_ERR(hdmi))
-               return PTR_ERR(hdmi);
-
-       platform_set_drvdata(pdev, hdmi);
-
-       return 0;
-}
-
-static int rcar_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 rcar_dw_hdmi_of_table[] = {
-       { .compatible = "renesas,rcar-gen3-hdmi" },
-       { /* Sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, rcar_dw_hdmi_of_table);
-
-static struct platform_driver rcar_dw_hdmi_platform_driver = {
-       .probe          = rcar_dw_hdmi_probe,
-       .remove         = rcar_dw_hdmi_remove,
-       .driver         = {
-               .name   = "rcar-dw-hdmi",
-               .of_match_table = rcar_dw_hdmi_of_table,
-       },
-};
-
-module_platform_driver(rcar_dw_hdmi_platform_driver);
-
-MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
-MODULE_DESCRIPTION("Renesas R-Car Gen3 HDMI Encoder Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
deleted file mode 100644 (file)
index ca215b5..0000000
+++ /dev/null
@@ -1,1035 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * R-Car LVDS Encoder
- *
- * Copyright (C) 2013-2018 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/media-bus-format.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/reset.h>
-#include <linux/slab.h>
-#include <linux/sys_soc.h>
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
-#include <drm/drm_of.h>
-#include <drm/drm_panel.h>
-#include <drm/drm_print.h>
-#include <drm/drm_probe_helper.h>
-
-#include "rcar_lvds.h"
-#include "rcar_lvds_regs.h"
-
-struct rcar_lvds;
-
-/* Keep in sync with the LVDCR0.LVMD hardware register values. */
-enum rcar_lvds_mode {
-       RCAR_LVDS_MODE_JEIDA = 0,
-       RCAR_LVDS_MODE_MIRROR = 1,
-       RCAR_LVDS_MODE_VESA = 4,
-};
-
-enum rcar_lvds_link_type {
-       RCAR_LVDS_SINGLE_LINK = 0,
-       RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 1,
-       RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 2,
-};
-
-#define RCAR_LVDS_QUIRK_LANES          BIT(0)  /* LVDS lanes 1 and 3 inverted */
-#define RCAR_LVDS_QUIRK_GEN3_LVEN      BIT(1)  /* LVEN bit needs to be set on R8A77970/R8A7799x */
-#define RCAR_LVDS_QUIRK_PWD            BIT(2)  /* PWD bit available (all of Gen3 but E3) */
-#define RCAR_LVDS_QUIRK_EXT_PLL                BIT(3)  /* Has extended PLL */
-#define RCAR_LVDS_QUIRK_DUAL_LINK      BIT(4)  /* Supports dual-link operation */
-
-struct rcar_lvds_device_info {
-       unsigned int gen;
-       unsigned int quirks;
-       void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq);
-};
-
-struct rcar_lvds {
-       struct device *dev;
-       const struct rcar_lvds_device_info *info;
-       struct reset_control *rstc;
-
-       struct drm_bridge bridge;
-
-       struct drm_bridge *next_bridge;
-       struct drm_panel *panel;
-
-       void __iomem *mmio;
-       struct {
-               struct clk *mod;                /* CPG module clock */
-               struct clk *extal;              /* External clock */
-               struct clk *dotclkin[2];        /* External DU clocks */
-       } clocks;
-
-       struct drm_bridge *companion;
-       enum rcar_lvds_link_type link_type;
-};
-
-#define bridge_to_rcar_lvds(b) \
-       container_of(b, struct rcar_lvds, bridge)
-
-static u32 rcar_lvds_read(struct rcar_lvds *lvds, u32 reg)
-{
-       return ioread32(lvds->mmio + reg);
-}
-
-static void rcar_lvds_write(struct rcar_lvds *lvds, u32 reg, u32 data)
-{
-       iowrite32(data, lvds->mmio + reg);
-}
-
-/* -----------------------------------------------------------------------------
- * PLL Setup
- */
-
-static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int freq)
-{
-       u32 val;
-
-       if (freq < 39000000)
-               val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
-       else if (freq < 61000000)
-               val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
-       else if (freq < 121000000)
-               val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M;
-       else
-               val = LVDPLLCR_PLLDLYCNT_150M;
-
-       rcar_lvds_write(lvds, LVDPLLCR, val);
-}
-
-static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int freq)
-{
-       u32 val;
-
-       if (freq < 42000000)
-               val = LVDPLLCR_PLLDIVCNT_42M;
-       else if (freq < 85000000)
-               val = LVDPLLCR_PLLDIVCNT_85M;
-       else if (freq < 128000000)
-               val = LVDPLLCR_PLLDIVCNT_128M;
-       else
-               val = LVDPLLCR_PLLDIVCNT_148M;
-
-       rcar_lvds_write(lvds, LVDPLLCR, val);
-}
-
-struct pll_info {
-       unsigned long diff;
-       unsigned int pll_m;
-       unsigned int pll_n;
-       unsigned int pll_e;
-       unsigned int div;
-       u32 clksel;
-};
-
-static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk,
-                                    unsigned long target, struct pll_info *pll,
-                                    u32 clksel, bool dot_clock_only)
-{
-       unsigned int div7 = dot_clock_only ? 1 : 7;
-       unsigned long output;
-       unsigned long fin;
-       unsigned int m_min;
-       unsigned int m_max;
-       unsigned int m;
-       int error;
-
-       if (!clk)
-               return;
-
-       /*
-        * The LVDS PLL is made of a pre-divider and a multiplier (strangely
-        * enough called M and N respectively), followed by a post-divider E.
-        *
-        *         ,-----.         ,-----.     ,-----.         ,-----.
-        * Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout
-        *         `-----'     ,-> |     |     `-----'   |     `-----'
-        *                     |   `-----'               |
-        *                     |         ,-----.         |
-        *                     `-------- | 1/N | <-------'
-        *                               `-----'
-        *
-        * The clock output by the PLL is then further divided by a programmable
-        * divider DIV to achieve the desired target frequency. Finally, an
-        * optional fixed /7 divider is used to convert the bit clock to a pixel
-        * clock (as LVDS transmits 7 bits per lane per clock sample).
-        *
-        *          ,-------.     ,-----.     |\
-        * Fout --> | 1/DIV | --> | 1/7 | --> | |
-        *          `-------'  |  `-----'     | | --> dot clock
-        *                     `------------> | |
-        *                                    |/
-        *
-        * The /7 divider is optional, it is enabled when the LVDS PLL is used
-        * to drive the LVDS encoder, and disabled when  used to generate a dot
-        * clock for the DU RGB output, without using the LVDS encoder.
-        *
-        * The PLL allowed input frequency range is 12 MHz to 192 MHz.
-        */
-
-       fin = clk_get_rate(clk);
-       if (fin < 12000000 || fin > 192000000)
-               return;
-
-       /*
-        * The comparison frequency range is 12 MHz to 24 MHz, which limits the
-        * allowed values for the pre-divider M (normal range 1-8).
-        *
-        * Fpfd = Fin / M
-        */
-       m_min = max_t(unsigned int, 1, DIV_ROUND_UP(fin, 24000000));
-       m_max = min_t(unsigned int, 8, fin / 12000000);
-
-       for (m = m_min; m <= m_max; ++m) {
-               unsigned long fpfd;
-               unsigned int n_min;
-               unsigned int n_max;
-               unsigned int n;
-
-               /*
-                * The VCO operating range is 900 Mhz to 1800 MHz, which limits
-                * the allowed values for the multiplier N (normal range
-                * 60-120).
-                *
-                * Fvco = Fin * N / M
-                */
-               fpfd = fin / m;
-               n_min = max_t(unsigned int, 60, DIV_ROUND_UP(900000000, fpfd));
-               n_max = min_t(unsigned int, 120, 1800000000 / fpfd);
-
-               for (n = n_min; n < n_max; ++n) {
-                       unsigned long fvco;
-                       unsigned int e_min;
-                       unsigned int e;
-
-                       /*
-                        * The output frequency is limited to 1039.5 MHz,
-                        * limiting again the allowed values for the
-                        * post-divider E (normal value 1, 2 or 4).
-                        *
-                        * Fout = Fvco / E
-                        */
-                       fvco = fpfd * n;
-                       e_min = fvco > 1039500000 ? 1 : 0;
-
-                       for (e = e_min; e < 3; ++e) {
-                               unsigned long fout;
-                               unsigned long diff;
-                               unsigned int div;
-
-                               /*
-                                * Finally we have a programable divider after
-                                * the PLL, followed by a an optional fixed /7
-                                * divider.
-                                */
-                               fout = fvco / (1 << e) / div7;
-                               div = max(1UL, DIV_ROUND_CLOSEST(fout, target));
-                               diff = abs(fout / div - target);
-
-                               if (diff < pll->diff) {
-                                       pll->diff = diff;
-                                       pll->pll_m = m;
-                                       pll->pll_n = n;
-                                       pll->pll_e = e;
-                                       pll->div = div;
-                                       pll->clksel = clksel;
-
-                                       if (diff == 0)
-                                               goto done;
-                               }
-                       }
-               }
-       }
-
-done:
-       output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e)
-              / div7 / pll->div;
-       error = (long)(output - target) * 10000 / (long)target;
-
-       dev_dbg(lvds->dev,
-               "%pC %lu Hz -> Fout %lu Hz (target %lu Hz, error %d.%02u%%), PLL M/N/E/DIV %u/%u/%u/%u\n",
-               clk, fin, output, target, error / 100,
-               error < 0 ? -error % 100 : error % 100,
-               pll->pll_m, pll->pll_n, pll->pll_e, pll->div);
-}
-
-static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds,
-                                     unsigned int freq, bool dot_clock_only)
-{
-       struct pll_info pll = { .diff = (unsigned long)-1 };
-       u32 lvdpllcr;
-
-       rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll,
-                                LVDPLLCR_CKSEL_DU_DOTCLKIN(0), dot_clock_only);
-       rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll,
-                                LVDPLLCR_CKSEL_DU_DOTCLKIN(1), dot_clock_only);
-       rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll,
-                                LVDPLLCR_CKSEL_EXTAL, dot_clock_only);
-
-       lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT
-                | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1);
-
-       if (pll.pll_e > 0)
-               lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL
-                        |  LVDPLLCR_PLLE(pll.pll_e - 1);
-
-       if (dot_clock_only)
-               lvdpllcr |= LVDPLLCR_OCKSEL;
-
-       rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
-
-       if (pll.div > 1)
-               /*
-                * The DIVRESET bit is a misnomer, setting it to 1 deasserts the
-                * divisor reset.
-                */
-               rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL |
-                               LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1));
-       else
-               rcar_lvds_write(lvds, LVDDIV, 0);
-}
-
-/* -----------------------------------------------------------------------------
- * Enable/disable
- */
-
-static enum rcar_lvds_mode rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds,
-                                       const struct drm_connector *connector)
-{
-       const struct drm_display_info *info;
-       enum rcar_lvds_mode mode;
-
-       /*
-        * There is no API yet to retrieve LVDS mode from a bridge, only panels
-        * are supported.
-        */
-       if (!lvds->panel)
-               return RCAR_LVDS_MODE_JEIDA;
-
-       info = &connector->display_info;
-       if (!info->num_bus_formats || !info->bus_formats) {
-               dev_warn(lvds->dev,
-                        "no LVDS bus format reported, using JEIDA\n");
-               return RCAR_LVDS_MODE_JEIDA;
-       }
-
-       switch (info->bus_formats[0]) {
-       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
-       case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
-               mode = RCAR_LVDS_MODE_JEIDA;
-               break;
-       case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
-               mode = RCAR_LVDS_MODE_VESA;
-               break;
-       default:
-               dev_warn(lvds->dev,
-                        "unsupported LVDS bus format 0x%04x, using JEIDA\n",
-                        info->bus_formats[0]);
-               return RCAR_LVDS_MODE_JEIDA;
-       }
-
-       if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
-               mode |= RCAR_LVDS_MODE_MIRROR;
-
-       return mode;
-}
-
-static void rcar_lvds_enable(struct drm_bridge *bridge,
-                            struct drm_atomic_state *state,
-                            struct drm_crtc *crtc,
-                            struct drm_connector *connector)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-       u32 lvdhcr;
-       u32 lvdcr0;
-       int ret;
-
-       ret = pm_runtime_resume_and_get(lvds->dev);
-       if (ret)
-               return;
-
-       /* Enable the companion LVDS encoder in dual-link mode. */
-       if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion)
-               rcar_lvds_enable(lvds->companion, state, crtc, connector);
-
-       /*
-        * Hardcode the channels and control signals routing for now.
-        *
-        * HSYNC -> CTRL0
-        * VSYNC -> CTRL1
-        * DISP  -> CTRL2
-        * 0     -> CTRL3
-        */
-       rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
-                       LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
-                       LVDCTRCR_CTR0SEL_HSYNC);
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_LANES)
-               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
-                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
-       else
-               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
-                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
-
-       rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) {
-               u32 lvdstripe = 0;
-
-               if (lvds->link_type != RCAR_LVDS_SINGLE_LINK) {
-                       /*
-                        * By default we generate even pixels from the primary
-                        * encoder and odd pixels from the companion encoder.
-                        * Swap pixels around if the sink requires odd pixels
-                        * from the primary encoder and even pixels from the
-                        * companion encoder.
-                        */
-                       bool swap_pixels = lvds->link_type ==
-                               RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
-
-                       /*
-                        * Configure vertical stripe since we are dealing with
-                        * an LVDS dual-link connection.
-                        *
-                        * ST_SWAP is reserved for the companion encoder, only
-                        * set it in the primary encoder.
-                        */
-                       lvdstripe = LVDSTRIPE_ST_ON
-                                 | (lvds->companion && swap_pixels ?
-                                    LVDSTRIPE_ST_SWAP : 0);
-               }
-               rcar_lvds_write(lvds, LVDSTRIPE, lvdstripe);
-       }
-
-       /*
-        * PLL clock configuration on all instances but the companion in
-        * dual-link mode.
-        *
-        * The extended PLL has been turned on by an explicit call to
-        * rcar_lvds_pclk_enable() from the DU driver.
-        */
-       if ((lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) &&
-           !(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
-               const struct drm_crtc_state *crtc_state =
-                       drm_atomic_get_new_crtc_state(state, crtc);
-               const struct drm_display_mode *mode =
-                       &crtc_state->adjusted_mode;
-
-               lvds->info->pll_setup(lvds, mode->clock * 1000);
-       }
-
-       /* Set the LVDS mode and select the input. */
-       lvdcr0 = rcar_lvds_get_lvds_mode(lvds, connector) << LVDCR0_LVMD_SHIFT;
-
-       if (lvds->bridge.encoder) {
-               if (drm_crtc_index(crtc) == 2)
-                       lvdcr0 |= LVDCR0_DUSEL;
-       }
-
-       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-
-       /* Turn all the channels on. */
-       rcar_lvds_write(lvds, LVDCR1,
-                       LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
-                       LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
-
-       if (lvds->info->gen < 3) {
-               /* Enable LVDS operation and turn the bias circuitry on. */
-               lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
-               /*
-                * Turn the PLL on (simple PLL only, extended PLL is fully
-                * controlled through LVDPLLCR).
-                */
-               lvdcr0 |= LVDCR0_PLLON;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) {
-               /* Set LVDS normal mode. */
-               lvdcr0 |= LVDCR0_PWD;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) {
-               /*
-                * Turn on the LVDS PHY. On D3, the LVEN and LVRES bit must be
-                * set at the same time, so don't write the register yet.
-                */
-               lvdcr0 |= LVDCR0_LVEN;
-               if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_PWD))
-                       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
-               /* Wait for the PLL startup delay (simple PLL only). */
-               usleep_range(100, 150);
-       }
-
-       /* Turn the output on. */
-       lvdcr0 |= LVDCR0_LVRES;
-       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-}
-
-static void rcar_lvds_disable(struct drm_bridge *bridge)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-       u32 lvdcr0;
-
-       /*
-        * Clear the LVDCR0 bits in the order specified by the hardware
-        * documentation, ending with a write of 0 to the full register to
-        * clear all remaining bits.
-        */
-       lvdcr0 = rcar_lvds_read(lvds, LVDCR0);
-
-       lvdcr0 &= ~LVDCR0_LVRES;
-       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) {
-               lvdcr0 &= ~LVDCR0_LVEN;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) {
-               lvdcr0 &= ~LVDCR0_PWD;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
-               lvdcr0 &= ~LVDCR0_PLLON;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       rcar_lvds_write(lvds, LVDCR0, 0);
-       rcar_lvds_write(lvds, LVDCR1, 0);
-
-       /* The extended PLL is turned off in rcar_lvds_pclk_disable(). */
-       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))
-               rcar_lvds_write(lvds, LVDPLLCR, 0);
-
-       /* Disable the companion LVDS encoder in dual-link mode. */
-       if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion)
-               rcar_lvds_disable(lvds->companion);
-
-       pm_runtime_put_sync(lvds->dev);
-}
-
-/* -----------------------------------------------------------------------------
- * Clock - D3/E3 only
- */
-
-int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq,
-                         bool dot_clk_only)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-       int ret;
-
-       if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)))
-               return -ENODEV;
-
-       dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq);
-
-       ret = pm_runtime_resume_and_get(lvds->dev);
-       if (ret)
-               return ret;
-
-       rcar_lvds_pll_setup_d3_e3(lvds, freq, dot_clk_only);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rcar_lvds_pclk_enable);
-
-void rcar_lvds_pclk_disable(struct drm_bridge *bridge, bool dot_clk_only)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-
-       if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)))
-               return;
-
-       dev_dbg(lvds->dev, "disabling LVDS PLL\n");
-
-       if (!dot_clk_only)
-               rcar_lvds_disable(bridge);
-
-       rcar_lvds_write(lvds, LVDPLLCR, 0);
-
-       pm_runtime_put_sync(lvds->dev);
-}
-EXPORT_SYMBOL_GPL(rcar_lvds_pclk_disable);
-
-/* -----------------------------------------------------------------------------
- * Bridge
- */
-
-static void rcar_lvds_atomic_enable(struct drm_bridge *bridge,
-                                   struct drm_bridge_state *old_bridge_state)
-{
-       struct drm_atomic_state *state = old_bridge_state->base.state;
-       struct drm_connector *connector;
-       struct drm_crtc *crtc;
-
-       connector = drm_atomic_get_new_connector_for_encoder(state,
-                                                            bridge->encoder);
-       crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
-
-       rcar_lvds_enable(bridge, state, crtc, connector);
-}
-
-static void rcar_lvds_atomic_disable(struct drm_bridge *bridge,
-                                    struct drm_bridge_state *old_bridge_state)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-
-       /*
-        * For D3 and E3, disabling the LVDS encoder before the DU would stall
-        * the DU, causing a vblank wait timeout when stopping the DU. This has
-        * been traced to clearing the LVEN bit, but the exact reason is
-        * unknown. Keep the encoder enabled, it will be disabled by an explicit
-        * call to rcar_lvds_pclk_disable() from the DU driver.
-        *
-        * We could clear the LVRES bit already to disable the LVDS output, but
-        * that's likely pointless.
-        */
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)
-               return;
-
-       rcar_lvds_disable(bridge);
-}
-
-static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge,
-                                const struct drm_display_mode *mode,
-                                struct drm_display_mode *adjusted_mode)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-       int min_freq;
-
-       /*
-        * The internal LVDS encoder has a restricted clock frequency operating
-        * range, from 5MHz to 148.5MHz on D3 and E3, and from 31MHz to
-        * 148.5MHz on all other platforms. Clamp the clock accordingly.
-        */
-       min_freq = lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL ? 5000 : 31000;
-       adjusted_mode->clock = clamp(adjusted_mode->clock, min_freq, 148500);
-
-       return true;
-}
-
-static int rcar_lvds_attach(struct drm_bridge *bridge,
-                           enum drm_bridge_attach_flags flags)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-
-       if (!lvds->next_bridge)
-               return 0;
-
-       return drm_bridge_attach(bridge->encoder, lvds->next_bridge, bridge,
-                                flags);
-}
-
-static const struct drm_bridge_funcs rcar_lvds_bridge_ops = {
-       .attach = rcar_lvds_attach,
-       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
-       .atomic_reset = drm_atomic_helper_bridge_reset,
-       .atomic_enable = rcar_lvds_atomic_enable,
-       .atomic_disable = rcar_lvds_atomic_disable,
-       .mode_fixup = rcar_lvds_mode_fixup,
-};
-
-bool rcar_lvds_dual_link(struct drm_bridge *bridge)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-
-       return lvds->link_type != RCAR_LVDS_SINGLE_LINK;
-}
-EXPORT_SYMBOL_GPL(rcar_lvds_dual_link);
-
-bool rcar_lvds_is_connected(struct drm_bridge *bridge)
-{
-       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-
-       return lvds->next_bridge != NULL;
-}
-EXPORT_SYMBOL_GPL(rcar_lvds_is_connected);
-
-/* -----------------------------------------------------------------------------
- * Probe & Remove
- */
-
-static int rcar_lvds_parse_dt_companion(struct rcar_lvds *lvds)
-{
-       const struct of_device_id *match;
-       struct device_node *companion;
-       struct device_node *port0, *port1;
-       struct rcar_lvds *companion_lvds;
-       struct device *dev = lvds->dev;
-       int dual_link;
-       int ret = 0;
-
-       /* Locate the companion LVDS encoder for dual-link operation, if any. */
-       companion = of_parse_phandle(dev->of_node, "renesas,companion", 0);
-       if (!companion)
-               return 0;
-
-       /*
-        * Sanity check: the companion encoder must have the same compatible
-        * string.
-        */
-       match = of_match_device(dev->driver->of_match_table, dev);
-       if (!of_device_is_compatible(companion, match->compatible)) {
-               dev_err(dev, "Companion LVDS encoder is invalid\n");
-               ret = -ENXIO;
-               goto done;
-       }
-
-       /*
-        * We need to work out if the sink is expecting us to function in
-        * dual-link mode. We do this by looking at the DT port nodes we are
-        * connected to, if they are marked as expecting even pixels and
-        * odd pixels than we need to enable vertical stripe output.
-        */
-       port0 = of_graph_get_port_by_id(dev->of_node, 1);
-       port1 = of_graph_get_port_by_id(companion, 1);
-       dual_link = drm_of_lvds_get_dual_link_pixel_order(port0, port1);
-       of_node_put(port0);
-       of_node_put(port1);
-
-       switch (dual_link) {
-       case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
-               lvds->link_type = RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
-               break;
-       case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
-               lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
-               break;
-       default:
-               /*
-                * Early dual-link bridge specific implementations populate the
-                * timings field of drm_bridge. If the flag is set, we assume
-                * that we are expected to generate even pixels from the primary
-                * encoder, and odd pixels from the companion encoder.
-                */
-               if (lvds->next_bridge->timings &&
-                   lvds->next_bridge->timings->dual_link)
-                       lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
-               else
-                       lvds->link_type = RCAR_LVDS_SINGLE_LINK;
-       }
-
-       if (lvds->link_type == RCAR_LVDS_SINGLE_LINK) {
-               dev_dbg(dev, "Single-link configuration detected\n");
-               goto done;
-       }
-
-       lvds->companion = of_drm_find_bridge(companion);
-       if (!lvds->companion) {
-               ret = -EPROBE_DEFER;
-               goto done;
-       }
-
-       dev_dbg(dev,
-               "Dual-link configuration detected (companion encoder %pOF)\n",
-               companion);
-
-       if (lvds->link_type == RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
-               dev_dbg(dev, "Data swapping required\n");
-
-       /*
-        * FIXME: We should not be messing with the companion encoder private
-        * data from the primary encoder, we should rather let the companion
-        * encoder work things out on its own. However, the companion encoder
-        * doesn't hold a reference to the primary encoder, and
-        * drm_of_lvds_get_dual_link_pixel_order needs to be given references
-        * to the output ports of both encoders, therefore leave it like this
-        * for the time being.
-        */
-       companion_lvds = bridge_to_rcar_lvds(lvds->companion);
-       companion_lvds->link_type = lvds->link_type;
-
-done:
-       of_node_put(companion);
-
-       return ret;
-}
-
-static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
-{
-       int ret;
-
-       ret = drm_of_find_panel_or_bridge(lvds->dev->of_node, 1, 0,
-                                         &lvds->panel, &lvds->next_bridge);
-       if (ret)
-               goto done;
-
-       if (lvds->panel) {
-               lvds->next_bridge = devm_drm_panel_bridge_add(lvds->dev,
-                                                             lvds->panel);
-               if (IS_ERR_OR_NULL(lvds->next_bridge)) {
-                       ret = -EINVAL;
-                       goto done;
-               }
-       }
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK)
-               ret = rcar_lvds_parse_dt_companion(lvds);
-
-done:
-       /*
-        * On D3/E3 the LVDS encoder provides a clock to the DU, which can be
-        * used for the DPAD output even when the LVDS output is not connected.
-        * Don't fail probe in that case as the DU will need the bridge to
-        * control the clock.
-        */
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)
-               return ret == -ENODEV ? 0 : ret;
-
-       return ret;
-}
-
-static struct clk *rcar_lvds_get_clock(struct rcar_lvds *lvds, const char *name,
-                                      bool optional)
-{
-       struct clk *clk;
-
-       clk = devm_clk_get(lvds->dev, name);
-       if (!IS_ERR(clk))
-               return clk;
-
-       if (PTR_ERR(clk) == -ENOENT && optional)
-               return NULL;
-
-       dev_err_probe(lvds->dev, PTR_ERR(clk), "failed to get %s clock\n",
-                     name ? name : "module");
-
-       return clk;
-}
-
-static int rcar_lvds_get_clocks(struct rcar_lvds *lvds)
-{
-       lvds->clocks.mod = rcar_lvds_get_clock(lvds, NULL, false);
-       if (IS_ERR(lvds->clocks.mod))
-               return PTR_ERR(lvds->clocks.mod);
-
-       /*
-        * LVDS encoders without an extended PLL have no external clock inputs.
-        */
-       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))
-               return 0;
-
-       lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true);
-       if (IS_ERR(lvds->clocks.extal))
-               return PTR_ERR(lvds->clocks.extal);
-
-       lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true);
-       if (IS_ERR(lvds->clocks.dotclkin[0]))
-               return PTR_ERR(lvds->clocks.dotclkin[0]);
-
-       lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true);
-       if (IS_ERR(lvds->clocks.dotclkin[1]))
-               return PTR_ERR(lvds->clocks.dotclkin[1]);
-
-       /* At least one input to the PLL must be available. */
-       if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] &&
-           !lvds->clocks.dotclkin[1]) {
-               dev_err(lvds->dev,
-                       "no input clock (extal, dclkin.0 or dclkin.1)\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static const struct rcar_lvds_device_info rcar_lvds_r8a7790es1_info = {
-       .gen = 2,
-       .quirks = RCAR_LVDS_QUIRK_LANES,
-       .pll_setup = rcar_lvds_pll_setup_gen2,
-};
-
-static const struct soc_device_attribute lvds_quirk_matches[] = {
-       {
-               .soc_id = "r8a7790", .revision = "ES1.*",
-               .data = &rcar_lvds_r8a7790es1_info,
-       },
-       { /* sentinel */ }
-};
-
-static int rcar_lvds_probe(struct platform_device *pdev)
-{
-       const struct soc_device_attribute *attr;
-       struct rcar_lvds *lvds;
-       int ret;
-
-       lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
-       if (lvds == NULL)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, lvds);
-
-       lvds->dev = &pdev->dev;
-       lvds->info = of_device_get_match_data(&pdev->dev);
-
-       attr = soc_device_match(lvds_quirk_matches);
-       if (attr)
-               lvds->info = attr->data;
-
-       ret = rcar_lvds_parse_dt(lvds);
-       if (ret < 0)
-               return ret;
-
-       lvds->bridge.funcs = &rcar_lvds_bridge_ops;
-       lvds->bridge.of_node = pdev->dev.of_node;
-
-       lvds->mmio = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(lvds->mmio))
-               return PTR_ERR(lvds->mmio);
-
-       ret = rcar_lvds_get_clocks(lvds);
-       if (ret < 0)
-               return ret;
-
-       lvds->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
-       if (IS_ERR(lvds->rstc))
-               return dev_err_probe(&pdev->dev, PTR_ERR(lvds->rstc),
-                                    "failed to get cpg reset\n");
-
-       pm_runtime_enable(&pdev->dev);
-
-       drm_bridge_add(&lvds->bridge);
-
-       return 0;
-}
-
-static int rcar_lvds_remove(struct platform_device *pdev)
-{
-       struct rcar_lvds *lvds = platform_get_drvdata(pdev);
-
-       drm_bridge_remove(&lvds->bridge);
-
-       pm_runtime_disable(&pdev->dev);
-
-       return 0;
-}
-
-static const struct rcar_lvds_device_info rcar_lvds_gen2_info = {
-       .gen = 2,
-       .pll_setup = rcar_lvds_pll_setup_gen2,
-};
-
-static const struct rcar_lvds_device_info rcar_lvds_gen3_info = {
-       .gen = 3,
-       .quirks = RCAR_LVDS_QUIRK_PWD,
-       .pll_setup = rcar_lvds_pll_setup_gen3,
-};
-
-static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = {
-       .gen = 3,
-       .quirks = RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_GEN3_LVEN,
-       .pll_setup = rcar_lvds_pll_setup_gen2,
-};
-
-static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = {
-       .gen = 3,
-       .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL
-               | RCAR_LVDS_QUIRK_DUAL_LINK,
-};
-
-static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = {
-       .gen = 3,
-       .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD
-               | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK,
-};
-
-static const struct of_device_id rcar_lvds_of_table[] = {
-       { .compatible = "renesas,r8a7742-lvds", .data = &rcar_lvds_gen2_info },
-       { .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info },
-       { .compatible = "renesas,r8a7744-lvds", .data = &rcar_lvds_gen2_info },
-       { .compatible = "renesas,r8a774a1-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a774b1-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a774c0-lvds", .data = &rcar_lvds_r8a77990_info },
-       { .compatible = "renesas,r8a774e1-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_gen2_info },
-       { .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info },
-       { .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info },
-       { .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a77961-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a77965-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info },
-       { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info },
-       { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info },
-       { .compatible = "renesas,r8a77995-lvds", .data = &rcar_lvds_r8a77995_info },
-       { }
-};
-
-MODULE_DEVICE_TABLE(of, rcar_lvds_of_table);
-
-static int rcar_lvds_runtime_suspend(struct device *dev)
-{
-       struct rcar_lvds *lvds = dev_get_drvdata(dev);
-
-       clk_disable_unprepare(lvds->clocks.mod);
-
-       reset_control_assert(lvds->rstc);
-
-       return 0;
-}
-
-static int rcar_lvds_runtime_resume(struct device *dev)
-{
-       struct rcar_lvds *lvds = dev_get_drvdata(dev);
-       int ret;
-
-       ret = reset_control_deassert(lvds->rstc);
-       if (ret)
-               return ret;
-
-       ret = clk_prepare_enable(lvds->clocks.mod);
-       if (ret < 0)
-               goto err_reset_assert;
-
-       return 0;
-
-err_reset_assert:
-       reset_control_assert(lvds->rstc);
-
-       return ret;
-}
-
-static const struct dev_pm_ops rcar_lvds_pm_ops = {
-       SET_RUNTIME_PM_OPS(rcar_lvds_runtime_suspend, rcar_lvds_runtime_resume, NULL)
-};
-
-static struct platform_driver rcar_lvds_platform_driver = {
-       .probe          = rcar_lvds_probe,
-       .remove         = rcar_lvds_remove,
-       .driver         = {
-               .name   = "rcar-lvds",
-               .pm     = &rcar_lvds_pm_ops,
-               .of_match_table = rcar_lvds_of_table,
-       },
-};
-
-module_platform_driver(rcar_lvds_platform_driver);
-
-MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
-MODULE_DESCRIPTION("Renesas R-Car LVDS Encoder Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.h b/drivers/gpu/drm/rcar-du/rcar_lvds.h
deleted file mode 100644 (file)
index 887c635..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * R-Car LVDS Encoder
- *
- * Copyright (C) 2013-2018 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_LVDS_H__
-#define __RCAR_LVDS_H__
-
-struct drm_bridge;
-
-#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
-int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq,
-                         bool dot_clk_only);
-void rcar_lvds_pclk_disable(struct drm_bridge *bridge, bool dot_clk_only);
-bool rcar_lvds_dual_link(struct drm_bridge *bridge);
-bool rcar_lvds_is_connected(struct drm_bridge *bridge);
-#else
-static inline int rcar_lvds_pclk_enable(struct drm_bridge *bridge,
-                                       unsigned long freq, bool dot_clk_only)
-{
-       return -ENOSYS;
-}
-static inline void rcar_lvds_pclk_disable(struct drm_bridge *bridge,
-                                         bool dot_clock_only)
-{
-}
-static inline bool rcar_lvds_dual_link(struct drm_bridge *bridge)
-{
-       return false;
-}
-static inline bool rcar_lvds_is_connected(struct drm_bridge *bridge)
-{
-       return false;
-}
-#endif /* CONFIG_DRM_RCAR_LVDS */
-
-#endif /* __RCAR_LVDS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
deleted file mode 100644 (file)
index ab0406a..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * R-Car LVDS Interface Registers Definitions
- *
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __RCAR_LVDS_REGS_H__
-#define __RCAR_LVDS_REGS_H__
-
-#define LVDCR0                         0x0000
-#define LVDCR0_DUSEL                   (1 << 15)
-#define LVDCR0_DMD                     (1 << 12)               /* Gen2 only */
-#define LVDCR0_LVMD_MASK               (0xf << 8)
-#define LVDCR0_LVMD_SHIFT              8
-#define LVDCR0_PLLON                   (1 << 4)
-#define LVDCR0_PWD                     (1 << 2)                /* Gen3 only */
-#define LVDCR0_BEN                     (1 << 2)                /* Gen2 only */
-#define LVDCR0_LVEN                    (1 << 1)
-#define LVDCR0_LVRES                   (1 << 0)
-
-#define LVDCR1                         0x0004
-#define LVDCR1_CKSEL                   (1 << 15)               /* Gen2 only */
-#define LVDCR1_CHSTBY(n)               (3 << (2 + (n) * 2))
-#define LVDCR1_CLKSTBY                 (3 << 0)
-
-#define LVDPLLCR                       0x0008
-/* Gen2 & V3M */
-#define LVDPLLCR_CEEN                  (1 << 14)
-#define LVDPLLCR_FBEN                  (1 << 13)
-#define LVDPLLCR_COSEL                 (1 << 12)
-#define LVDPLLCR_PLLDLYCNT_150M                (0x1bf << 0)
-#define LVDPLLCR_PLLDLYCNT_121M                (0x22c << 0)
-#define LVDPLLCR_PLLDLYCNT_60M         (0x77b << 0)
-#define LVDPLLCR_PLLDLYCNT_38M         (0x69a << 0)
-#define LVDPLLCR_PLLDLYCNT_MASK                (0x7ff << 0)
-/* Gen3 but V3M,D3 and E3 */
-#define LVDPLLCR_PLLDIVCNT_42M         (0x014cb << 0)
-#define LVDPLLCR_PLLDIVCNT_85M         (0x00a45 << 0)
-#define LVDPLLCR_PLLDIVCNT_128M                (0x006c3 << 0)
-#define LVDPLLCR_PLLDIVCNT_148M                (0x046c1 << 0)
-#define LVDPLLCR_PLLDIVCNT_MASK                (0x7ffff << 0)
-/* D3 and E3 */
-#define LVDPLLCR_PLLON                 (1 << 22)
-#define LVDPLLCR_PLLSEL_PLL0           (0 << 20)
-#define LVDPLLCR_PLLSEL_LVX            (1 << 20)
-#define LVDPLLCR_PLLSEL_PLL1           (2 << 20)
-#define LVDPLLCR_CKSEL_LVX             (1 << 17)
-#define LVDPLLCR_CKSEL_EXTAL           (3 << 17)
-#define LVDPLLCR_CKSEL_DU_DOTCLKIN(n)  ((5 + (n) * 2) << 17)
-#define LVDPLLCR_OCKSEL                        (1 << 16)
-#define LVDPLLCR_STP_CLKOUTE           (1 << 14)
-#define LVDPLLCR_OUTCLKSEL             (1 << 12)
-#define LVDPLLCR_CLKOUT                        (1 << 11)
-#define LVDPLLCR_PLLE(n)               ((n) << 10)
-#define LVDPLLCR_PLLN(n)               ((n) << 3)
-#define LVDPLLCR_PLLM(n)               ((n) << 0)
-
-#define LVDCTRCR                       0x000c
-#define LVDCTRCR_CTR3SEL_ZERO          (0 << 12)
-#define LVDCTRCR_CTR3SEL_ODD           (1 << 12)
-#define LVDCTRCR_CTR3SEL_CDE           (2 << 12)
-#define LVDCTRCR_CTR3SEL_MASK          (7 << 12)
-#define LVDCTRCR_CTR2SEL_DISP          (0 << 8)
-#define LVDCTRCR_CTR2SEL_ODD           (1 << 8)
-#define LVDCTRCR_CTR2SEL_CDE           (2 << 8)
-#define LVDCTRCR_CTR2SEL_HSYNC         (3 << 8)
-#define LVDCTRCR_CTR2SEL_VSYNC         (4 << 8)
-#define LVDCTRCR_CTR2SEL_MASK          (7 << 8)
-#define LVDCTRCR_CTR1SEL_VSYNC         (0 << 4)
-#define LVDCTRCR_CTR1SEL_DISP          (1 << 4)
-#define LVDCTRCR_CTR1SEL_ODD           (2 << 4)
-#define LVDCTRCR_CTR1SEL_CDE           (3 << 4)
-#define LVDCTRCR_CTR1SEL_HSYNC         (4 << 4)
-#define LVDCTRCR_CTR1SEL_MASK          (7 << 4)
-#define LVDCTRCR_CTR0SEL_HSYNC         (0 << 0)
-#define LVDCTRCR_CTR0SEL_VSYNC         (1 << 0)
-#define LVDCTRCR_CTR0SEL_DISP          (2 << 0)
-#define LVDCTRCR_CTR0SEL_ODD           (3 << 0)
-#define LVDCTRCR_CTR0SEL_CDE           (4 << 0)
-#define LVDCTRCR_CTR0SEL_MASK          (7 << 0)
-
-#define LVDCHCR                                0x0010
-#define LVDCHCR_CHSEL_CH(n, c)         ((((c) - (n)) & 3) << ((n) * 4))
-#define LVDCHCR_CHSEL_MASK(n)          (3 << ((n) * 4))
-
-/* All registers below are specific to D3 and E3 */
-#define LVDSTRIPE                      0x0014
-#define LVDSTRIPE_ST_TRGSEL_DISP       (0 << 2)
-#define LVDSTRIPE_ST_TRGSEL_HSYNC_R    (1 << 2)
-#define LVDSTRIPE_ST_TRGSEL_HSYNC_F    (2 << 2)
-#define LVDSTRIPE_ST_SWAP              (1 << 1)
-#define LVDSTRIPE_ST_ON                        (1 << 0)
-
-#define LVDSCR                         0x0018
-#define LVDSCR_DEPTH(n)                        (((n) - 1) << 29)
-#define LVDSCR_BANDSET                 (1 << 28)
-#define LVDSCR_TWGCNT(n)               ((((n) - 256) / 16) << 24)
-#define LVDSCR_SDIV(n)                 ((n) << 22)
-#define LVDSCR_MODE                    (1 << 21)
-#define LVDSCR_RSTN                    (1 << 20)
-
-#define LVDDIV                         0x001c
-#define LVDDIV_DIVSEL                  (1 << 8)
-#define LVDDIV_DIVRESET                        (1 << 7)
-#define LVDDIV_DIVSTP                  (1 << 6)
-#define LVDDIV_DIV(n)                  ((n) << 0)
-
-#endif /* __RCAR_LVDS_REGS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c
deleted file mode 100644 (file)
index e10e4d4..0000000
+++ /dev/null
@@ -1,1106 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * R-Car MIPI DSI Encoder
- *
- * Copyright (C) 2020 Renesas Electronics Corporation
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/math64.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/reset.h>
-#include <linux/slab.h>
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_of.h>
-#include <drm/drm_panel.h>
-#include <drm/drm_probe_helper.h>
-
-#include "rcar_mipi_dsi.h"
-#include "rcar_mipi_dsi_regs.h"
-
-#define MHZ(v) ((u32)((v) * 1000000U))
-
-enum rcar_mipi_dsi_hw_model {
-       RCAR_DSI_V3U,
-       RCAR_DSI_V4H,
-};
-
-struct rcar_mipi_dsi_device_info {
-       enum rcar_mipi_dsi_hw_model model;
-
-       const struct dsi_clk_config *clk_cfg;
-
-       u8 clockset2_m_offset;
-
-       u8 n_min;
-       u8 n_max;
-       u8 n_mul;
-       unsigned long fpfd_min;
-       unsigned long fpfd_max;
-       u16 m_min;
-       u16 m_max;
-       unsigned long fout_min;
-       unsigned long fout_max;
-};
-
-struct rcar_mipi_dsi {
-       struct device *dev;
-       const struct rcar_mipi_dsi_device_info *info;
-       struct reset_control *rstc;
-
-       struct mipi_dsi_host host;
-       struct drm_bridge bridge;
-       struct drm_bridge *next_bridge;
-       struct drm_connector connector;
-
-       void __iomem *mmio;
-       struct {
-               struct clk *mod;
-               struct clk *pll;
-               struct clk *dsi;
-       } clocks;
-
-       enum mipi_dsi_pixel_format format;
-       unsigned int num_data_lanes;
-       unsigned int lanes;
-};
-
-struct dsi_setup_info {
-       unsigned long hsfreq;
-       u16 hsfreqrange;
-
-       unsigned long fout;
-       u16 m;
-       u16 n;
-       u16 vclk_divider;
-       const struct dsi_clk_config *clkset;
-};
-
-static inline struct rcar_mipi_dsi *
-bridge_to_rcar_mipi_dsi(struct drm_bridge *bridge)
-{
-       return container_of(bridge, struct rcar_mipi_dsi, bridge);
-}
-
-static inline struct rcar_mipi_dsi *
-host_to_rcar_mipi_dsi(struct mipi_dsi_host *host)
-{
-       return container_of(host, struct rcar_mipi_dsi, host);
-}
-
-static const u32 hsfreqrange_table[][2] = {
-       {   MHZ(80), 0x00 }, {   MHZ(90), 0x10 }, {  MHZ(100), 0x20 },
-       {  MHZ(110), 0x30 }, {  MHZ(120), 0x01 }, {  MHZ(130), 0x11 },
-       {  MHZ(140), 0x21 }, {  MHZ(150), 0x31 }, {  MHZ(160), 0x02 },
-       {  MHZ(170), 0x12 }, {  MHZ(180), 0x22 }, {  MHZ(190), 0x32 },
-       {  MHZ(205), 0x03 }, {  MHZ(220), 0x13 }, {  MHZ(235), 0x23 },
-       {  MHZ(250), 0x33 }, {  MHZ(275), 0x04 }, {  MHZ(300), 0x14 },
-       {  MHZ(325), 0x25 }, {  MHZ(350), 0x35 }, {  MHZ(400), 0x05 },
-       {  MHZ(450), 0x16 }, {  MHZ(500), 0x26 }, {  MHZ(550), 0x37 },
-       {  MHZ(600), 0x07 }, {  MHZ(650), 0x18 }, {  MHZ(700), 0x28 },
-       {  MHZ(750), 0x39 }, {  MHZ(800), 0x09 }, {  MHZ(850), 0x19 },
-       {  MHZ(900), 0x29 }, {  MHZ(950), 0x3a }, { MHZ(1000), 0x0a },
-       { MHZ(1050), 0x1a }, { MHZ(1100), 0x2a }, { MHZ(1150), 0x3b },
-       { MHZ(1200), 0x0b }, { MHZ(1250), 0x1b }, { MHZ(1300), 0x2b },
-       { MHZ(1350), 0x3c }, { MHZ(1400), 0x0c }, { MHZ(1450), 0x1c },
-       { MHZ(1500), 0x2c }, { MHZ(1550), 0x3d }, { MHZ(1600), 0x0d },
-       { MHZ(1650), 0x1d }, { MHZ(1700), 0x2e }, { MHZ(1750), 0x3e },
-       { MHZ(1800), 0x0e }, { MHZ(1850), 0x1e }, { MHZ(1900), 0x2f },
-       { MHZ(1950), 0x3f }, { MHZ(2000), 0x0f }, { MHZ(2050), 0x40 },
-       { MHZ(2100), 0x41 }, { MHZ(2150), 0x42 }, { MHZ(2200), 0x43 },
-       { MHZ(2250), 0x44 }, { MHZ(2300), 0x45 }, { MHZ(2350), 0x46 },
-       { MHZ(2400), 0x47 }, { MHZ(2450), 0x48 }, { MHZ(2500), 0x49 },
-       { /* sentinel */ },
-};
-
-struct dsi_clk_config {
-       u32 min_freq;
-       u32 max_freq;
-       u8 vco_cntrl;
-       u8 cpbias_cntrl;
-       u8 gmp_cntrl;
-       u8 int_cntrl;
-       u8 prop_cntrl;
-};
-
-static const struct dsi_clk_config dsi_clk_cfg_v3u[] = {
-       {   MHZ(40),    MHZ(55), 0x3f, 0x10, 0x01, 0x00, 0x0b },
-       {   MHZ(52.5),  MHZ(80), 0x39, 0x10, 0x01, 0x00, 0x0b },
-       {   MHZ(80),   MHZ(110), 0x2f, 0x10, 0x01, 0x00, 0x0b },
-       {  MHZ(105),   MHZ(160), 0x29, 0x10, 0x01, 0x00, 0x0b },
-       {  MHZ(160),   MHZ(220), 0x1f, 0x10, 0x01, 0x00, 0x0b },
-       {  MHZ(210),   MHZ(320), 0x19, 0x10, 0x01, 0x00, 0x0b },
-       {  MHZ(320),   MHZ(440), 0x0f, 0x10, 0x01, 0x00, 0x0b },
-       {  MHZ(420),   MHZ(660), 0x09, 0x10, 0x01, 0x00, 0x0b },
-       {  MHZ(630),  MHZ(1149), 0x03, 0x10, 0x01, 0x00, 0x0b },
-       { MHZ(1100),  MHZ(1152), 0x01, 0x10, 0x01, 0x00, 0x0b },
-       { MHZ(1150),  MHZ(1250), 0x01, 0x10, 0x01, 0x00, 0x0c },
-       { /* sentinel */ },
-};
-
-static const struct dsi_clk_config dsi_clk_cfg_v4h[] = {
-       {   MHZ(40),    MHZ(45.31),  0x2b, 0x00, 0x00, 0x08, 0x0a },
-       {   MHZ(45.31), MHZ(54.66),  0x28, 0x00, 0x00, 0x08, 0x0a },
-       {   MHZ(54.66), MHZ(62.5),   0x28, 0x00, 0x00, 0x08, 0x0a },
-       {   MHZ(62.5),  MHZ(75),     0x27, 0x00, 0x00, 0x08, 0x0a },
-       {   MHZ(75),    MHZ(90.63),  0x23, 0x00, 0x00, 0x08, 0x0a },
-       {   MHZ(90.63), MHZ(109.37), 0x20, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(109.37), MHZ(125),    0x20, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(125),    MHZ(150),    0x1f, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(150),    MHZ(181.25), 0x1b, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(181.25), MHZ(218.75), 0x18, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(218.75), MHZ(250),    0x18, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(250),    MHZ(300),    0x17, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(300),    MHZ(362.5),  0x13, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(362.5),  MHZ(455.48), 0x10, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(455.48), MHZ(500),    0x10, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(500),    MHZ(600),    0x0f, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(600),    MHZ(725),    0x0b, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(725),    MHZ(875),    0x08, 0x00, 0x00, 0x08, 0x0a },
-       {  MHZ(875),   MHZ(1000),    0x08, 0x00, 0x00, 0x08, 0x0a },
-       { MHZ(1000),   MHZ(1200),    0x07, 0x00, 0x00, 0x08, 0x0a },
-       { MHZ(1200),   MHZ(1250),    0x03, 0x00, 0x00, 0x08, 0x0a },
-       { /* sentinel */ },
-};
-
-static void rcar_mipi_dsi_write(struct rcar_mipi_dsi *dsi, u32 reg, u32 data)
-{
-       iowrite32(data, dsi->mmio + reg);
-}
-
-static u32 rcar_mipi_dsi_read(struct rcar_mipi_dsi *dsi, u32 reg)
-{
-       return ioread32(dsi->mmio + reg);
-}
-
-static void rcar_mipi_dsi_clr(struct rcar_mipi_dsi *dsi, u32 reg, u32 clr)
-{
-       rcar_mipi_dsi_write(dsi, reg, rcar_mipi_dsi_read(dsi, reg) & ~clr);
-}
-
-static void rcar_mipi_dsi_set(struct rcar_mipi_dsi *dsi, u32 reg, u32 set)
-{
-       rcar_mipi_dsi_write(dsi, reg, rcar_mipi_dsi_read(dsi, reg) | set);
-}
-
-static int rcar_mipi_dsi_write_phtw(struct rcar_mipi_dsi *dsi, u32 phtw)
-{
-       u32 status;
-       int ret;
-
-       rcar_mipi_dsi_write(dsi, PHTW, phtw);
-
-       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                               !(status & (PHTW_DWEN | PHTW_CWEN)),
-                               2000, 10000, false, dsi, PHTW);
-       if (ret < 0) {
-               dev_err(dsi->dev, "PHY test interface write timeout (0x%08x)\n",
-                       phtw);
-               return ret;
-       }
-
-       return ret;
-}
-
-static int rcar_mipi_dsi_write_phtw_arr(struct rcar_mipi_dsi *dsi,
-                                       const u32 *phtw, unsigned int size)
-{
-       for (unsigned int i = 0; i < size; i++) {
-               int ret = rcar_mipi_dsi_write_phtw(dsi, phtw[i]);
-
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
-#define WRITE_PHTW(...)                                               \
-       ({                                                            \
-               static const u32 phtw[] = { __VA_ARGS__ };            \
-               int ret;                                              \
-               ret = rcar_mipi_dsi_write_phtw_arr(dsi, phtw,         \
-                                                  ARRAY_SIZE(phtw)); \
-               ret;                                                  \
-       })
-
-static int rcar_mipi_dsi_init_phtw_v3u(struct rcar_mipi_dsi *dsi)
-{
-       return WRITE_PHTW(0x01020114, 0x01600115, 0x01030116, 0x0102011d,
-                         0x011101a4, 0x018601a4, 0x014201a0, 0x010001a3,
-                         0x0101011f);
-}
-
-static int rcar_mipi_dsi_post_init_phtw_v3u(struct rcar_mipi_dsi *dsi)
-{
-       return WRITE_PHTW(0x010c0130, 0x010c0140, 0x010c0150, 0x010c0180,
-                         0x010c0190, 0x010a0160, 0x010a0170, 0x01800164,
-                         0x01800174);
-}
-
-static int rcar_mipi_dsi_init_phtw_v4h(struct rcar_mipi_dsi *dsi,
-                                      const struct dsi_setup_info *setup_info)
-{
-       int ret;
-
-       if (setup_info->hsfreq < MHZ(450)) {
-               ret = WRITE_PHTW(0x01010100, 0x011b01ac);
-               if (ret)
-                       return ret;
-       }
-
-       ret = WRITE_PHTW(0x01010100, 0x01030173, 0x01000174, 0x01500175,
-                        0x01030176, 0x01040166, 0x010201ad);
-       if (ret)
-               return ret;
-
-       if (setup_info->hsfreq <= MHZ(1000))
-               ret = WRITE_PHTW(0x01020100, 0x01910170, 0x01020171,
-                                0x01110172);
-       else if (setup_info->hsfreq <= MHZ(1500))
-               ret = WRITE_PHTW(0x01020100, 0x01980170, 0x01030171,
-                                0x01100172);
-       else if (setup_info->hsfreq <= MHZ(2500))
-               ret = WRITE_PHTW(0x01020100, 0x0144016b, 0x01000172);
-       else
-               return -EINVAL;
-
-       if (ret)
-               return ret;
-
-       if (dsi->lanes <= 1) {
-               ret = WRITE_PHTW(0x01070100, 0x010e010b);
-               if (ret)
-                       return ret;
-       }
-
-       if (dsi->lanes <= 2) {
-               ret = WRITE_PHTW(0x01090100, 0x010e010b);
-               if (ret)
-                       return ret;
-       }
-
-       if (dsi->lanes <= 3) {
-               ret = WRITE_PHTW(0x010b0100, 0x010e010b);
-               if (ret)
-                       return ret;
-       }
-
-       if (setup_info->hsfreq <= MHZ(1500)) {
-               ret = WRITE_PHTW(0x01010100, 0x01c0016e);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static int
-rcar_mipi_dsi_post_init_phtw_v4h(struct rcar_mipi_dsi *dsi,
-                                const struct dsi_setup_info *setup_info)
-{
-       u32 status;
-       int ret;
-
-       if (setup_info->hsfreq <= MHZ(1500)) {
-               WRITE_PHTW(0x01020100, 0x00000180);
-
-               ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                                       status & PHTR_TEST, 2000, 10000, false,
-                                       dsi, PHTR);
-               if (ret < 0) {
-                       dev_err(dsi->dev, "failed to test PHTR\n");
-                       return ret;
-               }
-
-               WRITE_PHTW(0x01010100, 0x0100016e);
-       }
-
-       return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Hardware Setup
- */
-
-static void rcar_mipi_dsi_pll_calc(struct rcar_mipi_dsi *dsi,
-                                  unsigned long fin_rate,
-                                  unsigned long fout_target,
-                                  struct dsi_setup_info *setup_info)
-{
-       unsigned int best_err = -1;
-       const struct rcar_mipi_dsi_device_info *info = dsi->info;
-
-       for (unsigned int n = info->n_min; n <= info->n_max; n++) {
-               unsigned long fpfd;
-
-               fpfd = fin_rate / n;
-
-               if (fpfd < info->fpfd_min || fpfd > info->fpfd_max)
-                       continue;
-
-               for (unsigned int m = info->m_min; m <= info->m_max; m++) {
-                       unsigned int err;
-                       u64 fout;
-
-                       fout = div64_u64((u64)fpfd * m, dsi->info->n_mul);
-
-                       if (fout < info->fout_min || fout > info->fout_max)
-                               continue;
-
-                       fout = div64_u64(fout, setup_info->vclk_divider);
-
-                       if (fout < setup_info->clkset->min_freq ||
-                           fout > setup_info->clkset->max_freq)
-                               continue;
-
-                       err = abs((long)(fout - fout_target) * 10000 /
-                                 (long)fout_target);
-                       if (err < best_err) {
-                               setup_info->m = m;
-                               setup_info->n = n;
-                               setup_info->fout = (unsigned long)fout;
-                               best_err = err;
-
-                               if (err == 0)
-                                       return;
-                       }
-               }
-       }
-}
-
-static void rcar_mipi_dsi_parameters_calc(struct rcar_mipi_dsi *dsi,
-                                         struct clk *clk, unsigned long target,
-                                         struct dsi_setup_info *setup_info)
-{
-
-       const struct dsi_clk_config *clk_cfg;
-       unsigned long fout_target;
-       unsigned long fin_rate;
-       unsigned int i;
-       unsigned int err;
-
-       /*
-        * Calculate Fout = dot clock * ColorDepth / (2 * Lane Count)
-        * The range out Fout is [40 - 1250] Mhz
-        */
-       fout_target = target * mipi_dsi_pixel_format_to_bpp(dsi->format)
-                   / (2 * dsi->lanes);
-       if (fout_target < MHZ(40) || fout_target > MHZ(1250))
-               return;
-
-       /* Find PLL settings */
-       for (clk_cfg = dsi->info->clk_cfg; clk_cfg->min_freq != 0; clk_cfg++) {
-               if (fout_target > clk_cfg->min_freq &&
-                   fout_target <= clk_cfg->max_freq) {
-                       setup_info->clkset = clk_cfg;
-                       break;
-               }
-       }
-
-       fin_rate = clk_get_rate(clk);
-
-       switch (dsi->info->model) {
-       case RCAR_DSI_V3U:
-       default:
-               setup_info->vclk_divider = 1 << ((clk_cfg->vco_cntrl >> 4) & 0x3);
-               break;
-
-       case RCAR_DSI_V4H:
-               setup_info->vclk_divider = 1 << (((clk_cfg->vco_cntrl >> 3) & 0x7) + 1);
-               break;
-       }
-
-       rcar_mipi_dsi_pll_calc(dsi, fin_rate, fout_target, setup_info);
-
-       /* Find hsfreqrange */
-       setup_info->hsfreq = setup_info->fout * 2;
-       for (i = 0; i < ARRAY_SIZE(hsfreqrange_table); i++) {
-               if (hsfreqrange_table[i][0] >= setup_info->hsfreq) {
-                       setup_info->hsfreqrange = hsfreqrange_table[i][1];
-                       break;
-               }
-       }
-
-       err = abs((long)(setup_info->fout - fout_target) * 10000 / (long)fout_target);
-
-       dev_dbg(dsi->dev,
-               "Fout = %u * %lu / (%u * %u * %u) = %lu (target %lu Hz, error %d.%02u%%)\n",
-               setup_info->m, fin_rate, dsi->info->n_mul, setup_info->n,
-               setup_info->vclk_divider, setup_info->fout, fout_target,
-               err / 100, err % 100);
-
-       dev_dbg(dsi->dev,
-               "vco_cntrl = 0x%x\tprop_cntrl = 0x%x\thsfreqrange = 0x%x\n",
-               clk_cfg->vco_cntrl, clk_cfg->prop_cntrl,
-               setup_info->hsfreqrange);
-}
-
-static void rcar_mipi_dsi_set_display_timing(struct rcar_mipi_dsi *dsi,
-                                            const struct drm_display_mode *mode)
-{
-       u32 setr;
-       u32 vprmset0r;
-       u32 vprmset1r;
-       u32 vprmset2r;
-       u32 vprmset3r;
-       u32 vprmset4r;
-
-       /* Configuration for Pixel Stream and Packet Header */
-       if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 24)
-               rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB24);
-       else if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 18)
-               rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB18);
-       else if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 16)
-               rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB16);
-       else {
-               dev_warn(dsi->dev, "unsupported format");
-               return;
-       }
-
-       /* Configuration for Blanking sequence and Input Pixel */
-       setr = TXVMSETR_HSABPEN_EN | TXVMSETR_HBPBPEN_EN
-            | TXVMSETR_HFPBPEN_EN | TXVMSETR_SYNSEQ_PULSES
-            | TXVMSETR_PIXWDTH | TXVMSETR_VSTPM;
-       rcar_mipi_dsi_write(dsi, TXVMSETR, setr);
-
-       /* Configuration for Video Parameters */
-       vprmset0r = (mode->flags & DRM_MODE_FLAG_PVSYNC ?
-                    TXVMVPRMSET0R_VSPOL_HIG : TXVMVPRMSET0R_VSPOL_LOW)
-                 | (mode->flags & DRM_MODE_FLAG_PHSYNC ?
-                    TXVMVPRMSET0R_HSPOL_HIG : TXVMVPRMSET0R_HSPOL_LOW)
-                 | TXVMVPRMSET0R_CSPC_RGB | TXVMVPRMSET0R_BPP_24;
-
-       vprmset1r = TXVMVPRMSET1R_VACTIVE(mode->vdisplay)
-                 | TXVMVPRMSET1R_VSA(mode->vsync_end - mode->vsync_start);
-
-       vprmset2r = TXVMVPRMSET2R_VFP(mode->vsync_start - mode->vdisplay)
-                 | TXVMVPRMSET2R_VBP(mode->vtotal - mode->vsync_end);
-
-       vprmset3r = TXVMVPRMSET3R_HACTIVE(mode->hdisplay)
-                 | TXVMVPRMSET3R_HSA(mode->hsync_end - mode->hsync_start);
-
-       vprmset4r = TXVMVPRMSET4R_HFP(mode->hsync_start - mode->hdisplay)
-                 | TXVMVPRMSET4R_HBP(mode->htotal - mode->hsync_end);
-
-       rcar_mipi_dsi_write(dsi, TXVMVPRMSET0R, vprmset0r);
-       rcar_mipi_dsi_write(dsi, TXVMVPRMSET1R, vprmset1r);
-       rcar_mipi_dsi_write(dsi, TXVMVPRMSET2R, vprmset2r);
-       rcar_mipi_dsi_write(dsi, TXVMVPRMSET3R, vprmset3r);
-       rcar_mipi_dsi_write(dsi, TXVMVPRMSET4R, vprmset4r);
-}
-
-static int rcar_mipi_dsi_startup(struct rcar_mipi_dsi *dsi,
-                                const struct drm_display_mode *mode)
-{
-       struct dsi_setup_info setup_info = {};
-       unsigned int timeout;
-       int ret;
-       int dsi_format;
-       u32 phy_setup;
-       u32 clockset2, clockset3;
-       u32 ppisetr;
-       u32 vclkset;
-
-       /* Checking valid format */
-       dsi_format = mipi_dsi_pixel_format_to_bpp(dsi->format);
-       if (dsi_format < 0) {
-               dev_warn(dsi->dev, "invalid format");
-               return -EINVAL;
-       }
-
-       /* Parameters Calculation */
-       rcar_mipi_dsi_parameters_calc(dsi, dsi->clocks.pll,
-                                     mode->clock * 1000, &setup_info);
-
-       /* LPCLK enable */
-       rcar_mipi_dsi_set(dsi, LPCLKSET, LPCLKSET_CKEN);
-
-       /* CFGCLK enabled */
-       rcar_mipi_dsi_set(dsi, CFGCLKSET, CFGCLKSET_CKEN);
-
-       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_RSTZ);
-       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_SHUTDOWNZ);
-
-       rcar_mipi_dsi_set(dsi, PHTC, PHTC_TESTCLR);
-       rcar_mipi_dsi_clr(dsi, PHTC, PHTC_TESTCLR);
-
-       /* PHY setting */
-       phy_setup = rcar_mipi_dsi_read(dsi, PHYSETUP);
-       phy_setup &= ~PHYSETUP_HSFREQRANGE_MASK;
-       phy_setup |= PHYSETUP_HSFREQRANGE(setup_info.hsfreqrange);
-       rcar_mipi_dsi_write(dsi, PHYSETUP, phy_setup);
-
-       switch (dsi->info->model) {
-       case RCAR_DSI_V3U:
-       default:
-               ret = rcar_mipi_dsi_init_phtw_v3u(dsi);
-               if (ret < 0)
-                       return ret;
-               break;
-
-       case RCAR_DSI_V4H:
-               ret = rcar_mipi_dsi_init_phtw_v4h(dsi, &setup_info);
-               if (ret < 0)
-                       return ret;
-               break;
-       }
-
-       /* PLL Clock Setting */
-       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_SHADOW_CLEAR);
-       rcar_mipi_dsi_set(dsi, CLOCKSET1, CLOCKSET1_SHADOW_CLEAR);
-       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_SHADOW_CLEAR);
-
-       clockset2 = CLOCKSET2_M(setup_info.m - dsi->info->clockset2_m_offset)
-                 | CLOCKSET2_N(setup_info.n - 1)
-                 | CLOCKSET2_VCO_CNTRL(setup_info.clkset->vco_cntrl);
-       clockset3 = CLOCKSET3_PROP_CNTRL(setup_info.clkset->prop_cntrl)
-                 | CLOCKSET3_INT_CNTRL(setup_info.clkset->int_cntrl)
-                 | CLOCKSET3_CPBIAS_CNTRL(setup_info.clkset->cpbias_cntrl)
-                 | CLOCKSET3_GMP_CNTRL(setup_info.clkset->gmp_cntrl);
-       rcar_mipi_dsi_write(dsi, CLOCKSET2, clockset2);
-       rcar_mipi_dsi_write(dsi, CLOCKSET3, clockset3);
-
-       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_UPDATEPLL);
-       rcar_mipi_dsi_set(dsi, CLOCKSET1, CLOCKSET1_UPDATEPLL);
-       udelay(10);
-       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_UPDATEPLL);
-
-       ppisetr = PPISETR_DLEN_3 | PPISETR_CLEN;
-       rcar_mipi_dsi_write(dsi, PPISETR, ppisetr);
-
-       rcar_mipi_dsi_set(dsi, PHYSETUP, PHYSETUP_SHUTDOWNZ);
-       rcar_mipi_dsi_set(dsi, PHYSETUP, PHYSETUP_RSTZ);
-       usleep_range(400, 500);
-
-       /* Checking PPI clock status register */
-       for (timeout = 10; timeout > 0; --timeout) {
-               if ((rcar_mipi_dsi_read(dsi, PPICLSR) & PPICLSR_STPST) &&
-                   (rcar_mipi_dsi_read(dsi, PPIDLSR) & PPIDLSR_STPST) &&
-                   (rcar_mipi_dsi_read(dsi, CLOCKSET1) & CLOCKSET1_LOCK))
-                       break;
-
-               usleep_range(1000, 2000);
-       }
-
-       if (!timeout) {
-               dev_err(dsi->dev, "failed to enable PPI clock\n");
-               return -ETIMEDOUT;
-       }
-
-       switch (dsi->info->model) {
-       case RCAR_DSI_V3U:
-       default:
-               ret = rcar_mipi_dsi_post_init_phtw_v3u(dsi);
-               if (ret < 0)
-                       return ret;
-               break;
-
-       case RCAR_DSI_V4H:
-               ret = rcar_mipi_dsi_post_init_phtw_v4h(dsi, &setup_info);
-               if (ret < 0)
-                       return ret;
-               break;
-       }
-
-       /* Enable DOT clock */
-       vclkset = VCLKSET_CKEN;
-       rcar_mipi_dsi_write(dsi, VCLKSET, vclkset);
-
-       if (dsi_format == 24)
-               vclkset |= VCLKSET_BPP_24;
-       else if (dsi_format == 18)
-               vclkset |= VCLKSET_BPP_18;
-       else if (dsi_format == 16)
-               vclkset |= VCLKSET_BPP_16;
-       else {
-               dev_warn(dsi->dev, "unsupported format");
-               return -EINVAL;
-       }
-
-       vclkset |= VCLKSET_COLOR_RGB | VCLKSET_LANE(dsi->lanes - 1);
-
-       switch (dsi->info->model) {
-       case RCAR_DSI_V3U:
-       default:
-               vclkset |= VCLKSET_DIV_V3U(__ffs(setup_info.vclk_divider));
-               break;
-
-       case RCAR_DSI_V4H:
-               vclkset |= VCLKSET_DIV_V4H(__ffs(setup_info.vclk_divider) - 1);
-               break;
-       }
-
-       rcar_mipi_dsi_write(dsi, VCLKSET, vclkset);
-
-       /* After setting VCLKSET register, enable VCLKEN */
-       rcar_mipi_dsi_set(dsi, VCLKEN, VCLKEN_CKEN);
-
-       dev_dbg(dsi->dev, "DSI device is started\n");
-
-       return 0;
-}
-
-static void rcar_mipi_dsi_shutdown(struct rcar_mipi_dsi *dsi)
-{
-       /* Disable VCLKEN */
-       rcar_mipi_dsi_write(dsi, VCLKSET, 0);
-
-       /* Disable DOT clock */
-       rcar_mipi_dsi_write(dsi, VCLKSET, 0);
-
-       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_RSTZ);
-       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_SHUTDOWNZ);
-
-       /* CFGCLK disable */
-       rcar_mipi_dsi_clr(dsi, CFGCLKSET, CFGCLKSET_CKEN);
-
-       /* LPCLK disable */
-       rcar_mipi_dsi_clr(dsi, LPCLKSET, LPCLKSET_CKEN);
-
-       dev_dbg(dsi->dev, "DSI device is shutdown\n");
-}
-
-static int rcar_mipi_dsi_clk_enable(struct rcar_mipi_dsi *dsi)
-{
-       int ret;
-
-       reset_control_deassert(dsi->rstc);
-
-       ret = clk_prepare_enable(dsi->clocks.mod);
-       if (ret < 0)
-               goto err_reset;
-
-       ret = clk_prepare_enable(dsi->clocks.dsi);
-       if (ret < 0)
-               goto err_clock;
-
-       return 0;
-
-err_clock:
-       clk_disable_unprepare(dsi->clocks.mod);
-err_reset:
-       reset_control_assert(dsi->rstc);
-       return ret;
-}
-
-static void rcar_mipi_dsi_clk_disable(struct rcar_mipi_dsi *dsi)
-{
-       clk_disable_unprepare(dsi->clocks.dsi);
-       clk_disable_unprepare(dsi->clocks.mod);
-
-       reset_control_assert(dsi->rstc);
-}
-
-static int rcar_mipi_dsi_start_hs_clock(struct rcar_mipi_dsi *dsi)
-{
-       /*
-        * In HW manual, we need to check TxDDRClkHS-Q Stable? but it dont
-        * write how to check. So we skip this check in this patch
-        */
-       u32 status;
-       int ret;
-
-       /* Start HS clock. */
-       rcar_mipi_dsi_set(dsi, PPICLCR, PPICLCR_TXREQHS);
-
-       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                               status & PPICLSR_TOHS,
-                               2000, 10000, false, dsi, PPICLSR);
-       if (ret < 0) {
-               dev_err(dsi->dev, "failed to enable HS clock\n");
-               return ret;
-       }
-
-       rcar_mipi_dsi_set(dsi, PPICLSCR, PPICLSCR_TOHS);
-
-       return 0;
-}
-
-static int rcar_mipi_dsi_start_video(struct rcar_mipi_dsi *dsi)
-{
-       u32 status;
-       int ret;
-
-       /* Wait for the link to be ready. */
-       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                               !(status & (LINKSR_LPBUSY | LINKSR_HSBUSY)),
-                               2000, 10000, false, dsi, LINKSR);
-       if (ret < 0) {
-               dev_err(dsi->dev, "Link failed to become ready\n");
-               return ret;
-       }
-
-       /* De-assert video FIFO clear. */
-       rcar_mipi_dsi_clr(dsi, TXVMCR, TXVMCR_VFCLR);
-
-       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                               status & TXVMSR_VFRDY,
-                               2000, 10000, false, dsi, TXVMSR);
-       if (ret < 0) {
-               dev_err(dsi->dev, "Failed to de-assert video FIFO clear\n");
-               return ret;
-       }
-
-       /* Enable transmission in video mode. */
-       rcar_mipi_dsi_set(dsi, TXVMCR, TXVMCR_EN_VIDEO);
-
-       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                               status & TXVMSR_RDY,
-                               2000, 10000, false, dsi, TXVMSR);
-       if (ret < 0) {
-               dev_err(dsi->dev, "Failed to enable video transmission\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static void rcar_mipi_dsi_stop_video(struct rcar_mipi_dsi *dsi)
-{
-       u32 status;
-       int ret;
-
-       /* Disable transmission in video mode. */
-       rcar_mipi_dsi_clr(dsi, TXVMCR, TXVMCR_EN_VIDEO);
-
-       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                               !(status & TXVMSR_ACT),
-                               2000, 100000, false, dsi, TXVMSR);
-       if (ret < 0) {
-               dev_err(dsi->dev, "Failed to disable video transmission\n");
-               return;
-       }
-
-       /* Assert video FIFO clear. */
-       rcar_mipi_dsi_set(dsi, TXVMCR, TXVMCR_VFCLR);
-
-       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
-                               !(status & TXVMSR_VFRDY),
-                               2000, 100000, false, dsi, TXVMSR);
-       if (ret < 0) {
-               dev_err(dsi->dev, "Failed to assert video FIFO clear\n");
-               return;
-       }
-}
-
-/* -----------------------------------------------------------------------------
- * Bridge
- */
-
-static int rcar_mipi_dsi_attach(struct drm_bridge *bridge,
-                               enum drm_bridge_attach_flags flags)
-{
-       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
-
-       return drm_bridge_attach(bridge->encoder, dsi->next_bridge, bridge,
-                                flags);
-}
-
-static void rcar_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
-                                       struct drm_bridge_state *old_bridge_state)
-{
-       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
-
-       rcar_mipi_dsi_start_video(dsi);
-}
-
-static void rcar_mipi_dsi_atomic_disable(struct drm_bridge *bridge,
-                                        struct drm_bridge_state *old_bridge_state)
-{
-       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
-
-       rcar_mipi_dsi_stop_video(dsi);
-}
-
-void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
-                              struct drm_atomic_state *state)
-{
-       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
-       const struct drm_display_mode *mode;
-       struct drm_connector *connector;
-       struct drm_crtc *crtc;
-       int ret;
-
-       connector = drm_atomic_get_new_connector_for_encoder(state,
-                                                            bridge->encoder);
-       crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
-       mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;
-
-       ret = rcar_mipi_dsi_clk_enable(dsi);
-       if (ret < 0) {
-               dev_err(dsi->dev, "failed to enable DSI clocks\n");
-               return;
-       }
-
-       ret = rcar_mipi_dsi_startup(dsi, mode);
-       if (ret < 0)
-               goto err_dsi_startup;
-
-       rcar_mipi_dsi_set_display_timing(dsi, mode);
-
-       ret = rcar_mipi_dsi_start_hs_clock(dsi);
-       if (ret < 0)
-               goto err_dsi_start_hs;
-
-       return;
-
-err_dsi_start_hs:
-       rcar_mipi_dsi_shutdown(dsi);
-err_dsi_startup:
-       rcar_mipi_dsi_clk_disable(dsi);
-}
-EXPORT_SYMBOL_GPL(rcar_mipi_dsi_pclk_enable);
-
-void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge)
-{
-       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
-
-       rcar_mipi_dsi_shutdown(dsi);
-       rcar_mipi_dsi_clk_disable(dsi);
-}
-EXPORT_SYMBOL_GPL(rcar_mipi_dsi_pclk_disable);
-
-static enum drm_mode_status
-rcar_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
-                               const struct drm_display_info *info,
-                               const struct drm_display_mode *mode)
-{
-       if (mode->clock > 297000)
-               return MODE_CLOCK_HIGH;
-
-       return MODE_OK;
-}
-
-static const struct drm_bridge_funcs rcar_mipi_dsi_bridge_ops = {
-       .attach = rcar_mipi_dsi_attach,
-       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
-       .atomic_reset = drm_atomic_helper_bridge_reset,
-       .atomic_enable = rcar_mipi_dsi_atomic_enable,
-       .atomic_disable = rcar_mipi_dsi_atomic_disable,
-       .mode_valid = rcar_mipi_dsi_bridge_mode_valid,
-};
-
-/* -----------------------------------------------------------------------------
- * Host setting
- */
-
-static int rcar_mipi_dsi_host_attach(struct mipi_dsi_host *host,
-                                    struct mipi_dsi_device *device)
-{
-       struct rcar_mipi_dsi *dsi = host_to_rcar_mipi_dsi(host);
-       int ret;
-
-       if (device->lanes > dsi->num_data_lanes)
-               return -EINVAL;
-
-       dsi->lanes = device->lanes;
-       dsi->format = device->format;
-
-       dsi->next_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node,
-                                                 1, 0);
-       if (IS_ERR(dsi->next_bridge)) {
-               ret = PTR_ERR(dsi->next_bridge);
-               dev_err(dsi->dev, "failed to get next bridge: %d\n", ret);
-               return ret;
-       }
-
-       /* Initialize the DRM bridge. */
-       dsi->bridge.funcs = &rcar_mipi_dsi_bridge_ops;
-       dsi->bridge.of_node = dsi->dev->of_node;
-       drm_bridge_add(&dsi->bridge);
-
-       return 0;
-}
-
-static int rcar_mipi_dsi_host_detach(struct mipi_dsi_host *host,
-                                       struct mipi_dsi_device *device)
-{
-       struct rcar_mipi_dsi *dsi = host_to_rcar_mipi_dsi(host);
-
-       drm_bridge_remove(&dsi->bridge);
-
-       return 0;
-}
-
-static const struct mipi_dsi_host_ops rcar_mipi_dsi_host_ops = {
-       .attach = rcar_mipi_dsi_host_attach,
-       .detach = rcar_mipi_dsi_host_detach,
-};
-
-/* -----------------------------------------------------------------------------
- * Probe & Remove
- */
-
-static int rcar_mipi_dsi_parse_dt(struct rcar_mipi_dsi *dsi)
-{
-       int ret;
-
-       ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4);
-       if (ret < 0) {
-               dev_err(dsi->dev, "missing or invalid data-lanes property\n");
-               return ret;
-       }
-
-       dsi->num_data_lanes = ret;
-       return 0;
-}
-
-static struct clk *rcar_mipi_dsi_get_clock(struct rcar_mipi_dsi *dsi,
-                                          const char *name,
-                                          bool optional)
-{
-       struct clk *clk;
-
-       clk = devm_clk_get(dsi->dev, name);
-       if (!IS_ERR(clk))
-               return clk;
-
-       if (PTR_ERR(clk) == -ENOENT && optional)
-               return NULL;
-
-       dev_err_probe(dsi->dev, PTR_ERR(clk), "failed to get %s clock\n",
-                     name ? name : "module");
-
-       return clk;
-}
-
-static int rcar_mipi_dsi_get_clocks(struct rcar_mipi_dsi *dsi)
-{
-       dsi->clocks.mod = rcar_mipi_dsi_get_clock(dsi, NULL, false);
-       if (IS_ERR(dsi->clocks.mod))
-               return PTR_ERR(dsi->clocks.mod);
-
-       dsi->clocks.pll = rcar_mipi_dsi_get_clock(dsi, "pll", true);
-       if (IS_ERR(dsi->clocks.pll))
-               return PTR_ERR(dsi->clocks.pll);
-
-       dsi->clocks.dsi = rcar_mipi_dsi_get_clock(dsi, "dsi", true);
-       if (IS_ERR(dsi->clocks.dsi))
-               return PTR_ERR(dsi->clocks.dsi);
-
-       if (!dsi->clocks.pll && !dsi->clocks.dsi) {
-               dev_err(dsi->dev, "no input clock (pll, dsi)\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int rcar_mipi_dsi_probe(struct platform_device *pdev)
-{
-       struct rcar_mipi_dsi *dsi;
-       struct resource *mem;
-       int ret;
-
-       dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
-       if (dsi == NULL)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, dsi);
-
-       dsi->dev = &pdev->dev;
-       dsi->info = of_device_get_match_data(&pdev->dev);
-
-       ret = rcar_mipi_dsi_parse_dt(dsi);
-       if (ret < 0)
-               return ret;
-
-       /* Acquire resources. */
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       dsi->mmio = devm_ioremap_resource(dsi->dev, mem);
-       if (IS_ERR(dsi->mmio))
-               return PTR_ERR(dsi->mmio);
-
-       ret = rcar_mipi_dsi_get_clocks(dsi);
-       if (ret < 0)
-               return ret;
-
-       dsi->rstc = devm_reset_control_get(dsi->dev, NULL);
-       if (IS_ERR(dsi->rstc)) {
-               dev_err(dsi->dev, "failed to get cpg reset\n");
-               return PTR_ERR(dsi->rstc);
-       }
-
-       /* Initialize the DSI host. */
-       dsi->host.dev = dsi->dev;
-       dsi->host.ops = &rcar_mipi_dsi_host_ops;
-       ret = mipi_dsi_host_register(&dsi->host);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int rcar_mipi_dsi_remove(struct platform_device *pdev)
-{
-       struct rcar_mipi_dsi *dsi = platform_get_drvdata(pdev);
-
-       mipi_dsi_host_unregister(&dsi->host);
-
-       return 0;
-}
-
-static const struct rcar_mipi_dsi_device_info v3u_data = {
-       .model = RCAR_DSI_V3U,
-       .clk_cfg = dsi_clk_cfg_v3u,
-       .clockset2_m_offset = 2,
-       .n_min = 3,
-       .n_max = 8,
-       .n_mul = 1,
-       .fpfd_min = MHZ(2),
-       .fpfd_max = MHZ(8),
-       .m_min = 64,
-       .m_max = 625,
-       .fout_min = MHZ(320),
-       .fout_max = MHZ(1250),
-};
-
-static const struct rcar_mipi_dsi_device_info v4h_data = {
-       .model = RCAR_DSI_V4H,
-       .clk_cfg = dsi_clk_cfg_v4h,
-       .clockset2_m_offset = 0,
-       .n_min = 1,
-       .n_max = 8,
-       .n_mul = 2,
-       .fpfd_min = MHZ(8),
-       .fpfd_max = MHZ(24),
-       .m_min = 167,
-       .m_max = 1000,
-       .fout_min = MHZ(2000),
-       .fout_max = MHZ(4000),
-};
-
-static const struct of_device_id rcar_mipi_dsi_of_table[] = {
-       { .compatible = "renesas,r8a779a0-dsi-csi2-tx", .data = &v3u_data },
-       { .compatible = "renesas,r8a779g0-dsi-csi2-tx", .data = &v4h_data },
-       { }
-};
-
-MODULE_DEVICE_TABLE(of, rcar_mipi_dsi_of_table);
-
-static struct platform_driver rcar_mipi_dsi_platform_driver = {
-       .probe          = rcar_mipi_dsi_probe,
-       .remove         = rcar_mipi_dsi_remove,
-       .driver         = {
-               .name   = "rcar-mipi-dsi",
-               .of_match_table = rcar_mipi_dsi_of_table,
-       },
-};
-
-module_platform_driver(rcar_mipi_dsi_platform_driver);
-
-MODULE_DESCRIPTION("Renesas R-Car MIPI DSI Encoder Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h
deleted file mode 100644 (file)
index 528a196..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * R-Car DSI Encoder
- *
- * Copyright (C) 2022 Renesas Electronics Corporation
- *
- * Contact: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
- */
-
-#ifndef __RCAR_MIPI_DSI_H__
-#define __RCAR_MIPI_DSI_H__
-
-struct drm_atomic_state;
-struct drm_bridge;
-
-#if IS_ENABLED(CONFIG_DRM_RCAR_MIPI_DSI)
-void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
-                              struct drm_atomic_state *state);
-void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge);
-#else
-static inline void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
-                                            struct drm_atomic_state *state)
-{
-}
-
-static inline void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge)
-{
-}
-#endif /* CONFIG_DRM_RCAR_MIPI_DSI */
-
-#endif /* __RCAR_MIPI_DSI_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi_regs.h b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi_regs.h
deleted file mode 100644 (file)
index f8114d1..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * R-Car MIPI DSI Interface Registers Definitions
- *
- * Copyright (C) 2020 Renesas Electronics Corporation
- */
-
-#ifndef __RCAR_MIPI_DSI_REGS_H__
-#define __RCAR_MIPI_DSI_REGS_H__
-
-#define LINKSR                         0x010
-#define LINKSR_LPBUSY                  (1 << 1)
-#define LINKSR_HSBUSY                  (1 << 0)
-
-/*
- * Video Mode Register
- */
-#define TXVMSETR                       0x180
-#define TXVMSETR_SYNSEQ_PULSES         (0 << 16)
-#define TXVMSETR_SYNSEQ_EVENTS         (1 << 16)
-#define TXVMSETR_VSTPM                 (1 << 15)
-#define TXVMSETR_PIXWDTH               (1 << 8)
-#define TXVMSETR_VSEN_EN               (1 << 4)
-#define TXVMSETR_VSEN_DIS              (0 << 4)
-#define TXVMSETR_HFPBPEN_EN            (1 << 2)
-#define TXVMSETR_HFPBPEN_DIS           (0 << 2)
-#define TXVMSETR_HBPBPEN_EN            (1 << 1)
-#define TXVMSETR_HBPBPEN_DIS           (0 << 1)
-#define TXVMSETR_HSABPEN_EN            (1 << 0)
-#define TXVMSETR_HSABPEN_DIS           (0 << 0)
-
-#define TXVMCR                         0x190
-#define TXVMCR_VFCLR                   (1 << 12)
-#define TXVMCR_EN_VIDEO                        (1 << 0)
-
-#define TXVMSR                         0x1a0
-#define TXVMSR_STR                     (1 << 16)
-#define TXVMSR_VFRDY                   (1 << 12)
-#define TXVMSR_ACT                     (1 << 8)
-#define TXVMSR_RDY                     (1 << 0)
-
-#define TXVMSCR                                0x1a4
-#define TXVMSCR_STR                    (1 << 16)
-
-#define TXVMPSPHSETR                   0x1c0
-#define TXVMPSPHSETR_DT_RGB16          (0x0e << 16)
-#define TXVMPSPHSETR_DT_RGB18          (0x1e << 16)
-#define TXVMPSPHSETR_DT_RGB18_LS       (0x2e << 16)
-#define TXVMPSPHSETR_DT_RGB24          (0x3e << 16)
-#define TXVMPSPHSETR_DT_YCBCR16                (0x2c << 16)
-
-#define TXVMVPRMSET0R                  0x1d0
-#define TXVMVPRMSET0R_HSPOL_HIG                (0 << 17)
-#define TXVMVPRMSET0R_HSPOL_LOW                (1 << 17)
-#define TXVMVPRMSET0R_VSPOL_HIG                (0 << 16)
-#define TXVMVPRMSET0R_VSPOL_LOW                (1 << 16)
-#define TXVMVPRMSET0R_CSPC_RGB         (0 << 4)
-#define TXVMVPRMSET0R_CSPC_YCbCr       (1 << 4)
-#define TXVMVPRMSET0R_BPP_16           (0 << 0)
-#define TXVMVPRMSET0R_BPP_18           (1 << 0)
-#define TXVMVPRMSET0R_BPP_24           (2 << 0)
-
-#define TXVMVPRMSET1R                  0x1d4
-#define TXVMVPRMSET1R_VACTIVE(x)       (((x) & 0x7fff) << 16)
-#define TXVMVPRMSET1R_VSA(x)           (((x) & 0xfff) << 0)
-
-#define TXVMVPRMSET2R                  0x1d8
-#define TXVMVPRMSET2R_VFP(x)           (((x) & 0x1fff) << 16)
-#define TXVMVPRMSET2R_VBP(x)           (((x) & 0x1fff) << 0)
-
-#define TXVMVPRMSET3R                  0x1dc
-#define TXVMVPRMSET3R_HACTIVE(x)       (((x) & 0x7fff) << 16)
-#define TXVMVPRMSET3R_HSA(x)           (((x) & 0xfff) << 0)
-
-#define TXVMVPRMSET4R                  0x1e0
-#define TXVMVPRMSET4R_HFP(x)           (((x) & 0x1fff) << 16)
-#define TXVMVPRMSET4R_HBP(x)           (((x) & 0x1fff) << 0)
-
-/*
- * PHY-Protocol Interface (PPI) Registers
- */
-#define PPISETR                                0x700
-#define PPISETR_DLEN_0                 (0x1 << 0)
-#define PPISETR_DLEN_1                 (0x3 << 0)
-#define PPISETR_DLEN_2                 (0x7 << 0)
-#define PPISETR_DLEN_3                 (0xf << 0)
-#define PPISETR_CLEN                   (1 << 8)
-
-#define PPICLCR                                0x710
-#define PPICLCR_TXREQHS                        (1 << 8)
-#define PPICLCR_TXULPSEXT              (1 << 1)
-#define PPICLCR_TXULPSCLK              (1 << 0)
-
-#define PPICLSR                                0x720
-#define PPICLSR_HSTOLP                 (1 << 27)
-#define PPICLSR_TOHS                   (1 << 26)
-#define PPICLSR_STPST                  (1 << 0)
-
-#define PPICLSCR                       0x724
-#define PPICLSCR_HSTOLP                        (1 << 27)
-#define PPICLSCR_TOHS                  (1 << 26)
-
-#define PPIDLSR                                0x760
-#define PPIDLSR_STPST                  (0xf << 0)
-
-/*
- * Clocks registers
- */
-#define LPCLKSET                       0x1000
-#define LPCLKSET_CKEN                  (1 << 8)
-#define LPCLKSET_LPCLKDIV(x)           (((x) & 0x3f) << 0)
-
-#define CFGCLKSET                      0x1004
-#define CFGCLKSET_CKEN                 (1 << 8)
-#define CFGCLKSET_CFGCLKDIV(x)         (((x) & 0x3f) << 0)
-
-#define DOTCLKDIV                      0x1008
-#define DOTCLKDIV_CKEN                 (1 << 8)
-#define DOTCLKDIV_DOTCLKDIV(x)         (((x) & 0x3f) << 0)
-
-#define VCLKSET                                0x100c
-#define VCLKSET_CKEN                   (1 << 16)
-#define VCLKSET_COLOR_RGB              (0 << 8)
-#define VCLKSET_COLOR_YCC              (1 << 8)
-#define VCLKSET_DIV_V3U(x)             (((x) & 0x3) << 4)
-#define VCLKSET_DIV_V4H(x)             (((x) & 0x7) << 4)
-#define VCLKSET_BPP_16                 (0 << 2)
-#define VCLKSET_BPP_18                 (1 << 2)
-#define VCLKSET_BPP_18L                        (2 << 2)
-#define VCLKSET_BPP_24                 (3 << 2)
-#define VCLKSET_LANE(x)                        (((x) & 0x3) << 0)
-
-#define VCLKEN                         0x1010
-#define VCLKEN_CKEN                    (1 << 0)
-
-#define PHYSETUP                       0x1014
-#define PHYSETUP_HSFREQRANGE(x)                (((x) & 0x7f) << 16)
-#define PHYSETUP_HSFREQRANGE_MASK      (0x7f << 16)
-#define PHYSETUP_CFGCLKFREQRANGE(x)    (((x) & 0x3f) << 8)
-#define PHYSETUP_SHUTDOWNZ             (1 << 1)
-#define PHYSETUP_RSTZ                  (1 << 0)
-
-#define CLOCKSET1                      0x101c
-#define CLOCKSET1_LOCK_PHY             (1 << 17)
-#define CLOCKSET1_LOCK                 (1 << 16)
-#define CLOCKSET1_CLKSEL               (1 << 8)
-#define CLOCKSET1_CLKINSEL_EXTAL       (0 << 2)
-#define CLOCKSET1_CLKINSEL_DIG         (1 << 2)
-#define CLOCKSET1_CLKINSEL_DU          (1 << 3)
-#define CLOCKSET1_SHADOW_CLEAR         (1 << 1)
-#define CLOCKSET1_UPDATEPLL            (1 << 0)
-
-#define CLOCKSET2                      0x1020
-#define CLOCKSET2_M(x)                 (((x) & 0xfff) << 16)
-#define CLOCKSET2_VCO_CNTRL(x)         (((x) & 0x3f) << 8)
-#define CLOCKSET2_N(x)                 (((x) & 0xf) << 0)
-
-#define CLOCKSET3                      0x1024
-#define CLOCKSET3_PROP_CNTRL(x)                (((x) & 0x3f) << 24)
-#define CLOCKSET3_INT_CNTRL(x)         (((x) & 0x3f) << 16)
-#define CLOCKSET3_CPBIAS_CNTRL(x)      (((x) & 0x7f) << 8)
-#define CLOCKSET3_GMP_CNTRL(x)         (((x) & 0x3) << 0)
-
-#define PHTW                           0x1034
-#define PHTW_DWEN                      (1 << 24)
-#define PHTW_TESTDIN_DATA(x)           (((x) & 0xff) << 16)
-#define PHTW_CWEN                      (1 << 8)
-#define PHTW_TESTDIN_CODE(x)           (((x) & 0xff) << 0)
-
-#define PHTR                           0x1038
-#define PHTR_TEST                      (1 << 16)
-
-#define PHTC                           0x103c
-#define PHTC_TESTCLR                   (1 << 0)
-
-#endif /* __RCAR_MIPI_DSI_REGS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi.c
deleted file mode 100644 (file)
index aa95b85..0000000
+++ /dev/null
@@ -1,816 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * RZ/G2L MIPI DSI Encoder Driver
- *
- * Copyright (C) 2022 Renesas Electronics Corporation
- */
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/reset.h>
-#include <linux/slab.h>
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_of.h>
-#include <drm/drm_panel.h>
-#include <drm/drm_probe_helper.h>
-
-#include "rzg2l_mipi_dsi_regs.h"
-
-struct rzg2l_mipi_dsi {
-       struct device *dev;
-       void __iomem *mmio;
-
-       struct reset_control *rstc;
-       struct reset_control *arstc;
-       struct reset_control *prstc;
-
-       struct mipi_dsi_host host;
-       struct drm_bridge bridge;
-       struct drm_bridge *next_bridge;
-
-       struct clk *vclk;
-
-       enum mipi_dsi_pixel_format format;
-       unsigned int num_data_lanes;
-       unsigned int lanes;
-       unsigned long mode_flags;
-};
-
-static inline struct rzg2l_mipi_dsi *
-bridge_to_rzg2l_mipi_dsi(struct drm_bridge *bridge)
-{
-       return container_of(bridge, struct rzg2l_mipi_dsi, bridge);
-}
-
-static inline struct rzg2l_mipi_dsi *
-host_to_rzg2l_mipi_dsi(struct mipi_dsi_host *host)
-{
-       return container_of(host, struct rzg2l_mipi_dsi, host);
-}
-
-struct rzg2l_mipi_dsi_timings {
-       unsigned long hsfreq_max;
-       u32 t_init;
-       u32 tclk_prepare;
-       u32 ths_prepare;
-       u32 tclk_zero;
-       u32 tclk_pre;
-       u32 tclk_post;
-       u32 tclk_trail;
-       u32 ths_zero;
-       u32 ths_trail;
-       u32 ths_exit;
-       u32 tlpx;
-};
-
-static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = {
-       {
-               .hsfreq_max = 80000,
-               .t_init = 79801,
-               .tclk_prepare = 8,
-               .ths_prepare = 13,
-               .tclk_zero = 33,
-               .tclk_pre = 24,
-               .tclk_post = 94,
-               .tclk_trail = 10,
-               .ths_zero = 23,
-               .ths_trail = 17,
-               .ths_exit = 13,
-               .tlpx = 6,
-       },
-       {
-               .hsfreq_max = 125000,
-               .t_init = 79801,
-               .tclk_prepare = 8,
-               .ths_prepare = 12,
-               .tclk_zero = 33,
-               .tclk_pre = 15,
-               .tclk_post = 94,
-               .tclk_trail = 10,
-               .ths_zero = 23,
-               .ths_trail = 17,
-               .ths_exit = 13,
-               .tlpx = 6,
-       },
-       {
-               .hsfreq_max = 250000,
-               .t_init = 79801,
-               .tclk_prepare = 8,
-               .ths_prepare = 12,
-               .tclk_zero = 33,
-               .tclk_pre = 13,
-               .tclk_post = 94,
-               .tclk_trail = 10,
-               .ths_zero = 23,
-               .ths_trail = 16,
-               .ths_exit = 13,
-               .tlpx = 6,
-       },
-       {
-               .hsfreq_max = 360000,
-               .t_init = 79801,
-               .tclk_prepare = 8,
-               .ths_prepare = 10,
-               .tclk_zero = 33,
-               .tclk_pre = 4,
-               .tclk_post = 35,
-               .tclk_trail = 7,
-               .ths_zero = 16,
-               .ths_trail = 9,
-               .ths_exit = 13,
-               .tlpx = 6,
-       },
-       {
-               .hsfreq_max = 720000,
-               .t_init = 79801,
-               .tclk_prepare = 8,
-               .ths_prepare = 9,
-               .tclk_zero = 33,
-               .tclk_pre = 4,
-               .tclk_post = 35,
-               .tclk_trail = 7,
-               .ths_zero = 16,
-               .ths_trail = 9,
-               .ths_exit = 13,
-               .tlpx = 6,
-       },
-       {
-               .hsfreq_max = 1500000,
-               .t_init = 79801,
-               .tclk_prepare = 8,
-               .ths_prepare = 9,
-               .tclk_zero = 33,
-               .tclk_pre = 4,
-               .tclk_post = 35,
-               .tclk_trail = 7,
-               .ths_zero = 16,
-               .ths_trail = 9,
-               .ths_exit = 13,
-               .tlpx = 6,
-       },
-};
-
-static void rzg2l_mipi_dsi_phy_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data)
-{
-       iowrite32(data, dsi->mmio + reg);
-}
-
-static void rzg2l_mipi_dsi_link_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data)
-{
-       iowrite32(data, dsi->mmio + LINK_REG_OFFSET + reg);
-}
-
-static u32 rzg2l_mipi_dsi_phy_read(struct rzg2l_mipi_dsi *dsi, u32 reg)
-{
-       return ioread32(dsi->mmio + reg);
-}
-
-static u32 rzg2l_mipi_dsi_link_read(struct rzg2l_mipi_dsi *dsi, u32 reg)
-{
-       return ioread32(dsi->mmio + LINK_REG_OFFSET + reg);
-}
-
-/* -----------------------------------------------------------------------------
- * Hardware Setup
- */
-
-static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi,
-                                   unsigned long hsfreq)
-{
-       const struct rzg2l_mipi_dsi_timings *dphy_timings;
-       unsigned int i;
-       u32 dphyctrl0;
-       u32 dphytim0;
-       u32 dphytim1;
-       u32 dphytim2;
-       u32 dphytim3;
-       int ret;
-
-       /* All DSI global operation timings are set with recommended setting */
-       for (i = 0; i < ARRAY_SIZE(rzg2l_mipi_dsi_global_timings); ++i) {
-               dphy_timings = &rzg2l_mipi_dsi_global_timings[i];
-               if (hsfreq <= dphy_timings->hsfreq_max)
-                       break;
-       }
-
-       /* Initializing DPHY before accessing LINK */
-       dphyctrl0 = DSIDPHYCTRL0_CAL_EN_HSRX_OFS | DSIDPHYCTRL0_CMN_MASTER_EN |
-                   DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 | DSIDPHYCTRL0_EN_BGR;
-
-       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0);
-       usleep_range(20, 30);
-
-       dphyctrl0 |= DSIDPHYCTRL0_EN_LDO1200;
-       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0);
-       usleep_range(10, 20);
-
-       dphytim0 = DSIDPHYTIM0_TCLK_MISS(0) |
-                  DSIDPHYTIM0_T_INIT(dphy_timings->t_init);
-       dphytim1 = DSIDPHYTIM1_THS_PREPARE(dphy_timings->ths_prepare) |
-                  DSIDPHYTIM1_TCLK_PREPARE(dphy_timings->tclk_prepare) |
-                  DSIDPHYTIM1_THS_SETTLE(0) |
-                  DSIDPHYTIM1_TCLK_SETTLE(0);
-       dphytim2 = DSIDPHYTIM2_TCLK_TRAIL(dphy_timings->tclk_trail) |
-                  DSIDPHYTIM2_TCLK_POST(dphy_timings->tclk_post) |
-                  DSIDPHYTIM2_TCLK_PRE(dphy_timings->tclk_pre) |
-                  DSIDPHYTIM2_TCLK_ZERO(dphy_timings->tclk_zero);
-       dphytim3 = DSIDPHYTIM3_TLPX(dphy_timings->tlpx) |
-                  DSIDPHYTIM3_THS_EXIT(dphy_timings->ths_exit) |
-                  DSIDPHYTIM3_THS_TRAIL(dphy_timings->ths_trail) |
-                  DSIDPHYTIM3_THS_ZERO(dphy_timings->ths_zero);
-
-       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM0, dphytim0);
-       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM1, dphytim1);
-       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM2, dphytim2);
-       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM3, dphytim3);
-
-       ret = reset_control_deassert(dsi->rstc);
-       if (ret < 0)
-               return ret;
-
-       udelay(1);
-
-       return 0;
-}
-
-static void rzg2l_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi)
-{
-       u32 dphyctrl0;
-
-       dphyctrl0 = rzg2l_mipi_dsi_phy_read(dsi, DSIDPHYCTRL0);
-
-       dphyctrl0 &= ~(DSIDPHYCTRL0_EN_LDO1200 | DSIDPHYCTRL0_EN_BGR);
-       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0);
-
-       reset_control_assert(dsi->rstc);
-}
-
-static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi,
-                                 const struct drm_display_mode *mode)
-{
-       unsigned long hsfreq;
-       unsigned int bpp;
-       u32 txsetr;
-       u32 clstptsetr;
-       u32 lptrnstsetr;
-       u32 clkkpt;
-       u32 clkbfht;
-       u32 clkstpt;
-       u32 golpbkt;
-       int ret;
-
-       /*
-        * Relationship between hsclk and vclk must follow
-        * vclk * bpp = hsclk * 8 * lanes
-        * where vclk: video clock (Hz)
-        *       bpp: video pixel bit depth
-        *       hsclk: DSI HS Byte clock frequency (Hz)
-        *       lanes: number of data lanes
-        *
-        * hsclk(bit) = hsclk(byte) * 8
-        */
-       bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
-       hsfreq = (mode->clock * bpp * 8) / (8 * dsi->lanes);
-
-       ret = pm_runtime_resume_and_get(dsi->dev);
-       if (ret < 0)
-               return ret;
-
-       clk_set_rate(dsi->vclk, mode->clock * 1000);
-
-       ret = rzg2l_mipi_dsi_dphy_init(dsi, hsfreq);
-       if (ret < 0)
-               goto err_phy;
-
-       /* Enable Data lanes and Clock lanes */
-       txsetr = TXSETR_DLEN | TXSETR_NUMLANEUSE(dsi->lanes - 1) | TXSETR_CLEN;
-       rzg2l_mipi_dsi_link_write(dsi, TXSETR, txsetr);
-
-       /*
-        * Global timings characteristic depends on high speed Clock Frequency
-        * Currently MIPI DSI-IF just supports maximum FHD@60 with:
-        * - videoclock = 148.5 (MHz)
-        * - bpp: maximum 24bpp
-        * - data lanes: maximum 4 lanes
-        * Therefore maximum hsclk will be 891 Mbps.
-        */
-       if (hsfreq > 445500) {
-               clkkpt = 12;
-               clkbfht = 15;
-               clkstpt = 48;
-               golpbkt = 75;
-       } else if (hsfreq > 250000) {
-               clkkpt = 7;
-               clkbfht = 8;
-               clkstpt = 27;
-               golpbkt = 40;
-       } else {
-               clkkpt = 8;
-               clkbfht = 6;
-               clkstpt = 24;
-               golpbkt = 29;
-       }
-
-       clstptsetr = CLSTPTSETR_CLKKPT(clkkpt) | CLSTPTSETR_CLKBFHT(clkbfht) |
-                    CLSTPTSETR_CLKSTPT(clkstpt);
-       rzg2l_mipi_dsi_link_write(dsi, CLSTPTSETR, clstptsetr);
-
-       lptrnstsetr = LPTRNSTSETR_GOLPBKT(golpbkt);
-       rzg2l_mipi_dsi_link_write(dsi, LPTRNSTSETR, lptrnstsetr);
-
-       return 0;
-
-err_phy:
-       rzg2l_mipi_dsi_dphy_exit(dsi);
-       pm_runtime_put(dsi->dev);
-
-       return ret;
-}
-
-static void rzg2l_mipi_dsi_stop(struct rzg2l_mipi_dsi *dsi)
-{
-       rzg2l_mipi_dsi_dphy_exit(dsi);
-       pm_runtime_put(dsi->dev);
-}
-
-static void rzg2l_mipi_dsi_set_display_timing(struct rzg2l_mipi_dsi *dsi,
-                                             const struct drm_display_mode *mode)
-{
-       u32 vich1ppsetr;
-       u32 vich1vssetr;
-       u32 vich1vpsetr;
-       u32 vich1hssetr;
-       u32 vich1hpsetr;
-       int dsi_format;
-       u32 delay[2];
-       u8 index;
-
-       /* Configuration for Pixel Packet */
-       dsi_format = mipi_dsi_pixel_format_to_bpp(dsi->format);
-       switch (dsi_format) {
-       case 24:
-               vich1ppsetr = VICH1PPSETR_DT_RGB24;
-               break;
-       case 18:
-               vich1ppsetr = VICH1PPSETR_DT_RGB18;
-               break;
-       }
-
-       if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) &&
-           !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST))
-               vich1ppsetr |= VICH1PPSETR_TXESYNC_PULSE;
-
-       rzg2l_mipi_dsi_link_write(dsi, VICH1PPSETR, vich1ppsetr);
-
-       /* Configuration for Video Parameters */
-       vich1vssetr = VICH1VSSETR_VACTIVE(mode->vdisplay) |
-                     VICH1VSSETR_VSA(mode->vsync_end - mode->vsync_start);
-       vich1vssetr |= (mode->flags & DRM_MODE_FLAG_PVSYNC) ?
-                       VICH1VSSETR_VSPOL_HIGH : VICH1VSSETR_VSPOL_LOW;
-
-       vich1vpsetr = VICH1VPSETR_VFP(mode->vsync_start - mode->vdisplay) |
-                     VICH1VPSETR_VBP(mode->vtotal - mode->vsync_end);
-
-       vich1hssetr = VICH1HSSETR_HACTIVE(mode->hdisplay) |
-                     VICH1HSSETR_HSA(mode->hsync_end - mode->hsync_start);
-       vich1hssetr |= (mode->flags & DRM_MODE_FLAG_PHSYNC) ?
-                       VICH1HSSETR_HSPOL_HIGH : VICH1HSSETR_HSPOL_LOW;
-
-       vich1hpsetr = VICH1HPSETR_HFP(mode->hsync_start - mode->hdisplay) |
-                     VICH1HPSETR_HBP(mode->htotal - mode->hsync_end);
-
-       rzg2l_mipi_dsi_link_write(dsi, VICH1VSSETR, vich1vssetr);
-       rzg2l_mipi_dsi_link_write(dsi, VICH1VPSETR, vich1vpsetr);
-       rzg2l_mipi_dsi_link_write(dsi, VICH1HSSETR, vich1hssetr);
-       rzg2l_mipi_dsi_link_write(dsi, VICH1HPSETR, vich1hpsetr);
-
-       /*
-        * Configuration for Delay Value
-        * Delay value based on 2 ranges of video clock.
-        * 74.25MHz is videoclock of HD@60p or FHD@30p
-        */
-       if (mode->clock > 74250) {
-               delay[0] = 231;
-               delay[1] = 216;
-       } else {
-               delay[0] = 220;
-               delay[1] = 212;
-       }
-
-       if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
-               index = 0;
-       else
-               index = 1;
-
-       rzg2l_mipi_dsi_link_write(dsi, VICH1SET1R,
-                                 VICH1SET1R_DLY(delay[index]));
-}
-
-static int rzg2l_mipi_dsi_start_hs_clock(struct rzg2l_mipi_dsi *dsi)
-{
-       bool is_clk_cont;
-       u32 hsclksetr;
-       u32 status;
-       int ret;
-
-       is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS);
-
-       /* Start HS clock */
-       hsclksetr = HSCLKSETR_HSCLKRUN_HS | (is_clk_cont ?
-                                            HSCLKSETR_HSCLKMODE_CONT :
-                                            HSCLKSETR_HSCLKMODE_NON_CONT);
-       rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR, hsclksetr);
-
-       if (is_clk_cont) {
-               ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
-                                       status & PLSR_CLLP2HS,
-                                       2000, 20000, false, dsi, PLSR);
-               if (ret < 0) {
-                       dev_err(dsi->dev, "failed to start HS clock\n");
-                       return ret;
-               }
-       }
-
-       dev_dbg(dsi->dev, "Start High Speed Clock with %s clock mode",
-               is_clk_cont ? "continuous" : "non-continuous");
-
-       return 0;
-}
-
-static int rzg2l_mipi_dsi_stop_hs_clock(struct rzg2l_mipi_dsi *dsi)
-{
-       bool is_clk_cont;
-       u32 status;
-       int ret;
-
-       is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS);
-
-       /* Stop HS clock */
-       rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR,
-                                 is_clk_cont ? HSCLKSETR_HSCLKMODE_CONT :
-                                 HSCLKSETR_HSCLKMODE_NON_CONT);
-
-       if (is_clk_cont) {
-               ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
-                                       status & PLSR_CLHS2LP,
-                                       2000, 20000, false, dsi, PLSR);
-               if (ret < 0) {
-                       dev_err(dsi->dev, "failed to stop HS clock\n");
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-static int rzg2l_mipi_dsi_start_video(struct rzg2l_mipi_dsi *dsi)
-{
-       u32 vich1set0r;
-       u32 status;
-       int ret;
-
-       /* Configuration for Blanking sequence and start video input*/
-       vich1set0r = VICH1SET0R_HFPNOLP | VICH1SET0R_HBPNOLP |
-                    VICH1SET0R_HSANOLP | VICH1SET0R_VSTART;
-       rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, vich1set0r);
-
-       ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
-                               status & VICH1SR_VIRDY,
-                               2000, 20000, false, dsi, VICH1SR);
-       if (ret < 0)
-               dev_err(dsi->dev, "Failed to start video signal input\n");
-
-       return ret;
-}
-
-static int rzg2l_mipi_dsi_stop_video(struct rzg2l_mipi_dsi *dsi)
-{
-       u32 status;
-       int ret;
-
-       rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, VICH1SET0R_VSTPAFT);
-       ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
-                               (status & VICH1SR_STOP) && (!(status & VICH1SR_RUNNING)),
-                               2000, 20000, false, dsi, VICH1SR);
-       if (ret < 0)
-               goto err;
-
-       ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
-                               !(status & LINKSR_HSBUSY),
-                               2000, 20000, false, dsi, LINKSR);
-       if (ret < 0)
-               goto err;
-
-       return 0;
-
-err:
-       dev_err(dsi->dev, "Failed to stop video signal input\n");
-       return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * Bridge
- */
-
-static int rzg2l_mipi_dsi_attach(struct drm_bridge *bridge,
-                                enum drm_bridge_attach_flags flags)
-{
-       struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
-
-       return drm_bridge_attach(bridge->encoder, dsi->next_bridge, bridge,
-                                flags);
-}
-
-static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
-                                        struct drm_bridge_state *old_bridge_state)
-{
-       struct drm_atomic_state *state = old_bridge_state->base.state;
-       struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
-       const struct drm_display_mode *mode;
-       struct drm_connector *connector;
-       struct drm_crtc *crtc;
-       int ret;
-
-       connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
-       crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
-       mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;
-
-       ret = rzg2l_mipi_dsi_startup(dsi, mode);
-       if (ret < 0)
-               return;
-
-       rzg2l_mipi_dsi_set_display_timing(dsi, mode);
-
-       ret = rzg2l_mipi_dsi_start_hs_clock(dsi);
-       if (ret < 0)
-               goto err_stop;
-
-       ret = rzg2l_mipi_dsi_start_video(dsi);
-       if (ret < 0)
-               goto err_stop_clock;
-
-       return;
-
-err_stop_clock:
-       rzg2l_mipi_dsi_stop_hs_clock(dsi);
-err_stop:
-       rzg2l_mipi_dsi_stop(dsi);
-}
-
-static void rzg2l_mipi_dsi_atomic_disable(struct drm_bridge *bridge,
-                                         struct drm_bridge_state *old_bridge_state)
-{
-       struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
-
-       rzg2l_mipi_dsi_stop_video(dsi);
-       rzg2l_mipi_dsi_stop_hs_clock(dsi);
-       rzg2l_mipi_dsi_stop(dsi);
-}
-
-static enum drm_mode_status
-rzg2l_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
-                                const struct drm_display_info *info,
-                                const struct drm_display_mode *mode)
-{
-       if (mode->clock > 148500)
-               return MODE_CLOCK_HIGH;
-
-       return MODE_OK;
-}
-
-static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = {
-       .attach = rzg2l_mipi_dsi_attach,
-       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
-       .atomic_reset = drm_atomic_helper_bridge_reset,
-       .atomic_enable = rzg2l_mipi_dsi_atomic_enable,
-       .atomic_disable = rzg2l_mipi_dsi_atomic_disable,
-       .mode_valid = rzg2l_mipi_dsi_bridge_mode_valid,
-};
-
-/* -----------------------------------------------------------------------------
- * Host setting
- */
-
-static int rzg2l_mipi_dsi_host_attach(struct mipi_dsi_host *host,
-                                     struct mipi_dsi_device *device)
-{
-       struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host);
-       int ret;
-
-       if (device->lanes > dsi->num_data_lanes) {
-               dev_err(dsi->dev,
-                       "Number of lines of device (%u) exceeds host (%u)\n",
-                       device->lanes, dsi->num_data_lanes);
-               return -EINVAL;
-       }
-
-       switch (mipi_dsi_pixel_format_to_bpp(device->format)) {
-       case 24:
-       case 18:
-               break;
-       default:
-               dev_err(dsi->dev, "Unsupported format 0x%04x\n", device->format);
-               return -EINVAL;
-       }
-
-       dsi->lanes = device->lanes;
-       dsi->format = device->format;
-       dsi->mode_flags = device->mode_flags;
-
-       dsi->next_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node,
-                                                 1, 0);
-       if (IS_ERR(dsi->next_bridge)) {
-               ret = PTR_ERR(dsi->next_bridge);
-               dev_err(dsi->dev, "failed to get next bridge: %d\n", ret);
-               return ret;
-       }
-
-       drm_bridge_add(&dsi->bridge);
-
-       return 0;
-}
-
-static int rzg2l_mipi_dsi_host_detach(struct mipi_dsi_host *host,
-                                     struct mipi_dsi_device *device)
-{
-       struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host);
-
-       drm_bridge_remove(&dsi->bridge);
-
-       return 0;
-}
-
-static const struct mipi_dsi_host_ops rzg2l_mipi_dsi_host_ops = {
-       .attach = rzg2l_mipi_dsi_host_attach,
-       .detach = rzg2l_mipi_dsi_host_detach,
-};
-
-/* -----------------------------------------------------------------------------
- * Power Management
- */
-
-static int __maybe_unused rzg2l_mipi_pm_runtime_suspend(struct device *dev)
-{
-       struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev);
-
-       reset_control_assert(dsi->prstc);
-       reset_control_assert(dsi->arstc);
-
-       return 0;
-}
-
-static int __maybe_unused rzg2l_mipi_pm_runtime_resume(struct device *dev)
-{
-       struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev);
-       int ret;
-
-       ret = reset_control_deassert(dsi->arstc);
-       if (ret < 0)
-               return ret;
-
-       ret = reset_control_deassert(dsi->prstc);
-       if (ret < 0)
-               reset_control_assert(dsi->arstc);
-
-       return ret;
-}
-
-static const struct dev_pm_ops rzg2l_mipi_pm_ops = {
-       SET_RUNTIME_PM_OPS(rzg2l_mipi_pm_runtime_suspend, rzg2l_mipi_pm_runtime_resume, NULL)
-};
-
-/* -----------------------------------------------------------------------------
- * Probe & Remove
- */
-
-static int rzg2l_mipi_dsi_probe(struct platform_device *pdev)
-{
-       unsigned int num_data_lanes;
-       struct rzg2l_mipi_dsi *dsi;
-       u32 txsetr;
-       int ret;
-
-       dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
-       if (!dsi)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, dsi);
-       dsi->dev = &pdev->dev;
-
-       ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4);
-       if (ret < 0)
-               return dev_err_probe(dsi->dev, ret,
-                                    "missing or invalid data-lanes property\n");
-
-       num_data_lanes = ret;
-
-       dsi->mmio = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(dsi->mmio))
-               return PTR_ERR(dsi->mmio);
-
-       dsi->vclk = devm_clk_get(dsi->dev, "vclk");
-       if (IS_ERR(dsi->vclk))
-               return PTR_ERR(dsi->vclk);
-
-       dsi->rstc = devm_reset_control_get_exclusive(dsi->dev, "rst");
-       if (IS_ERR(dsi->rstc))
-               return dev_err_probe(dsi->dev, PTR_ERR(dsi->rstc),
-                                    "failed to get rst\n");
-
-       dsi->arstc = devm_reset_control_get_exclusive(dsi->dev, "arst");
-       if (IS_ERR(dsi->arstc))
-               return dev_err_probe(&pdev->dev, PTR_ERR(dsi->arstc),
-                                    "failed to get arst\n");
-
-       dsi->prstc = devm_reset_control_get_exclusive(dsi->dev, "prst");
-       if (IS_ERR(dsi->prstc))
-               return dev_err_probe(dsi->dev, PTR_ERR(dsi->prstc),
-                                    "failed to get prst\n");
-
-       platform_set_drvdata(pdev, dsi);
-
-       pm_runtime_enable(dsi->dev);
-
-       ret = pm_runtime_resume_and_get(dsi->dev);
-       if (ret < 0)
-               goto err_pm_disable;
-
-       /*
-        * TXSETR register can be read only after DPHY init. But during probe
-        * mode->clock and format are not available. So initialize DPHY with
-        * timing parameters for 80Mbps.
-        */
-       ret = rzg2l_mipi_dsi_dphy_init(dsi, 80000);
-       if (ret < 0)
-               goto err_phy;
-
-       txsetr = rzg2l_mipi_dsi_link_read(dsi, TXSETR);
-       dsi->num_data_lanes = min(((txsetr >> 16) & 3) + 1, num_data_lanes);
-       rzg2l_mipi_dsi_dphy_exit(dsi);
-       pm_runtime_put(dsi->dev);
-
-       /* Initialize the DRM bridge. */
-       dsi->bridge.funcs = &rzg2l_mipi_dsi_bridge_ops;
-       dsi->bridge.of_node = dsi->dev->of_node;
-
-       /* Init host device */
-       dsi->host.dev = dsi->dev;
-       dsi->host.ops = &rzg2l_mipi_dsi_host_ops;
-       ret = mipi_dsi_host_register(&dsi->host);
-       if (ret < 0)
-               goto err_pm_disable;
-
-       return 0;
-
-err_phy:
-       rzg2l_mipi_dsi_dphy_exit(dsi);
-       pm_runtime_put(dsi->dev);
-err_pm_disable:
-       pm_runtime_disable(dsi->dev);
-       return ret;
-}
-
-static int rzg2l_mipi_dsi_remove(struct platform_device *pdev)
-{
-       struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev);
-
-       mipi_dsi_host_unregister(&dsi->host);
-       pm_runtime_disable(&pdev->dev);
-
-       return 0;
-}
-
-static const struct of_device_id rzg2l_mipi_dsi_of_table[] = {
-       { .compatible = "renesas,rzg2l-mipi-dsi" },
-       { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, rzg2l_mipi_dsi_of_table);
-
-static struct platform_driver rzg2l_mipi_dsi_platform_driver = {
-       .probe  = rzg2l_mipi_dsi_probe,
-       .remove = rzg2l_mipi_dsi_remove,
-       .driver = {
-               .name = "rzg2l-mipi-dsi",
-               .pm = &rzg2l_mipi_pm_ops,
-               .of_match_table = rzg2l_mipi_dsi_of_table,
-       },
-};
-
-module_platform_driver(rzg2l_mipi_dsi_platform_driver);
-
-MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
-MODULE_DESCRIPTION("Renesas RZ/G2L MIPI DSI Encoder Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi_regs.h b/drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi_regs.h
deleted file mode 100644 (file)
index 1dbc16e..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * RZ/G2L MIPI DSI Interface Registers Definitions
- *
- * Copyright (C) 2022 Renesas Electronics Corporation
- */
-
-#ifndef __RZG2L_MIPI_DSI_REGS_H__
-#define __RZG2L_MIPI_DSI_REGS_H__
-
-#include <linux/bits.h>
-
-/* DPHY Registers */
-#define DSIDPHYCTRL0                   0x00
-#define DSIDPHYCTRL0_CAL_EN_HSRX_OFS   BIT(16)
-#define DSIDPHYCTRL0_CMN_MASTER_EN     BIT(8)
-#define DSIDPHYCTRL0_RE_VDD_DETVCCQLV18        BIT(2)
-#define DSIDPHYCTRL0_EN_LDO1200                BIT(1)
-#define DSIDPHYCTRL0_EN_BGR            BIT(0)
-
-#define DSIDPHYTIM0                    0x04
-#define DSIDPHYTIM0_TCLK_MISS(x)       ((x) << 24)
-#define DSIDPHYTIM0_T_INIT(x)          ((x) << 0)
-
-#define DSIDPHYTIM1                    0x08
-#define DSIDPHYTIM1_THS_PREPARE(x)     ((x) << 24)
-#define DSIDPHYTIM1_TCLK_PREPARE(x)    ((x) << 16)
-#define DSIDPHYTIM1_THS_SETTLE(x)      ((x) << 8)
-#define DSIDPHYTIM1_TCLK_SETTLE(x)     ((x) << 0)
-
-#define DSIDPHYTIM2                    0x0c
-#define DSIDPHYTIM2_TCLK_TRAIL(x)      ((x) << 24)
-#define DSIDPHYTIM2_TCLK_POST(x)       ((x) << 16)
-#define DSIDPHYTIM2_TCLK_PRE(x)                ((x) << 8)
-#define DSIDPHYTIM2_TCLK_ZERO(x)       ((x) << 0)
-
-#define DSIDPHYTIM3                    0x10
-#define DSIDPHYTIM3_TLPX(x)            ((x) << 24)
-#define DSIDPHYTIM3_THS_EXIT(x)                ((x) << 16)
-#define DSIDPHYTIM3_THS_TRAIL(x)       ((x) << 8)
-#define DSIDPHYTIM3_THS_ZERO(x)                ((x) << 0)
-
-/* --------------------------------------------------------*/
-/* Link Registers */
-#define LINK_REG_OFFSET                        0x10000
-
-/* Link Status Register */
-#define LINKSR                         0x10
-#define LINKSR_LPBUSY                  BIT(13)
-#define LINKSR_HSBUSY                  BIT(12)
-#define LINKSR_VICHRUN1                        BIT(8)
-#define LINKSR_SQCHRUN1                        BIT(4)
-#define LINKSR_SQCHRUN0                        BIT(0)
-
-/* Tx Set Register */
-#define TXSETR                         0x100
-#define TXSETR_NUMLANECAP              (0x3 << 16)
-#define TXSETR_DLEN                    (1 << 9)
-#define TXSETR_CLEN                    (1 << 8)
-#define TXSETR_NUMLANEUSE(x)           (((x) & 0x3) << 0)
-
-/* HS Clock Set Register */
-#define HSCLKSETR                      0x104
-#define HSCLKSETR_HSCLKMODE_CONT       (1 << 1)
-#define HSCLKSETR_HSCLKMODE_NON_CONT   (0 << 1)
-#define HSCLKSETR_HSCLKRUN_HS          (1 << 0)
-#define HSCLKSETR_HSCLKRUN_LP          (0 << 0)
-
-/* Reset Control Register */
-#define RSTCR                          0x110
-#define RSTCR_SWRST                    BIT(0)
-#define RSTCR_FCETXSTP                 BIT(16)
-
-/* Reset Status Register */
-#define RSTSR                          0x114
-#define RSTSR_DL0DIR                   (1 << 15)
-#define RSTSR_DLSTPST                  (0xf << 8)
-#define RSTSR_SWRSTV1                  (1 << 4)
-#define RSTSR_SWRSTIB                  (1 << 3)
-#define RSTSR_SWRSTAPB                 (1 << 2)
-#define RSTSR_SWRSTLP                  (1 << 1)
-#define RSTSR_SWRSTHS                  (1 << 0)
-
-/* Clock Lane Stop Time Set Register */
-#define CLSTPTSETR                     0x314
-#define CLSTPTSETR_CLKKPT(x)           ((x) << 24)
-#define CLSTPTSETR_CLKBFHT(x)          ((x) << 16)
-#define CLSTPTSETR_CLKSTPT(x)          ((x) << 2)
-
-/* LP Transition Time Set Register */
-#define LPTRNSTSETR                    0x318
-#define LPTRNSTSETR_GOLPBKT(x)         ((x) << 0)
-
-/* Physical Lane Status Register */
-#define PLSR                           0x320
-#define PLSR_CLHS2LP                   BIT(27)
-#define PLSR_CLLP2HS                   BIT(26)
-
-/* Video-Input Channel 1 Set 0 Register */
-#define VICH1SET0R                     0x400
-#define VICH1SET0R_VSEN                        BIT(12)
-#define VICH1SET0R_HFPNOLP             BIT(10)
-#define VICH1SET0R_HBPNOLP             BIT(9)
-#define VICH1SET0R_HSANOLP             BIT(8)
-#define VICH1SET0R_VSTPAFT             BIT(1)
-#define VICH1SET0R_VSTART              BIT(0)
-
-/* Video-Input Channel 1 Set 1 Register */
-#define VICH1SET1R                     0x404
-#define VICH1SET1R_DLY(x)              (((x) & 0xfff) << 2)
-
-/* Video-Input Channel 1 Status Register */
-#define VICH1SR                                0x410
-#define VICH1SR_VIRDY                  BIT(3)
-#define VICH1SR_RUNNING                        BIT(2)
-#define VICH1SR_STOP                   BIT(1)
-#define VICH1SR_START                  BIT(0)
-
-/* Video-Input Channel 1 Pixel Packet Set Register */
-#define VICH1PPSETR                    0x420
-#define VICH1PPSETR_DT_RGB18           (0x1e << 16)
-#define VICH1PPSETR_DT_RGB18_LS                (0x2e << 16)
-#define VICH1PPSETR_DT_RGB24           (0x3e << 16)
-#define VICH1PPSETR_TXESYNC_PULSE      (1 << 15)
-#define VICH1PPSETR_VC(x)              ((x) << 22)
-
-/* Video-Input Channel 1 Vertical Size Set Register */
-#define VICH1VSSETR                    0x428
-#define VICH1VSSETR_VACTIVE(x)         (((x) & 0x7fff) << 16)
-#define VICH1VSSETR_VSPOL_LOW          (1 << 15)
-#define VICH1VSSETR_VSPOL_HIGH         (0 << 15)
-#define VICH1VSSETR_VSA(x)             (((x) & 0xfff) << 0)
-
-/* Video-Input Channel 1 Vertical Porch Set Register */
-#define VICH1VPSETR                    0x42c
-#define VICH1VPSETR_VFP(x)             (((x) & 0x1fff) << 16)
-#define VICH1VPSETR_VBP(x)             (((x) & 0x1fff) << 0)
-
-/* Video-Input Channel 1 Horizontal Size Set Register */
-#define VICH1HSSETR                    0x430
-#define VICH1HSSETR_HACTIVE(x)         (((x) & 0x7fff) << 16)
-#define VICH1HSSETR_HSPOL_LOW          (1 << 15)
-#define VICH1HSSETR_HSPOL_HIGH         (0 << 15)
-#define VICH1HSSETR_HSA(x)             (((x) & 0xfff) << 0)
-
-/* Video-Input Channel 1 Horizontal Porch Set Register */
-#define VICH1HPSETR                    0x434
-#define VICH1HPSETR_HFP(x)             (((x) & 0x1fff) << 16)
-#define VICH1HPSETR_HBP(x)             (((x) & 0x1fff) << 0)
-
-#endif /* __RZG2L_MIPI_DSI_REGS_H__ */
diff --git a/drivers/gpu/drm/renesas/Kconfig b/drivers/gpu/drm/renesas/Kconfig
new file mode 100644 (file)
index 0000000..3777dad
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/gpu/drm/renesas/rcar-du/Kconfig"
+source "drivers/gpu/drm/renesas/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/renesas/Makefile b/drivers/gpu/drm/renesas/Makefile
new file mode 100644 (file)
index 0000000..ec0e89e
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += rcar-du/
+obj-$(CONFIG_DRM_SHMOBILE) += shmobile/
diff --git a/drivers/gpu/drm/renesas/rcar-du/Kconfig b/drivers/gpu/drm/renesas/rcar-du/Kconfig
new file mode 100644 (file)
index 0000000..53c356a
--- /dev/null
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_RCAR_DU
+       tristate "DRM Support for R-Car Display Unit"
+       depends on DRM && OF
+       depends on ARM || ARM64
+       depends on ARCH_RENESAS || COMPILE_TEST
+       select DRM_KMS_HELPER
+       select DRM_GEM_DMA_HELPER
+       select VIDEOMODE_HELPERS
+       help
+         Choose this option if you have an R-Car chipset.
+         If M is selected the module will be called rcar-du-drm.
+
+config DRM_RCAR_USE_CMM
+       bool "R-Car DU Color Management Module (CMM) Support"
+       depends on DRM_RCAR_DU
+       default DRM_RCAR_DU
+       help
+         Enable support for R-Car Color Management Module (CMM).
+
+config DRM_RCAR_CMM
+       def_tristate DRM_RCAR_DU
+       depends on DRM_RCAR_USE_CMM
+
+config DRM_RCAR_DW_HDMI
+       tristate "R-Car Gen3 and RZ/G2 DU HDMI Encoder Support"
+       depends on DRM && OF
+       depends on DRM_RCAR_DU || COMPILE_TEST
+       select DRM_DW_HDMI
+       help
+         Enable support for R-Car Gen3 or RZ/G2 internal HDMI encoder.
+
+config DRM_RCAR_USE_LVDS
+       bool "R-Car DU LVDS Encoder Support"
+       depends on DRM_BRIDGE && OF
+       depends on DRM_RCAR_DU || COMPILE_TEST
+       default DRM_RCAR_DU
+       help
+         Enable support for the R-Car Display Unit embedded LVDS encoders.
+
+config DRM_RCAR_LVDS
+       def_tristate DRM_RCAR_DU
+       depends on DRM_RCAR_USE_LVDS
+       depends on PM
+       select DRM_KMS_HELPER
+       select DRM_PANEL
+       select RESET_CONTROLLER
+
+config DRM_RCAR_USE_MIPI_DSI
+       bool "R-Car DU MIPI DSI Encoder Support"
+       depends on DRM_BRIDGE && OF
+       depends on DRM_RCAR_DU || COMPILE_TEST
+       default DRM_RCAR_DU
+       help
+         Enable support for the R-Car Display Unit embedded MIPI DSI encoders.
+
+config DRM_RCAR_MIPI_DSI
+       def_tristate DRM_RCAR_DU
+       depends on DRM_RCAR_USE_MIPI_DSI
+       select DRM_MIPI_DSI
+       select RESET_CONTROLLER
+
+config DRM_RZG2L_MIPI_DSI
+       tristate "RZ/G2L MIPI DSI Encoder Support"
+       depends on DRM && DRM_BRIDGE && OF
+       depends on ARCH_RENESAS || COMPILE_TEST
+       select DRM_MIPI_DSI
+       help
+         Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders.
+
+config DRM_RCAR_VSP
+       bool "R-Car DU VSP Compositor Support" if ARM
+       default y if ARM64
+       depends on DRM_RCAR_DU
+       depends on VIDEO_RENESAS_VSP1=y || (VIDEO_RENESAS_VSP1 && DRM_RCAR_DU=m)
+       help
+         Enable support to expose the R-Car VSP Compositor as KMS planes.
+
+config DRM_RCAR_WRITEBACK
+       bool
+       default y if ARM64
+       depends on DRM_RCAR_DU
diff --git a/drivers/gpu/drm/renesas/rcar-du/Makefile b/drivers/gpu/drm/renesas/rcar-du/Makefile
new file mode 100644 (file)
index 0000000..b8f2c82
--- /dev/null
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+rcar-du-drm-y := rcar_du_crtc.o \
+                rcar_du_drv.o \
+                rcar_du_encoder.o \
+                rcar_du_group.o \
+                rcar_du_kms.o \
+                rcar_du_plane.o \
+
+rcar-du-drm-$(CONFIG_DRM_RCAR_VSP)     += rcar_du_vsp.o
+rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
+
+obj-$(CONFIG_DRM_RCAR_CMM)             += rcar_cmm.o
+obj-$(CONFIG_DRM_RCAR_DU)              += rcar-du-drm.o
+obj-$(CONFIG_DRM_RCAR_DW_HDMI)         += rcar_dw_hdmi.o
+obj-$(CONFIG_DRM_RCAR_LVDS)            += rcar_lvds.o
+obj-$(CONFIG_DRM_RCAR_MIPI_DSI)                += rcar_mipi_dsi.o
+
+obj-$(CONFIG_DRM_RZG2L_MIPI_DSI)       += rzg2l_mipi_dsi.o
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c b/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c
new file mode 100644 (file)
index 0000000..e2a67dd
--- /dev/null
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit Color Management Module
+ *
+ * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_color_mgmt.h>
+
+#include "rcar_cmm.h"
+
+#define CM2_LUT_CTRL           0x0000
+#define CM2_LUT_CTRL_LUT_EN    BIT(0)
+#define CM2_LUT_TBL_BASE       0x0600
+#define CM2_LUT_TBL(__i)       (CM2_LUT_TBL_BASE + (__i) * 4)
+
+struct rcar_cmm {
+       void __iomem *base;
+
+       /*
+        * @lut:                1D-LUT state
+        * @lut.enabled:        1D-LUT enabled flag
+        */
+       struct {
+               bool enabled;
+       } lut;
+};
+
+static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg)
+{
+       return ioread32(rcmm->base + reg);
+}
+
+static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data)
+{
+       iowrite32(data, rcmm->base + reg);
+}
+
+/*
+ * rcar_cmm_lut_write() - Scale the DRM LUT table entries to hardware precision
+ *                       and write to the CMM registers
+ * @rcmm: Pointer to the CMM device
+ * @drm_lut: Pointer to the DRM LUT table
+ */
+static void rcar_cmm_lut_write(struct rcar_cmm *rcmm,
+                              const struct drm_color_lut *drm_lut)
+{
+       unsigned int i;
+
+       for (i = 0; i < CM2_LUT_SIZE; ++i) {
+               u32 entry = drm_color_lut_extract(drm_lut[i].red, 8) << 16
+                         | drm_color_lut_extract(drm_lut[i].green, 8) << 8
+                         | drm_color_lut_extract(drm_lut[i].blue, 8);
+
+               rcar_cmm_write(rcmm, CM2_LUT_TBL(i), entry);
+       }
+}
+
+/*
+ * rcar_cmm_setup() - Configure the CMM unit
+ * @pdev: The platform device associated with the CMM instance
+ * @config: The CMM unit configuration
+ *
+ * Configure the CMM unit with the given configuration. Currently enabling,
+ * disabling and programming of the 1-D LUT unit is supported.
+ *
+ * As rcar_cmm_setup() accesses the CMM registers the unit should be powered
+ * and its functional clock enabled. To guarantee this, before any call to
+ * this function is made, the CMM unit has to be enabled by calling
+ * rcar_cmm_enable() first.
+ *
+ * TODO: Add support for LUT double buffer operations to avoid updating the
+ * LUT table entries while a frame is being displayed.
+ */
+int rcar_cmm_setup(struct platform_device *pdev,
+                  const struct rcar_cmm_config *config)
+{
+       struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
+
+       /* Disable LUT if no table is provided. */
+       if (!config->lut.table) {
+               if (rcmm->lut.enabled) {
+                       rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
+                       rcmm->lut.enabled = false;
+               }
+
+               return 0;
+       }
+
+       /* Enable LUT and program the new gamma table values. */
+       if (!rcmm->lut.enabled) {
+               rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN);
+               rcmm->lut.enabled = true;
+       }
+
+       rcar_cmm_lut_write(rcmm, config->lut.table);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_setup);
+
+/*
+ * rcar_cmm_enable() - Enable the CMM unit
+ * @pdev: The platform device associated with the CMM instance
+ *
+ * When the output of the corresponding DU channel is routed to the CMM unit,
+ * the unit shall be enabled before the DU channel is started, and remain
+ * enabled until the channel is stopped. The CMM unit shall be disabled with
+ * rcar_cmm_disable().
+ *
+ * Calls to rcar_cmm_enable() and rcar_cmm_disable() are not reference-counted.
+ * It is an error to attempt to enable an already enabled CMM unit, or to
+ * attempt to disable a disabled unit.
+ */
+int rcar_cmm_enable(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = pm_runtime_resume_and_get(&pdev->dev);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_enable);
+
+/*
+ * rcar_cmm_disable() - Disable the CMM unit
+ * @pdev: The platform device associated with the CMM instance
+ *
+ * See rcar_cmm_enable() for usage information.
+ *
+ * Disabling the CMM unit disable all the internal processing blocks. The CMM
+ * state shall thus be restored with rcar_cmm_setup() when re-enabling the CMM
+ * unit after the next rcar_cmm_enable() call.
+ */
+void rcar_cmm_disable(struct platform_device *pdev)
+{
+       struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
+
+       rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0);
+       rcmm->lut.enabled = false;
+
+       pm_runtime_put(&pdev->dev);
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_disable);
+
+/*
+ * rcar_cmm_init() - Initialize the CMM unit
+ * @pdev: The platform device associated with the CMM instance
+ *
+ * Return: 0 on success, -EPROBE_DEFER if the CMM is not available yet,
+ *         -ENODEV if the DRM_RCAR_CMM config option is disabled
+ */
+int rcar_cmm_init(struct platform_device *pdev)
+{
+       struct rcar_cmm *rcmm = platform_get_drvdata(pdev);
+
+       if (!rcmm)
+               return -EPROBE_DEFER;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rcar_cmm_init);
+
+static int rcar_cmm_probe(struct platform_device *pdev)
+{
+       struct rcar_cmm *rcmm;
+
+       rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL);
+       if (!rcmm)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, rcmm);
+
+       rcmm->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(rcmm->base))
+               return PTR_ERR(rcmm->base);
+
+       pm_runtime_enable(&pdev->dev);
+
+       return 0;
+}
+
+static int rcar_cmm_remove(struct platform_device *pdev)
+{
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id rcar_cmm_of_table[] = {
+       { .compatible = "renesas,rcar-gen3-cmm", },
+       { .compatible = "renesas,rcar-gen2-cmm", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rcar_cmm_of_table);
+
+static struct platform_driver rcar_cmm_platform_driver = {
+       .probe          = rcar_cmm_probe,
+       .remove         = rcar_cmm_remove,
+       .driver         = {
+               .name   = "rcar-cmm",
+               .of_match_table = rcar_cmm_of_table,
+       },
+};
+
+module_platform_driver(rcar_cmm_platform_driver);
+
+MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
+MODULE_DESCRIPTION("Renesas R-Car CMM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.h b/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.h
new file mode 100644 (file)
index 0000000..628072a
--- /dev/null
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit Color Management Module
+ *
+ * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org>
+ */
+
+#ifndef __RCAR_CMM_H__
+#define __RCAR_CMM_H__
+
+#define CM2_LUT_SIZE           256
+
+struct drm_color_lut;
+struct platform_device;
+
+/**
+ * struct rcar_cmm_config - CMM configuration
+ *
+ * @lut:       1D-LUT configuration
+ * @lut.table: 1D-LUT table entries. Disable LUT operations when NULL
+ */
+struct rcar_cmm_config {
+       struct {
+               struct drm_color_lut *table;
+       } lut;
+};
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_CMM)
+int rcar_cmm_init(struct platform_device *pdev);
+
+int rcar_cmm_enable(struct platform_device *pdev);
+void rcar_cmm_disable(struct platform_device *pdev);
+
+int rcar_cmm_setup(struct platform_device *pdev,
+                  const struct rcar_cmm_config *config);
+#else
+static inline int rcar_cmm_init(struct platform_device *pdev)
+{
+       return -ENODEV;
+}
+
+static inline int rcar_cmm_enable(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static inline void rcar_cmm_disable(struct platform_device *pdev)
+{
+}
+
+static inline int rcar_cmm_setup(struct platform_device *pdev,
+                                const struct rcar_cmm_config *config)
+{
+       return 0;
+}
+#endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */
+
+#endif /* __RCAR_CMM_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
new file mode 100644 (file)
index 0000000..7e175db
--- /dev/null
@@ -0,0 +1,1338 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "rcar_cmm.h"
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vsp.h"
+#include "rcar_lvds.h"
+#include "rcar_mipi_dsi.h"
+
+static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
+}
+
+static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
+}
+
+static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
+}
+
+static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
+}
+
+void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       rcrtc->dsysr = (rcrtc->dsysr & ~clr) | set;
+       rcar_du_write(rcdu, rcrtc->mmio_offset + DSYSR, rcrtc->dsysr);
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware Setup
+ */
+
+struct dpll_info {
+       unsigned int output;
+       unsigned int fdpll;
+       unsigned int n;
+       unsigned int m;
+};
+
+static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc,
+                                struct dpll_info *dpll,
+                                unsigned long input,
+                                unsigned long target)
+{
+       unsigned long best_diff = (unsigned long)-1;
+       unsigned long diff;
+       unsigned int fdpll;
+       unsigned int m;
+       unsigned int n;
+
+       /*
+        *   fin                                 fvco        fout       fclkout
+        * in --> [1/M] --> |PD| -> [LPF] -> [VCO] -> [1/P] -+-> [1/FDPLL] -> out
+        *              +-> |  |                             |
+        *              |                                    |
+        *              +---------------- [1/N] <------------+
+        *
+        *      fclkout = fvco / P / FDPLL -- (1)
+        *
+        * fin/M = fvco/P/N
+        *
+        *      fvco = fin * P *  N / M -- (2)
+        *
+        * (1) + (2) indicates
+        *
+        *      fclkout = fin * N / M / FDPLL
+        *
+        * NOTES
+        *      N       : (n + 1)
+        *      M       : (m + 1)
+        *      FDPLL   : (fdpll + 1)
+        *      P       : 2
+        *      2kHz < fvco < 4096MHz
+        *
+        * To minimize the jitter,
+        * N : as large as possible
+        * M : as small as possible
+        */
+       for (m = 0; m < 4; m++) {
+               for (n = 119; n > 38; n--) {
+                       /*
+                        * This code only runs on 64-bit architectures, the
+                        * unsigned long type can thus be used for 64-bit
+                        * computation. It will still compile without any
+                        * warning on 32-bit architectures.
+                        *
+                        * To optimize calculations, use fout instead of fvco
+                        * to verify the VCO frequency constraint.
+                        */
+                       unsigned long fout = input * (n + 1) / (m + 1);
+
+                       if (fout < 1000 || fout > 2048 * 1000 * 1000U)
+                               continue;
+
+                       for (fdpll = 1; fdpll < 32; fdpll++) {
+                               unsigned long output;
+
+                               output = fout / (fdpll + 1);
+                               if (output >= 400 * 1000 * 1000)
+                                       continue;
+
+                               diff = abs((long)output - (long)target);
+                               if (best_diff > diff) {
+                                       best_diff = diff;
+                                       dpll->n = n;
+                                       dpll->m = m;
+                                       dpll->fdpll = fdpll;
+                                       dpll->output = output;
+                               }
+
+                               if (diff == 0)
+                                       goto done;
+                       }
+               }
+       }
+
+done:
+       dev_dbg(rcrtc->dev->dev,
+               "output:%u, fdpll:%u, n:%u, m:%u, diff:%lu\n",
+                dpll->output, dpll->fdpll, dpll->n, dpll->m, best_diff);
+}
+
+struct du_clk_params {
+       struct clk *clk;
+       unsigned long rate;
+       unsigned long diff;
+       u32 escr;
+};
+
+static void rcar_du_escr_divider(struct clk *clk, unsigned long target,
+                                u32 escr, struct du_clk_params *params)
+{
+       unsigned long rate;
+       unsigned long diff;
+       u32 div;
+
+       /*
+        * If the target rate has already been achieved perfectly we can't do
+        * better.
+        */
+       if (params->diff == 0)
+               return;
+
+       /*
+        * Compute the input clock rate and internal divisor values to obtain
+        * the clock rate closest to the target frequency.
+        */
+       rate = clk_round_rate(clk, target);
+       div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1;
+       diff = abs(rate / (div + 1) - target);
+
+       /*
+        * Store the parameters if the resulting frequency is better than any
+        * previously calculated value.
+        */
+       if (diff < params->diff) {
+               params->clk = clk;
+               params->rate = rate;
+               params->diff = diff;
+               params->escr = escr | div;
+       }
+}
+
+static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
+{
+       const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
+       struct rcar_du_device *rcdu = rcrtc->dev;
+       unsigned long mode_clock = mode->clock * 1000;
+       unsigned int hdse_offset;
+       u32 dsmr;
+       u32 escr;
+
+       if (rcdu->info->dpll_mask & (1 << rcrtc->index)) {
+               unsigned long target = mode_clock;
+               struct dpll_info dpll = { 0 };
+               unsigned long extclk;
+               u32 dpllcr;
+               u32 div = 0;
+
+               /*
+                * DU channels that have a display PLL can't use the internal
+                * system clock, and have no internal clock divider.
+                */
+               extclk = clk_get_rate(rcrtc->extclock);
+               rcar_du_dpll_divider(rcrtc, &dpll, extclk, target);
+
+               dpllcr = DPLLCR_CODE | DPLLCR_CLKE
+                      | DPLLCR_FDPLL(dpll.fdpll)
+                      | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m)
+                      | DPLLCR_STBY;
+
+               if (rcrtc->index == 1)
+                       dpllcr |= DPLLCR_PLCS1
+                              |  DPLLCR_INCS_DOTCLKIN1;
+               else
+                       dpllcr |= DPLLCR_PLCS0
+                              |  DPLLCR_INCS_DOTCLKIN0;
+
+               rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr);
+
+               escr = ESCR_DCLKSEL_DCLKIN | div;
+       } else if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) ||
+                  rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) {
+               /*
+                * Use the external LVDS or DSI PLL output as the dot clock when
+                * outputting to the LVDS or DSI encoder on an SoC that supports
+                * this clock routing option. We use the clock directly in that
+                * case, without any additional divider.
+                */
+               escr = ESCR_DCLKSEL_DCLKIN;
+       } else {
+               struct du_clk_params params = { .diff = (unsigned long)-1 };
+
+               rcar_du_escr_divider(rcrtc->clock, mode_clock,
+                                    ESCR_DCLKSEL_CLKS, &params);
+               if (rcrtc->extclock)
+                       rcar_du_escr_divider(rcrtc->extclock, mode_clock,
+                                            ESCR_DCLKSEL_DCLKIN, &params);
+
+               dev_dbg(rcrtc->dev->dev, "mode clock %lu %s rate %lu\n",
+                       mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext",
+                       params.rate);
+
+               clk_set_rate(params.clk, params.rate);
+               escr = params.escr;
+       }
+
+       /*
+        * The ESCR register only exists in DU channels that can output to an
+        * LVDS or DPAT, and the OTAR register in DU channels that can output
+        * to a DPAD.
+        */
+       if ((rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs |
+            rcdu->info->routes[RCAR_DU_OUTPUT_DPAD1].possible_crtcs |
+            rcdu->info->routes[RCAR_DU_OUTPUT_LVDS0].possible_crtcs |
+            rcdu->info->routes[RCAR_DU_OUTPUT_LVDS1].possible_crtcs) &
+           BIT(rcrtc->index)) {
+               dev_dbg(rcrtc->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr);
+
+               rcar_du_crtc_write(rcrtc, rcrtc->index % 2 ? ESCR13 : ESCR02, escr);
+       }
+
+       if ((rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs |
+            rcdu->info->routes[RCAR_DU_OUTPUT_DPAD1].possible_crtcs) &
+           BIT(rcrtc->index))
+               rcar_du_crtc_write(rcrtc, rcrtc->index % 2 ? OTAR13 : OTAR02, 0);
+
+       /* Signal polarities */
+       dsmr = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
+            | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
+            | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0)
+            | DSMR_DIPM_DISP | DSMR_CSPM;
+       rcar_du_crtc_write(rcrtc, DSMR, dsmr);
+
+       /*
+        * When the CMM is enabled, an additional offset of 25 pixels must be
+        * subtracted from the HDS (horizontal display start) and HDE
+        * (horizontal display end) registers.
+        */
+       hdse_offset = 19;
+       if (rcrtc->group->cmms_mask & BIT(rcrtc->index % 2))
+               hdse_offset += 25;
+
+       /* Display timings */
+       rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start -
+                                       hdse_offset);
+       rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
+                                       mode->hdisplay - hdse_offset);
+       rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
+                                       mode->hsync_start - 1);
+       rcar_du_crtc_write(rcrtc, HCR,  mode->htotal - 1);
+
+       rcar_du_crtc_write(rcrtc, VDSR, mode->crtc_vtotal -
+                                       mode->crtc_vsync_end - 2);
+       rcar_du_crtc_write(rcrtc, VDER, mode->crtc_vtotal -
+                                       mode->crtc_vsync_end +
+                                       mode->crtc_vdisplay - 2);
+       rcar_du_crtc_write(rcrtc, VSPR, mode->crtc_vtotal -
+                                       mode->crtc_vsync_end +
+                                       mode->crtc_vsync_start - 1);
+       rcar_du_crtc_write(rcrtc, VCR,  mode->crtc_vtotal - 1);
+
+       rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start - 1);
+       rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
+}
+
+static unsigned int plane_zpos(struct rcar_du_plane *plane)
+{
+       return plane->plane.state->normalized_zpos;
+}
+
+static const struct rcar_du_format_info *
+plane_format(struct rcar_du_plane *plane)
+{
+       return to_rcar_plane_state(plane->plane.state)->format;
+}
+
+static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
+       struct rcar_du_device *rcdu = rcrtc->dev;
+       unsigned int num_planes = 0;
+       unsigned int dptsr_planes;
+       unsigned int hwplanes = 0;
+       unsigned int prio = 0;
+       unsigned int i;
+       u32 dspr = 0;
+
+       for (i = 0; i < rcrtc->group->num_planes; ++i) {
+               struct rcar_du_plane *plane = &rcrtc->group->planes[i];
+               unsigned int j;
+
+               if (plane->plane.state->crtc != &rcrtc->crtc ||
+                   !plane->plane.state->visible)
+                       continue;
+
+               /* Insert the plane in the sorted planes array. */
+               for (j = num_planes++; j > 0; --j) {
+                       if (plane_zpos(planes[j-1]) <= plane_zpos(plane))
+                               break;
+                       planes[j] = planes[j-1];
+               }
+
+               planes[j] = plane;
+               prio += plane_format(plane)->planes * 4;
+       }
+
+       for (i = 0; i < num_planes; ++i) {
+               struct rcar_du_plane *plane = planes[i];
+               struct drm_plane_state *state = plane->plane.state;
+               unsigned int index = to_rcar_plane_state(state)->hwindex;
+
+               prio -= 4;
+               dspr |= (index + 1) << prio;
+               hwplanes |= 1 << index;
+
+               if (plane_format(plane)->planes == 2) {
+                       index = (index + 1) % 8;
+
+                       prio -= 4;
+                       dspr |= (index + 1) << prio;
+                       hwplanes |= 1 << index;
+               }
+       }
+
+       /* If VSP+DU integration is enabled the plane assignment is fixed. */
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+               if (rcdu->info->gen < 3) {
+                       dspr = (rcrtc->index % 2) + 1;
+                       hwplanes = 1 << (rcrtc->index % 2);
+               } else {
+                       dspr = (rcrtc->index % 2) ? 3 : 1;
+                       hwplanes = 1 << ((rcrtc->index % 2) ? 2 : 0);
+               }
+       }
+
+       /*
+        * Update the planes to display timing and dot clock generator
+        * associations.
+        *
+        * Updating the DPTSR register requires restarting the CRTC group,
+        * resulting in visible flicker. To mitigate the issue only update the
+        * association if needed by enabled planes. Planes being disabled will
+        * keep their current association.
+        */
+       mutex_lock(&rcrtc->group->lock);
+
+       dptsr_planes = rcrtc->index % 2 ? rcrtc->group->dptsr_planes | hwplanes
+                    : rcrtc->group->dptsr_planes & ~hwplanes;
+
+       if (dptsr_planes != rcrtc->group->dptsr_planes) {
+               rcar_du_group_write(rcrtc->group, DPTSR,
+                                   (dptsr_planes << 16) | dptsr_planes);
+               rcrtc->group->dptsr_planes = dptsr_planes;
+
+               if (rcrtc->group->used_crtcs)
+                       rcar_du_group_restart(rcrtc->group);
+       }
+
+       /* Restart the group if plane sources have changed. */
+       if (rcrtc->group->need_restart)
+               rcar_du_group_restart(rcrtc->group);
+
+       mutex_unlock(&rcrtc->group->lock);
+
+       rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR,
+                           dspr);
+}
+
+/* -----------------------------------------------------------------------------
+ * Page Flip
+ */
+
+void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = rcrtc->event;
+       rcrtc->event = NULL;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       if (event == NULL)
+               return;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       drm_crtc_send_vblank_event(&rcrtc->crtc, event);
+       wake_up(&rcrtc->flip_wait);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       drm_crtc_vblank_put(&rcrtc->crtc);
+}
+
+static bool rcar_du_crtc_page_flip_pending(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+       bool pending;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       pending = rcrtc->event != NULL;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       return pending;
+}
+
+static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       if (wait_event_timeout(rcrtc->flip_wait,
+                              !rcar_du_crtc_page_flip_pending(rcrtc),
+                              msecs_to_jiffies(50)))
+               return;
+
+       dev_warn(rcdu->dev, "page flip timeout\n");
+
+       rcar_du_crtc_finish_page_flip(rcrtc);
+}
+
+/* -----------------------------------------------------------------------------
+ * Color Management Module (CMM)
+ */
+
+static int rcar_du_cmm_check(struct drm_crtc *crtc,
+                            struct drm_crtc_state *state)
+{
+       struct drm_property_blob *drm_lut = state->gamma_lut;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct device *dev = rcrtc->dev->dev;
+
+       if (!drm_lut)
+               return 0;
+
+       /* We only accept fully populated LUT tables. */
+       if (drm_color_lut_size(drm_lut) != CM2_LUT_SIZE) {
+               dev_err(dev, "invalid gamma lut size: %zu bytes\n",
+                       drm_lut->length);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void rcar_du_cmm_setup(struct drm_crtc *crtc)
+{
+       struct drm_property_blob *drm_lut = crtc->state->gamma_lut;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_cmm_config cmm_config = {};
+
+       if (!rcrtc->cmm)
+               return;
+
+       if (drm_lut)
+               cmm_config.lut.table = (struct drm_color_lut *)drm_lut->data;
+
+       rcar_cmm_setup(rcrtc->cmm, &cmm_config);
+}
+
+/* -----------------------------------------------------------------------------
+ * Start/Stop and Suspend/Resume
+ */
+
+static void rcar_du_crtc_setup(struct rcar_du_crtc *rcrtc)
+{
+       /* Set display off and background to black */
+       rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
+       rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
+
+       /* Configure display timings and output routing */
+       rcar_du_crtc_set_display_timing(rcrtc);
+       rcar_du_group_set_routing(rcrtc->group);
+
+       /* Start with all planes disabled. */
+       rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
+
+       /* Enable the VSP compositor. */
+       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_enable(rcrtc);
+
+       /* Turn vertical blanking interrupt reporting on. */
+       drm_crtc_vblank_on(&rcrtc->crtc);
+}
+
+static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
+{
+       int ret;
+
+       /*
+        * Guard against double-get, as the function is called from both the
+        * .atomic_enable() and .atomic_begin() handlers.
+        */
+       if (rcrtc->initialized)
+               return 0;
+
+       ret = clk_prepare_enable(rcrtc->clock);
+       if (ret < 0)
+               return ret;
+
+       ret = clk_prepare_enable(rcrtc->extclock);
+       if (ret < 0)
+               goto error_clock;
+
+       ret = rcar_du_group_get(rcrtc->group);
+       if (ret < 0)
+               goto error_group;
+
+       rcar_du_crtc_setup(rcrtc);
+       rcrtc->initialized = true;
+
+       return 0;
+
+error_group:
+       clk_disable_unprepare(rcrtc->extclock);
+error_clock:
+       clk_disable_unprepare(rcrtc->clock);
+       return ret;
+}
+
+static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
+{
+       rcar_du_group_put(rcrtc->group);
+
+       clk_disable_unprepare(rcrtc->extclock);
+       clk_disable_unprepare(rcrtc->clock);
+
+       rcrtc->initialized = false;
+}
+
+static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
+{
+       bool interlaced;
+
+       /*
+        * Select master sync mode. This enables display operation in master
+        * sync mode (with the HSYNC and VSYNC signals configured as outputs and
+        * actively driven).
+        */
+       interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE;
+       rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK | DSYSR_SCM_MASK,
+                                  (interlaced ? DSYSR_SCM_INT_VIDEO : 0) |
+                                  DSYSR_TVM_MASTER);
+
+       rcar_du_group_start_stop(rcrtc->group, true);
+}
+
+static void rcar_du_crtc_disable_planes(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       u32 status;
+
+       /* Make sure vblank interrupts are enabled. */
+       drm_crtc_vblank_get(crtc);
+
+       /*
+        * Disable planes and calculate how many vertical blanking interrupts we
+        * have to wait for. If a vertical blanking interrupt has been triggered
+        * but not processed yet, we don't know whether it occurred before or
+        * after the planes got disabled. We thus have to wait for two vblank
+        * interrupts in that case.
+        */
+       spin_lock_irq(&rcrtc->vblank_lock);
+       rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
+       status = rcar_du_crtc_read(rcrtc, DSSR);
+       rcrtc->vblank_count = status & DSSR_VBK ? 2 : 1;
+       spin_unlock_irq(&rcrtc->vblank_lock);
+
+       if (!wait_event_timeout(rcrtc->vblank_wait, rcrtc->vblank_count == 0,
+                               msecs_to_jiffies(100)))
+               dev_warn(rcdu->dev, "vertical blanking timeout\n");
+
+       drm_crtc_vblank_put(crtc);
+}
+
+static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+
+       /*
+        * Disable all planes and wait for the change to take effect. This is
+        * required as the plane enable registers are updated on vblank, and no
+        * vblank will occur once the CRTC is stopped. Disabling planes when
+        * starting the CRTC thus wouldn't be enough as it would start scanning
+        * out immediately from old frame buffers until the next vblank.
+        *
+        * This increases the CRTC stop delay, especially when multiple CRTCs
+        * are stopped in one operation as we now wait for one vblank per CRTC.
+        * Whether this can be improved needs to be researched.
+        */
+       rcar_du_crtc_disable_planes(rcrtc);
+
+       /*
+        * Disable vertical blanking interrupt reporting. We first need to wait
+        * for page flip completion before stopping the CRTC as userspace
+        * expects page flips to eventually complete.
+        */
+       rcar_du_crtc_wait_page_flip(rcrtc);
+       drm_crtc_vblank_off(crtc);
+
+       /* Disable the VSP compositor. */
+       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_disable(rcrtc);
+
+       if (rcrtc->cmm)
+               rcar_cmm_disable(rcrtc->cmm);
+
+       /*
+        * Select switch sync mode. This stops display operation and configures
+        * the HSYNC and VSYNC signals as inputs.
+        *
+        * TODO: Find another way to stop the display for DUs that don't support
+        * TVM sync.
+        */
+       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_TVM_SYNC))
+               rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK,
+                                          DSYSR_TVM_SWITCH);
+
+       rcar_du_group_start_stop(rcrtc->group, false);
+}
+
+/* -----------------------------------------------------------------------------
+ * CRTC Functions
+ */
+
+static int rcar_du_crtc_atomic_check(struct drm_crtc *crtc,
+                                    struct drm_atomic_state *state)
+{
+       struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+                                                                         crtc);
+       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc_state);
+       struct drm_encoder *encoder;
+       int ret;
+
+       ret = rcar_du_cmm_check(crtc, crtc_state);
+       if (ret)
+               return ret;
+
+       /* Store the routes from the CRTC output to the DU outputs. */
+       rstate->outputs = 0;
+
+       drm_for_each_encoder_mask(encoder, crtc->dev,
+                                 crtc_state->encoder_mask) {
+               struct rcar_du_encoder *renc;
+
+               /* Skip the writeback encoder. */
+               if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
+                       continue;
+
+               renc = to_rcar_encoder(encoder);
+               rstate->outputs |= BIT(renc->output);
+       }
+
+       return 0;
+}
+
+static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
+                                      struct drm_atomic_state *state)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state);
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       if (rcrtc->cmm)
+               rcar_cmm_enable(rcrtc->cmm);
+       rcar_du_crtc_get(rcrtc);
+
+       /*
+        * On D3/E3 the dot clock is provided by the LVDS encoder attached to
+        * the DU channel. We need to enable its clock output explicitly before
+        * starting the CRTC, as the bridge hasn't been enabled by the atomic
+        * helpers yet.
+        */
+       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
+               bool dot_clk_only = rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0);
+               struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
+               const struct drm_display_mode *mode =
+                       &crtc->state->adjusted_mode;
+
+               rcar_lvds_pclk_enable(bridge, mode->clock * 1000, dot_clk_only);
+       }
+
+       /*
+        * Similarly to LVDS, on V3U the dot clock is provided by the DSI
+        * encoder, and we need to enable the DSI clocks before enabling the CRTC.
+        */
+       if ((rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) &&
+           (rstate->outputs &
+            (BIT(RCAR_DU_OUTPUT_DSI0) | BIT(RCAR_DU_OUTPUT_DSI1)))) {
+               struct drm_bridge *bridge = rcdu->dsi[rcrtc->index];
+
+               rcar_mipi_dsi_pclk_enable(bridge, state);
+       }
+
+       rcar_du_crtc_start(rcrtc);
+
+       /*
+        * TODO: The chip manual indicates that CMM tables should be written
+        * after the DU channel has been activated. Investigate the impact
+        * of this restriction on the first displayed frame.
+        */
+       rcar_du_cmm_setup(crtc);
+}
+
+static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
+                                       struct drm_atomic_state *state)
+{
+       struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+                                                                        crtc);
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(old_state);
+       struct rcar_du_device *rcdu = rcrtc->dev;
+
+       rcar_du_crtc_stop(rcrtc);
+       rcar_du_crtc_put(rcrtc);
+
+       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
+               bool dot_clk_only = rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0);
+               struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
+
+               /*
+                * Disable the LVDS clock output, see
+                * rcar_du_crtc_atomic_enable(). When the LVDS output is used,
+                * this also disables the LVDS encoder.
+                */
+               rcar_lvds_pclk_disable(bridge, dot_clk_only);
+       }
+
+       if ((rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) &&
+           (rstate->outputs &
+            (BIT(RCAR_DU_OUTPUT_DSI0) | BIT(RCAR_DU_OUTPUT_DSI1)))) {
+               struct drm_bridge *bridge = rcdu->dsi[rcrtc->index];
+
+               /*
+                * Disable the DSI clock output, see
+                * rcar_du_crtc_atomic_enable().
+                */
+               rcar_mipi_dsi_pclk_disable(bridge);
+       }
+
+       spin_lock_irq(&crtc->dev->event_lock);
+       if (crtc->state->event) {
+               drm_crtc_send_vblank_event(crtc, crtc->state->event);
+               crtc->state->event = NULL;
+       }
+       spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc,
+                                     struct drm_atomic_state *state)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       WARN_ON(!crtc->state->enable);
+
+       /*
+        * If a mode set is in progress we can be called with the CRTC disabled.
+        * We thus need to first get and setup the CRTC in order to configure
+        * planes. We must *not* put the CRTC in .atomic_flush(), as it must be
+        * kept awake until the .atomic_enable() call that will follow. The get
+        * operation in .atomic_enable() will in that case be a no-op, and the
+        * CRTC will be put later in .atomic_disable().
+        *
+        * If a mode set is not in progress the CRTC is enabled, and the
+        * following get call will be a no-op. There is thus no need to balance
+        * it in .atomic_flush() either.
+        */
+       rcar_du_crtc_get(rcrtc);
+
+       /* If the active state changed, we let .atomic_enable handle CMM. */
+       if (crtc->state->color_mgmt_changed && !crtc->state->active_changed)
+               rcar_du_cmm_setup(crtc);
+
+       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_atomic_begin(rcrtc);
+}
+
+static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc,
+                                     struct drm_atomic_state *state)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       rcar_du_crtc_update_planes(rcrtc);
+
+       if (crtc->state->event) {
+               WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+               spin_lock_irqsave(&dev->event_lock, flags);
+               rcrtc->event = crtc->state->event;
+               crtc->state->event = NULL;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+
+       if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
+               rcar_du_vsp_atomic_flush(rcrtc);
+}
+
+static enum drm_mode_status
+rcar_du_crtc_mode_valid(struct drm_crtc *crtc,
+                       const struct drm_display_mode *mode)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_device *rcdu = rcrtc->dev;
+       bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+       unsigned int min_sync_porch;
+       unsigned int vbp;
+
+       if (interlaced && !rcar_du_has(rcdu, RCAR_DU_FEATURE_INTERLACED))
+               return MODE_NO_INTERLACE;
+
+       /*
+        * The hardware requires a minimum combined horizontal sync and back
+        * porch of 20 pixels (when CMM isn't used) or 45 pixels (when CMM is
+        * used), and a minimum vertical back porch of 3 lines.
+        */
+       min_sync_porch = 20;
+       if (rcrtc->group->cmms_mask & BIT(rcrtc->index % 2))
+               min_sync_porch += 25;
+
+       if (mode->htotal - mode->hsync_start < min_sync_porch)
+               return MODE_HBLANK_NARROW;
+
+       vbp = (mode->vtotal - mode->vsync_end) / (interlaced ? 2 : 1);
+       if (vbp < 3)
+               return MODE_VBLANK_NARROW;
+
+       return MODE_OK;
+}
+
+static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+       .atomic_check = rcar_du_crtc_atomic_check,
+       .atomic_begin = rcar_du_crtc_atomic_begin,
+       .atomic_flush = rcar_du_crtc_atomic_flush,
+       .atomic_enable = rcar_du_crtc_atomic_enable,
+       .atomic_disable = rcar_du_crtc_atomic_disable,
+       .mode_valid = rcar_du_crtc_mode_valid,
+};
+
+static void rcar_du_crtc_crc_init(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->dev;
+       const char **sources;
+       unsigned int count;
+       int i = -1;
+
+       /* CRC available only on Gen3 HW. */
+       if (rcdu->info->gen < 3)
+               return;
+
+       /* Reserve 1 for "auto" source. */
+       count = rcrtc->vsp->num_planes + 1;
+
+       sources = kmalloc_array(count, sizeof(*sources), GFP_KERNEL);
+       if (!sources)
+               return;
+
+       sources[0] = kstrdup("auto", GFP_KERNEL);
+       if (!sources[0])
+               goto error;
+
+       for (i = 0; i < rcrtc->vsp->num_planes; ++i) {
+               struct drm_plane *plane = &rcrtc->vsp->planes[i].plane;
+               char name[16];
+
+               sprintf(name, "plane%u", plane->base.id);
+               sources[i + 1] = kstrdup(name, GFP_KERNEL);
+               if (!sources[i + 1])
+                       goto error;
+       }
+
+       rcrtc->sources = sources;
+       rcrtc->sources_count = count;
+       return;
+
+error:
+       while (i >= 0) {
+               kfree(sources[i]);
+               i--;
+       }
+       kfree(sources);
+}
+
+static void rcar_du_crtc_crc_cleanup(struct rcar_du_crtc *rcrtc)
+{
+       unsigned int i;
+
+       if (!rcrtc->sources)
+               return;
+
+       for (i = 0; i < rcrtc->sources_count; i++)
+               kfree(rcrtc->sources[i]);
+       kfree(rcrtc->sources);
+
+       rcrtc->sources = NULL;
+       rcrtc->sources_count = 0;
+}
+
+static struct drm_crtc_state *
+rcar_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc_state *state;
+       struct rcar_du_crtc_state *copy;
+
+       if (WARN_ON(!crtc->state))
+               return NULL;
+
+       state = to_rcar_crtc_state(crtc->state);
+       copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+       if (copy == NULL)
+               return NULL;
+
+       __drm_atomic_helper_crtc_duplicate_state(crtc, &copy->state);
+
+       return &copy->state;
+}
+
+static void rcar_du_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+                                             struct drm_crtc_state *state)
+{
+       __drm_atomic_helper_crtc_destroy_state(state);
+       kfree(to_rcar_crtc_state(state));
+}
+
+static void rcar_du_crtc_cleanup(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       rcar_du_crtc_crc_cleanup(rcrtc);
+
+       return drm_crtc_cleanup(crtc);
+}
+
+static void rcar_du_crtc_reset(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc_state *state;
+
+       if (crtc->state) {
+               rcar_du_crtc_atomic_destroy_state(crtc, crtc->state);
+               crtc->state = NULL;
+       }
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return;
+
+       state->crc.source = VSP1_DU_CRC_NONE;
+       state->crc.index = 0;
+
+       __drm_atomic_helper_crtc_reset(crtc, &state->state);
+}
+
+static int rcar_du_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
+       rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
+       rcrtc->vblank_enable = true;
+
+       return 0;
+}
+
+static void rcar_du_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
+       rcrtc->vblank_enable = false;
+}
+
+static int rcar_du_crtc_parse_crc_source(struct rcar_du_crtc *rcrtc,
+                                        const char *source_name,
+                                        enum vsp1_du_crc_source *source)
+{
+       unsigned int index;
+       int ret;
+
+       /*
+        * Parse the source name. Supported values are "plane%u" to compute the
+        * CRC on an input plane (%u is the plane ID), and "auto" to compute the
+        * CRC on the composer (VSP) output.
+        */
+
+       if (!source_name) {
+               *source = VSP1_DU_CRC_NONE;
+               return 0;
+       } else if (!strcmp(source_name, "auto")) {
+               *source = VSP1_DU_CRC_OUTPUT;
+               return 0;
+       } else if (strstarts(source_name, "plane")) {
+               unsigned int i;
+
+               *source = VSP1_DU_CRC_PLANE;
+
+               ret = kstrtouint(source_name + strlen("plane"), 10, &index);
+               if (ret < 0)
+                       return ret;
+
+               for (i = 0; i < rcrtc->vsp->num_planes; ++i) {
+                       if (index == rcrtc->vsp->planes[i].plane.base.id)
+                               return i;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int rcar_du_crtc_verify_crc_source(struct drm_crtc *crtc,
+                                         const char *source_name,
+                                         size_t *values_cnt)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       enum vsp1_du_crc_source source;
+
+       if (rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source) < 0) {
+               DRM_DEBUG_DRIVER("unknown source %s\n", source_name);
+               return -EINVAL;
+       }
+
+       *values_cnt = 1;
+       return 0;
+}
+
+static const char *const *
+rcar_du_crtc_get_crc_sources(struct drm_crtc *crtc, size_t *count)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       *count = rcrtc->sources_count;
+       return rcrtc->sources;
+}
+
+static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc,
+                                      const char *source_name)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_crtc_state *crtc_state;
+       struct drm_atomic_state *state;
+       enum vsp1_du_crc_source source;
+       unsigned int index;
+       int ret;
+
+       ret = rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source);
+       if (ret < 0)
+               return ret;
+
+       index = ret;
+
+       /* Perform an atomic commit to set the CRC source. */
+       drm_modeset_acquire_init(&ctx, 0);
+
+       state = drm_atomic_state_alloc(crtc->dev);
+       if (!state) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
+
+       state->acquire_ctx = &ctx;
+
+retry:
+       crtc_state = drm_atomic_get_crtc_state(state, crtc);
+       if (!IS_ERR(crtc_state)) {
+               struct rcar_du_crtc_state *rcrtc_state;
+
+               rcrtc_state = to_rcar_crtc_state(crtc_state);
+               rcrtc_state->crc.source = source;
+               rcrtc_state->crc.index = index;
+
+               ret = drm_atomic_commit(state);
+       } else {
+               ret = PTR_ERR(crtc_state);
+       }
+
+       if (ret == -EDEADLK) {
+               drm_atomic_state_clear(state);
+               drm_modeset_backoff(&ctx);
+               goto retry;
+       }
+
+       drm_atomic_state_put(state);
+
+unlock:
+       drm_modeset_drop_locks(&ctx);
+       drm_modeset_acquire_fini(&ctx);
+
+       return ret;
+}
+
+static const struct drm_crtc_funcs crtc_funcs_gen2 = {
+       .reset = rcar_du_crtc_reset,
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_atomic_helper_set_config,
+       .page_flip = drm_atomic_helper_page_flip,
+       .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state,
+       .atomic_destroy_state = rcar_du_crtc_atomic_destroy_state,
+       .enable_vblank = rcar_du_crtc_enable_vblank,
+       .disable_vblank = rcar_du_crtc_disable_vblank,
+};
+
+static const struct drm_crtc_funcs crtc_funcs_gen3 = {
+       .reset = rcar_du_crtc_reset,
+       .destroy = rcar_du_crtc_cleanup,
+       .set_config = drm_atomic_helper_set_config,
+       .page_flip = drm_atomic_helper_page_flip,
+       .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state,
+       .atomic_destroy_state = rcar_du_crtc_atomic_destroy_state,
+       .enable_vblank = rcar_du_crtc_enable_vblank,
+       .disable_vblank = rcar_du_crtc_disable_vblank,
+       .set_crc_source = rcar_du_crtc_set_crc_source,
+       .verify_crc_source = rcar_du_crtc_verify_crc_source,
+       .get_crc_sources = rcar_du_crtc_get_crc_sources,
+};
+
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
+
+static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
+{
+       struct rcar_du_crtc *rcrtc = arg;
+       struct rcar_du_device *rcdu = rcrtc->dev;
+       irqreturn_t ret = IRQ_NONE;
+       u32 status;
+
+       spin_lock(&rcrtc->vblank_lock);
+
+       status = rcar_du_crtc_read(rcrtc, DSSR);
+       rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
+
+       if (status & DSSR_VBK) {
+               /*
+                * Wake up the vblank wait if the counter reaches 0. This must
+                * be protected by the vblank_lock to avoid races in
+                * rcar_du_crtc_disable_planes().
+                */
+               if (rcrtc->vblank_count) {
+                       if (--rcrtc->vblank_count == 0)
+                               wake_up(&rcrtc->vblank_wait);
+               }
+       }
+
+       spin_unlock(&rcrtc->vblank_lock);
+
+       if (status & DSSR_VBK) {
+               if (rcdu->info->gen < 3) {
+                       drm_crtc_handle_vblank(&rcrtc->crtc);
+                       rcar_du_crtc_finish_page_flip(rcrtc);
+               }
+
+               ret = IRQ_HANDLED;
+       }
+
+       return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
+                       unsigned int hwindex)
+{
+       static const unsigned int mmio_offsets[] = {
+               DU0_REG_OFFSET, DU1_REG_OFFSET, DU2_REG_OFFSET, DU3_REG_OFFSET
+       };
+
+       struct rcar_du_device *rcdu = rgrp->dev;
+       struct platform_device *pdev = to_platform_device(rcdu->dev);
+       struct rcar_du_crtc *rcrtc = &rcdu->crtcs[swindex];
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       struct drm_plane *primary;
+       unsigned int irqflags;
+       struct clk *clk;
+       char clk_name[9];
+       char *name;
+       int irq;
+       int ret;
+
+       /* Get the CRTC clock and the optional external clock. */
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_CLOCK)) {
+               sprintf(clk_name, "du.%u", hwindex);
+               name = clk_name;
+       } else {
+               name = NULL;
+       }
+
+       rcrtc->clock = devm_clk_get(rcdu->dev, name);
+       if (IS_ERR(rcrtc->clock)) {
+               dev_err(rcdu->dev, "no clock for DU channel %u\n", hwindex);
+               return PTR_ERR(rcrtc->clock);
+       }
+
+       sprintf(clk_name, "dclkin.%u", hwindex);
+       clk = devm_clk_get(rcdu->dev, clk_name);
+       if (!IS_ERR(clk)) {
+               rcrtc->extclock = clk;
+       } else if (PTR_ERR(clk) == -EPROBE_DEFER) {
+               return -EPROBE_DEFER;
+       } else if (rcdu->info->dpll_mask & BIT(hwindex)) {
+               /*
+                * DU channels that have a display PLL can't use the internal
+                * system clock and thus require an external clock.
+                */
+               ret = PTR_ERR(clk);
+               dev_err(rcdu->dev, "can't get dclkin.%u: %d\n", hwindex, ret);
+               return ret;
+       }
+
+       init_waitqueue_head(&rcrtc->flip_wait);
+       init_waitqueue_head(&rcrtc->vblank_wait);
+       spin_lock_init(&rcrtc->vblank_lock);
+
+       rcrtc->dev = rcdu;
+       rcrtc->group = rgrp;
+       rcrtc->mmio_offset = mmio_offsets[hwindex];
+       rcrtc->index = hwindex;
+       rcrtc->dsysr = rcrtc->index % 2 ? 0 : DSYSR_DRES;
+
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_TVM_SYNC))
+               rcrtc->dsysr |= DSYSR_TVM_TVSYNC;
+
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
+               primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane;
+       else
+               primary = &rgrp->planes[swindex % 2].plane;
+
+       ret = drm_crtc_init_with_planes(&rcdu->ddev, crtc, primary, NULL,
+                                       rcdu->info->gen <= 2 ?
+                                       &crtc_funcs_gen2 : &crtc_funcs_gen3,
+                                       NULL);
+       if (ret < 0)
+               return ret;
+
+       /* CMM might be disabled for this CRTC. */
+       if (rcdu->cmms[swindex]) {
+               rcrtc->cmm = rcdu->cmms[swindex];
+               rgrp->cmms_mask |= BIT(hwindex % 2);
+
+               drm_mode_crtc_set_gamma_size(crtc, CM2_LUT_SIZE);
+               drm_crtc_enable_color_mgmt(crtc, 0, false, CM2_LUT_SIZE);
+       }
+
+       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+       /* Register the interrupt handler. */
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ)) {
+               /* The IRQ's are associated with the CRTC (sw)index. */
+               irq = platform_get_irq(pdev, swindex);
+               irqflags = 0;
+       } else {
+               irq = platform_get_irq(pdev, 0);
+               irqflags = IRQF_SHARED;
+       }
+
+       if (irq < 0) {
+               dev_err(rcdu->dev, "no IRQ for CRTC %u\n", swindex);
+               return irq;
+       }
+
+       ret = devm_request_irq(rcdu->dev, irq, rcar_du_crtc_irq, irqflags,
+                              dev_name(rcdu->dev), rcrtc);
+       if (ret < 0) {
+               dev_err(rcdu->dev,
+                       "failed to register IRQ for CRTC %u\n", swindex);
+               return ret;
+       }
+
+       rcar_du_crtc_crc_init(rcrtc);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h
new file mode 100644 (file)
index 0000000..d0f38a8
--- /dev/null
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_CRTC_H__
+#define __RCAR_DU_CRTC_H__
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_writeback.h>
+
+#include <media/vsp1.h>
+
+struct rcar_du_group;
+struct rcar_du_vsp;
+
+/**
+ * struct rcar_du_crtc - the CRTC, representing a DU superposition processor
+ * @crtc: base DRM CRTC
+ * @dev: the DU device
+ * @clock: the CRTC functional clock
+ * @extclock: external pixel dot clock (optional)
+ * @mmio_offset: offset of the CRTC registers in the DU MMIO block
+ * @index: CRTC hardware index
+ * @initialized: whether the CRTC has been initialized and clocks enabled
+ * @dsysr: cached value of the DSYSR register
+ * @vblank_enable: whether vblank events are enabled on this CRTC
+ * @event: event to post when the pending page flip completes
+ * @flip_wait: wait queue used to signal page flip completion
+ * @vblank_lock: protects vblank_wait and vblank_count
+ * @vblank_wait: wait queue used to signal vertical blanking
+ * @vblank_count: number of vertical blanking interrupts to wait for
+ * @group: CRTC group this CRTC belongs to
+ * @cmm: CMM associated with this CRTC
+ * @vsp: VSP feeding video to this CRTC
+ * @vsp_pipe: index of the VSP pipeline feeding video to this CRTC
+ * @writeback: the writeback connector
+ */
+struct rcar_du_crtc {
+       struct drm_crtc crtc;
+
+       struct rcar_du_device *dev;
+       struct clk *clock;
+       struct clk *extclock;
+       unsigned int mmio_offset;
+       unsigned int index;
+       bool initialized;
+
+       u32 dsysr;
+
+       bool vblank_enable;
+       struct drm_pending_vblank_event *event;
+       wait_queue_head_t flip_wait;
+
+       spinlock_t vblank_lock;
+       wait_queue_head_t vblank_wait;
+       unsigned int vblank_count;
+
+       struct rcar_du_group *group;
+       struct platform_device *cmm;
+       struct rcar_du_vsp *vsp;
+       unsigned int vsp_pipe;
+
+       const char *const *sources;
+       unsigned int sources_count;
+
+       struct drm_writeback_connector writeback;
+};
+
+#define to_rcar_crtc(c)                container_of(c, struct rcar_du_crtc, crtc)
+#define wb_to_rcar_crtc(c)     container_of(c, struct rcar_du_crtc, writeback)
+
+/**
+ * struct rcar_du_crtc_state - Driver-specific CRTC state
+ * @state: base DRM CRTC state
+ * @crc: CRC computation configuration
+ * @outputs: bitmask of the outputs (enum rcar_du_output) driven by this CRTC
+ */
+struct rcar_du_crtc_state {
+       struct drm_crtc_state state;
+
+       struct vsp1_du_crc_config crc;
+       unsigned int outputs;
+};
+
+#define to_rcar_crtc_state(s) container_of(s, struct rcar_du_crtc_state, state)
+
+int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
+                       unsigned int hwindex);
+
+void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc);
+
+void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set);
+
+#endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
new file mode 100644 (file)
index 0000000..1ffde19
--- /dev/null
@@ -0,0 +1,744 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_probe_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+
+/* -----------------------------------------------------------------------------
+ * Device Information
+ */
+
+static const struct rcar_du_device_info rzg1_du_r8a7743_info = {
+       .gen = 2,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A774[34] has one RGB output and one LVDS output
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(1) | BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 1,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 4,
+};
+
+static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
+       .gen = 2,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A7745 has two RGB outputs
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_DPAD1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+       },
+       .num_rpf = 4,
+};
+
+static const struct rcar_du_device_info rzg1_du_r8a77470_info = {
+       .gen = 2,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A77470 has two RGB outputs, one LVDS output, and
+                * one (currently unsupported) analog video output
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_DPAD1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0) | BIT(1),
+                       .port = 2,
+               },
+       },
+       .num_rpf = 4,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a774a1_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(2) | BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A774A1 has one RGB output, one LVDS output and one HDMI
+                * output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(2),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_HDMI0] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 5,
+       .dpll_mask =  BIT(1),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a774b1_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(3) | BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A774B1 has one RGB output, one LVDS output and one HDMI
+                * output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(2),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_HDMI0] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 5,
+       .dpll_mask =  BIT(1),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a774c0_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A774C0 has one RGB output and two LVDS outputs
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0) | BIT(1),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 2,
+       .num_rpf = 4,
+       .lvds_clk_mask =  BIT(1) | BIT(0),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a774e1_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(3) | BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A774E1 has one RGB output, one LVDS output and one HDMI
+                * output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(2),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_HDMI0] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 5,
+       .dpll_mask =  BIT(1),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7779_info = {
+       .gen = 1,
+       .features = RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A7779 has two RGB outputs and one (currently unsupported)
+                * TCON output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_DPAD1] = {
+                       .possible_crtcs = BIT(1) | BIT(0),
+                       .port = 1,
+               },
+       },
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7790_info = {
+       .gen = 2,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .quirks = RCAR_DU_QUIRK_ALIGN_128B,
+       .channels_mask = BIT(2) | BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A7742 and R8A7790 each have one RGB output and two LVDS
+                * outputs. Additionally R8A7790 supports one TCON output
+                * (currently unsupported by the driver).
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(2) | BIT(1) | BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS1] = {
+                       .possible_crtcs = BIT(2) | BIT(1),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 2,
+       .num_rpf = 4,
+};
+
+/* M2-W (r8a7791) and M2-N (r8a7793) are identical */
+static const struct rcar_du_device_info rcar_du_r8a7791_info = {
+       .gen = 2,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A779[13] has one RGB output, one LVDS output and one
+                * (currently unsupported) TCON output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(1) | BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 1,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 4,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7792_info = {
+       .gen = 2,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /* R8A7792 has two RGB outputs. */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_DPAD1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+       },
+       .num_rpf = 4,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7794_info = {
+       .gen = 2,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A7794 has two RGB outputs and one (currently unsupported)
+                * TCON output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_DPAD1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+       },
+       .num_rpf = 4,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7795_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A7795 has one RGB output, two HDMI outputs and one
+                * LVDS output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(3),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_HDMI0] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_HDMI1] = {
+                       .possible_crtcs = BIT(2),
+                       .port = 2,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 3,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 5,
+       .dpll_mask =  BIT(2) | BIT(1),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7796_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(2) | BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A7796 has one RGB output, one LVDS output and one HDMI
+                * output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(2),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_HDMI0] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 5,
+       .dpll_mask =  BIT(1),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a77965_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(3) | BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A77965 has one RGB output, one LVDS output and one HDMI
+                * output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(2),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_HDMI0] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 5,
+       .dpll_mask =  BIT(1),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a77970_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_INTERLACED
+                 | RCAR_DU_FEATURE_TVM_SYNC,
+       .channels_mask = BIT(0),
+       .routes = {
+               /*
+                * R8A77970 and R8A77980 have one RGB output and one LVDS
+                * output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 1,
+               },
+       },
+       .num_lvds = 1,
+       .num_rpf = 5,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7799x_info = {
+       .gen = 3,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_CRTC_CLOCK
+                 | RCAR_DU_FEATURE_VSP1_SOURCE,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /*
+                * R8A77990 and R8A77995 have one RGB output and two LVDS
+                * outputs.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0) | BIT(1),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 1,
+               },
+               [RCAR_DU_OUTPUT_LVDS1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 2,
+               },
+       },
+       .num_lvds = 2,
+       .num_rpf = 5,
+       .lvds_clk_mask =  BIT(1) | BIT(0),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a779a0_info = {
+       .gen = 4,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_NO_BLENDING,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /* R8A779A0 has two MIPI DSI outputs. */
+               [RCAR_DU_OUTPUT_DSI0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_DSI1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+       },
+       .num_rpf = 5,
+       .dsi_clk_mask =  BIT(1) | BIT(0),
+};
+
+static const struct rcar_du_device_info rcar_du_r8a779g0_info = {
+       .gen = 4,
+       .features = RCAR_DU_FEATURE_CRTC_IRQ
+                 | RCAR_DU_FEATURE_VSP1_SOURCE
+                 | RCAR_DU_FEATURE_NO_BLENDING,
+       .channels_mask = BIT(1) | BIT(0),
+       .routes = {
+               /* R8A779G0 has two MIPI DSI outputs. */
+               [RCAR_DU_OUTPUT_DSI0] = {
+                       .possible_crtcs = BIT(0),
+                       .port = 0,
+               },
+               [RCAR_DU_OUTPUT_DSI1] = {
+                       .possible_crtcs = BIT(1),
+                       .port = 1,
+               },
+       },
+       .num_rpf = 5,
+       .dsi_clk_mask =  BIT(1) | BIT(0),
+};
+
+static const struct of_device_id rcar_du_of_table[] = {
+       { .compatible = "renesas,du-r8a7742", .data = &rcar_du_r8a7790_info },
+       { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },
+       { .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info },
+       { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
+       { .compatible = "renesas,du-r8a77470", .data = &rzg1_du_r8a77470_info },
+       { .compatible = "renesas,du-r8a774a1", .data = &rcar_du_r8a774a1_info },
+       { .compatible = "renesas,du-r8a774b1", .data = &rcar_du_r8a774b1_info },
+       { .compatible = "renesas,du-r8a774c0", .data = &rcar_du_r8a774c0_info },
+       { .compatible = "renesas,du-r8a774e1", .data = &rcar_du_r8a774e1_info },
+       { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
+       { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
+       { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
+       { .compatible = "renesas,du-r8a7792", .data = &rcar_du_r8a7792_info },
+       { .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info },
+       { .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
+       { .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
+       { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
+       { .compatible = "renesas,du-r8a77961", .data = &rcar_du_r8a7796_info },
+       { .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info },
+       { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info },
+       { .compatible = "renesas,du-r8a77980", .data = &rcar_du_r8a77970_info },
+       { .compatible = "renesas,du-r8a77990", .data = &rcar_du_r8a7799x_info },
+       { .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info },
+       { .compatible = "renesas,du-r8a779a0", .data = &rcar_du_r8a779a0_info },
+       { .compatible = "renesas,du-r8a779g0", .data = &rcar_du_r8a779g0_info },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, rcar_du_of_table);
+
+const char *rcar_du_output_name(enum rcar_du_output output)
+{
+       static const char * const names[] = {
+               [RCAR_DU_OUTPUT_DPAD0] = "DPAD0",
+               [RCAR_DU_OUTPUT_DPAD1] = "DPAD1",
+               [RCAR_DU_OUTPUT_DSI0] = "DSI0",
+               [RCAR_DU_OUTPUT_DSI1] = "DSI1",
+               [RCAR_DU_OUTPUT_HDMI0] = "HDMI0",
+               [RCAR_DU_OUTPUT_HDMI1] = "HDMI1",
+               [RCAR_DU_OUTPUT_LVDS0] = "LVDS0",
+               [RCAR_DU_OUTPUT_LVDS1] = "LVDS1",
+               [RCAR_DU_OUTPUT_TCON] = "TCON",
+       };
+
+       if (output >= ARRAY_SIZE(names) || !names[output])
+               return "UNKNOWN";
+
+       return names[output];
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM operations
+ */
+
+DEFINE_DRM_GEM_DMA_FOPS(rcar_du_fops);
+
+static const struct drm_driver rcar_du_driver = {
+       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+       .dumb_create            = rcar_du_dumb_create,
+       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
+       .gem_prime_import_sg_table = rcar_du_gem_prime_import_sg_table,
+       .gem_prime_mmap         = drm_gem_prime_mmap,
+       .fops                   = &rcar_du_fops,
+       .name                   = "rcar-du",
+       .desc                   = "Renesas R-Car Display Unit",
+       .date                   = "20130110",
+       .major                  = 1,
+       .minor                  = 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int rcar_du_pm_suspend(struct device *dev)
+{
+       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+       return drm_mode_config_helper_suspend(&rcdu->ddev);
+}
+
+static int rcar_du_pm_resume(struct device *dev)
+{
+       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+       return drm_mode_config_helper_resume(&rcdu->ddev);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(rcar_du_pm_ops,
+                               rcar_du_pm_suspend, rcar_du_pm_resume);
+
+/* -----------------------------------------------------------------------------
+ * Platform driver
+ */
+
+static int rcar_du_remove(struct platform_device *pdev)
+{
+       struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
+       struct drm_device *ddev = &rcdu->ddev;
+
+       drm_dev_unregister(ddev);
+       drm_atomic_helper_shutdown(ddev);
+
+       drm_kms_helper_poll_fini(ddev);
+
+       return 0;
+}
+
+static void rcar_du_shutdown(struct platform_device *pdev)
+{
+       struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
+
+       drm_atomic_helper_shutdown(&rcdu->ddev);
+}
+
+static int rcar_du_probe(struct platform_device *pdev)
+{
+       struct rcar_du_device *rcdu;
+       unsigned int mask;
+       int ret;
+
+       if (drm_firmware_drivers_only())
+               return -ENODEV;
+
+       /* Allocate and initialize the R-Car device structure. */
+       rcdu = devm_drm_dev_alloc(&pdev->dev, &rcar_du_driver,
+                                 struct rcar_du_device, ddev);
+       if (IS_ERR(rcdu))
+               return PTR_ERR(rcdu);
+
+       rcdu->dev = &pdev->dev;
+
+       rcdu->info = of_device_get_match_data(rcdu->dev);
+
+       platform_set_drvdata(pdev, rcdu);
+
+       /* I/O resources */
+       rcdu->mmio = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(rcdu->mmio))
+               return PTR_ERR(rcdu->mmio);
+
+       /*
+        * Set the DMA coherent mask to reflect the DU 32-bit DMA address space
+        * limitations. When sourcing frames from a VSP the DU doesn't perform
+        * any memory access so set the mask to 40 bits to accept all buffers.
+        */
+       mask = rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE) ? 40 : 32;
+       ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(mask));
+       if (ret)
+               return ret;
+
+       /* DRM/KMS objects */
+       ret = rcar_du_modeset_init(rcdu);
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev,
+                               "failed to initialize DRM/KMS (%d)\n", ret);
+               goto error;
+       }
+
+       /*
+        * Register the DRM device with the core and the connectors with
+        * sysfs.
+        */
+       ret = drm_dev_register(&rcdu->ddev, 0);
+       if (ret)
+               goto error;
+
+       DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
+
+       drm_fbdev_generic_setup(&rcdu->ddev, 32);
+
+       return 0;
+
+error:
+       drm_kms_helper_poll_fini(&rcdu->ddev);
+       return ret;
+}
+
+static struct platform_driver rcar_du_platform_driver = {
+       .probe          = rcar_du_probe,
+       .remove         = rcar_du_remove,
+       .shutdown       = rcar_du_shutdown,
+       .driver         = {
+               .name   = "rcar-du",
+               .pm     = pm_sleep_ptr(&rcar_du_pm_ops),
+               .of_match_table = rcar_du_of_table,
+       },
+};
+
+module_platform_driver(rcar_du_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h
new file mode 100644 (file)
index 0000000..5cfa2bb
--- /dev/null
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_DRV_H__
+#define __RCAR_DU_DRV_H__
+
+#include <linux/kernel.h>
+#include <linux/wait.h>
+
+#include <drm/drm_device.h>
+
+#include "rcar_cmm.h"
+#include "rcar_du_crtc.h"
+#include "rcar_du_group.h"
+#include "rcar_du_vsp.h"
+
+struct clk;
+struct device;
+struct drm_bridge;
+struct drm_property;
+struct rcar_du_device;
+
+#define RCAR_DU_FEATURE_CRTC_IRQ       BIT(0)  /* Per-CRTC IRQ */
+#define RCAR_DU_FEATURE_CRTC_CLOCK     BIT(1)  /* Per-CRTC clock */
+#define RCAR_DU_FEATURE_VSP1_SOURCE    BIT(2)  /* Has inputs from VSP1 */
+#define RCAR_DU_FEATURE_INTERLACED     BIT(3)  /* HW supports interlaced */
+#define RCAR_DU_FEATURE_TVM_SYNC       BIT(4)  /* Has TV switch/sync modes */
+#define RCAR_DU_FEATURE_NO_BLENDING    BIT(5)  /* PnMR.SPIM does not have ALP nor EOR bits */
+
+#define RCAR_DU_QUIRK_ALIGN_128B       BIT(0)  /* Align pitches to 128 bytes */
+
+enum rcar_du_output {
+       RCAR_DU_OUTPUT_DPAD0,
+       RCAR_DU_OUTPUT_DPAD1,
+       RCAR_DU_OUTPUT_DSI0,
+       RCAR_DU_OUTPUT_DSI1,
+       RCAR_DU_OUTPUT_HDMI0,
+       RCAR_DU_OUTPUT_HDMI1,
+       RCAR_DU_OUTPUT_LVDS0,
+       RCAR_DU_OUTPUT_LVDS1,
+       RCAR_DU_OUTPUT_TCON,
+       RCAR_DU_OUTPUT_MAX,
+};
+
+/*
+ * struct rcar_du_output_routing - Output routing specification
+ * @possible_crtcs: bitmask of possible CRTCs for the output
+ * @port: device tree port number corresponding to this output route
+ *
+ * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
+ * specify the valid SoC outputs, which CRTCs can drive the output, and the type
+ * of in-SoC encoder for the output.
+ */
+struct rcar_du_output_routing {
+       unsigned int possible_crtcs;
+       unsigned int port;
+};
+
+/*
+ * struct rcar_du_device_info - DU model-specific information
+ * @gen: device generation (2 or 3)
+ * @features: device features (RCAR_DU_FEATURE_*)
+ * @quirks: device quirks (RCAR_DU_QUIRK_*)
+ * @channels_mask: bit mask of available DU channels
+ * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
+ * @num_lvds: number of internal LVDS encoders
+ * @num_rpf: number of RPFs in VSP
+ * @dpll_mask: bit mask of DU channels equipped with a DPLL
+ * @dsi_clk_mask: bitmask of channels that can use the DSI clock as dot clock
+ * @lvds_clk_mask: bitmask of channels that can use the LVDS clock as dot clock
+ */
+struct rcar_du_device_info {
+       unsigned int gen;
+       unsigned int features;
+       unsigned int quirks;
+       unsigned int channels_mask;
+       struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
+       unsigned int num_lvds;
+       unsigned int num_rpf;
+       unsigned int dpll_mask;
+       unsigned int dsi_clk_mask;
+       unsigned int lvds_clk_mask;
+};
+
+#define RCAR_DU_MAX_CRTCS              4
+#define RCAR_DU_MAX_GROUPS             DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
+#define RCAR_DU_MAX_VSPS               4
+#define RCAR_DU_MAX_LVDS               2
+#define RCAR_DU_MAX_DSI                        2
+
+struct rcar_du_device {
+       struct device *dev;
+       const struct rcar_du_device_info *info;
+
+       void __iomem *mmio;
+
+       struct drm_device ddev;
+
+       struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS];
+       unsigned int num_crtcs;
+
+       struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
+       struct platform_device *cmms[RCAR_DU_MAX_CRTCS];
+       struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
+       struct drm_bridge *lvds[RCAR_DU_MAX_LVDS];
+       struct drm_bridge *dsi[RCAR_DU_MAX_DSI];
+
+       struct {
+               struct drm_property *colorkey;
+       } props;
+
+       unsigned int dpad0_source;
+       unsigned int dpad1_source;
+       unsigned int vspd1_sink;
+};
+
+static inline struct rcar_du_device *to_rcar_du_device(struct drm_device *dev)
+{
+       return container_of(dev, struct rcar_du_device, ddev);
+}
+
+static inline bool rcar_du_has(struct rcar_du_device *rcdu,
+                              unsigned int feature)
+{
+       return rcdu->info->features & feature;
+}
+
+static inline bool rcar_du_needs(struct rcar_du_device *rcdu,
+                                unsigned int quirk)
+{
+       return rcdu->info->quirks & quirk;
+}
+
+static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
+{
+       return ioread32(rcdu->mmio + reg);
+}
+
+static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data)
+{
+       iowrite32(data, rcdu->mmio + reg);
+}
+
+const char *rcar_du_output_name(enum rcar_du_output output);
+
+#endif /* __RCAR_DU_DRV_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
new file mode 100644 (file)
index 0000000..7ecec7b
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit Encoder
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/export.h>
+#include <linux/of.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_panel.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
+#include "rcar_lvds.h"
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+static unsigned int rcar_du_encoder_count_ports(struct device_node *node)
+{
+       struct device_node *ports;
+       struct device_node *port;
+       unsigned int num_ports = 0;
+
+       ports = of_get_child_by_name(node, "ports");
+       if (!ports)
+               ports = of_node_get(node);
+
+       for_each_child_of_node(ports, port) {
+               if (of_node_name_eq(port, "port"))
+                       num_ports++;
+       }
+
+       of_node_put(ports);
+
+       return num_ports;
+}
+
+static const struct drm_encoder_funcs rcar_du_encoder_funcs = {
+};
+
+int rcar_du_encoder_init(struct rcar_du_device *rcdu,
+                        enum rcar_du_output output,
+                        struct device_node *enc_node)
+{
+       struct rcar_du_encoder *renc;
+       struct drm_connector *connector;
+       struct drm_bridge *bridge;
+       int ret;
+
+       /*
+        * Locate the DRM bridge from the DT node. For the DPAD outputs, if the
+        * DT node has a single port, assume that it describes a panel and
+        * create a panel bridge.
+        */
+       if ((output == RCAR_DU_OUTPUT_DPAD0 ||
+            output == RCAR_DU_OUTPUT_DPAD1) &&
+           rcar_du_encoder_count_ports(enc_node) == 1) {
+               struct drm_panel *panel = of_drm_find_panel(enc_node);
+
+               if (IS_ERR(panel))
+                       return PTR_ERR(panel);
+
+               bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
+                                                        DRM_MODE_CONNECTOR_DPI);
+               if (IS_ERR(bridge))
+                       return PTR_ERR(bridge);
+       } else {
+               bridge = of_drm_find_bridge(enc_node);
+               if (!bridge)
+                       return -EPROBE_DEFER;
+
+               if (output == RCAR_DU_OUTPUT_LVDS0 ||
+                   output == RCAR_DU_OUTPUT_LVDS1)
+                       rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
+
+               if (output == RCAR_DU_OUTPUT_DSI0 ||
+                   output == RCAR_DU_OUTPUT_DSI1)
+                       rcdu->dsi[output - RCAR_DU_OUTPUT_DSI0] = bridge;
+       }
+
+       /*
+        * Create and initialize the encoder. On Gen3, skip the LVDS1 output if
+        * the LVDS1 encoder is used as a companion for LVDS0 in dual-link
+        * mode, or any LVDS output if it isn't connected. The latter may happen
+        * on D3 or E3 as the LVDS encoders are needed to provide the pixel
+        * clock to the DU, even when the LVDS outputs are not used.
+        */
+       if (rcdu->info->gen >= 3) {
+               if (output == RCAR_DU_OUTPUT_LVDS1 &&
+                   rcar_lvds_dual_link(bridge))
+                       return -ENOLINK;
+
+               if ((output == RCAR_DU_OUTPUT_LVDS0 ||
+                    output == RCAR_DU_OUTPUT_LVDS1) &&
+                   !rcar_lvds_is_connected(bridge))
+                       return -ENOLINK;
+       }
+
+       dev_dbg(rcdu->dev, "initializing encoder %pOF for output %s\n",
+               enc_node, rcar_du_output_name(output));
+
+       renc = drmm_encoder_alloc(&rcdu->ddev, struct rcar_du_encoder, base,
+                                 &rcar_du_encoder_funcs, DRM_MODE_ENCODER_NONE,
+                                 NULL);
+       if (IS_ERR(renc))
+               return PTR_ERR(renc);
+
+       renc->output = output;
+
+       /* Attach the bridge to the encoder. */
+       ret = drm_bridge_attach(&renc->base, bridge, NULL,
+                               DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+       if (ret) {
+               dev_err(rcdu->dev,
+                       "failed to attach bridge %pOF for output %s (%d)\n",
+                       bridge->of_node, rcar_du_output_name(output), ret);
+               return ret;
+       }
+
+       /* Create the connector for the chain of bridges. */
+       connector = drm_bridge_connector_init(&rcdu->ddev, &renc->base);
+       if (IS_ERR(connector)) {
+               dev_err(rcdu->dev,
+                       "failed to created connector for output %s (%ld)\n",
+                       rcar_du_output_name(output), PTR_ERR(connector));
+               return PTR_ERR(connector);
+       }
+
+       return drm_connector_attach_encoder(connector, &renc->base);
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h
new file mode 100644 (file)
index 0000000..e5ec8fb
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit Encoder
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_ENCODER_H__
+#define __RCAR_DU_ENCODER_H__
+
+#include <drm/drm_encoder.h>
+
+struct rcar_du_device;
+
+struct rcar_du_encoder {
+       struct drm_encoder base;
+       enum rcar_du_output output;
+};
+
+#define to_rcar_encoder(e) \
+       container_of(e, struct rcar_du_encoder, base)
+
+int rcar_du_encoder_init(struct rcar_du_device *rcdu,
+                        enum rcar_du_output output,
+                        struct device_node *enc_node);
+
+#endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c
new file mode 100644 (file)
index 0000000..2ccd258
--- /dev/null
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit Channels Pair
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+/*
+ * The R8A7779 DU is split in per-CRTC resources (scan-out engine, blending
+ * unit, timings generator, ...) and device-global resources (start/stop
+ * control, planes, ...) shared between the two CRTCs.
+ *
+ * The R8A7790 introduced a third CRTC with its own set of global resources.
+ * This would be modeled as two separate DU device instances if it wasn't for
+ * a handful or resources that are shared between the three CRTCs (mostly
+ * related to input and output routing). For this reason the R8A7790 DU must be
+ * modeled as a single device with three CRTCs, two sets of "semi-global"
+ * resources, and a few device-global resources.
+ *
+ * The rcar_du_group object is a driver specific object, without any real
+ * counterpart in the DU documentation, that models those semi-global resources.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_group.h"
+#include "rcar_du_regs.h"
+
+u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg)
+{
+       return rcar_du_read(rgrp->dev, rgrp->mmio_offset + reg);
+}
+
+void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data)
+{
+       rcar_du_write(rgrp->dev, rgrp->mmio_offset + reg, data);
+}
+
+static void rcar_du_group_setup_pins(struct rcar_du_group *rgrp)
+{
+       u32 defr6 = DEFR6_CODE;
+
+       if (rgrp->channels_mask & BIT(0))
+               defr6 |= DEFR6_ODPM02_DISP;
+
+       if (rgrp->channels_mask & BIT(1))
+               defr6 |= DEFR6_ODPM12_DISP;
+
+       rcar_du_group_write(rgrp, DEFR6, defr6);
+}
+
+static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+       u32 defr8 = DEFR8_CODE;
+
+       if (rcdu->info->gen < 3) {
+               defr8 |= DEFR8_DEFE8;
+
+               /*
+                * On Gen2 the DEFR8 register for the first group also controls
+                * RGB output routing to DPAD0 and VSPD1 routing to DU0/1/2 for
+                * DU instances that support it.
+                */
+               if (rgrp->index == 0) {
+                       defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
+                       if (rgrp->dev->vspd1_sink == 2)
+                               defr8 |= DEFR8_VSCS;
+               }
+       } else {
+               /*
+                * On Gen3 VSPD routing can't be configured, and DPAD routing
+                * is set in the group corresponding to the DPAD output (no Gen3
+                * SoC has multiple DPAD sources belonging to separate groups).
+                */
+               if (rgrp->index == rcdu->dpad0_source / 2)
+                       defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
+       }
+
+       rcar_du_group_write(rgrp, DEFR8, defr8);
+}
+
+static void rcar_du_group_setup_didsr(struct rcar_du_group *rgrp)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+       struct rcar_du_crtc *rcrtc;
+       unsigned int num_crtcs = 0;
+       unsigned int i;
+       u32 didsr;
+
+       /*
+        * Configure input dot clock routing with a hardcoded configuration. If
+        * the DU channel can use the LVDS encoder output clock as the dot
+        * clock, do so. Otherwise route DU_DOTCLKINn signal to DUn.
+        *
+        * Each channel can then select between the dot clock configured here
+        * and the clock provided by the CPG through the ESCR register.
+        */
+       if (rcdu->info->gen < 3 && rgrp->index == 0) {
+               /*
+                * On Gen2 a single register in the first group controls dot
+                * clock selection for all channels.
+                */
+               rcrtc = rcdu->crtcs;
+               num_crtcs = rcdu->num_crtcs;
+       } else if (rcdu->info->gen >= 3 && rgrp->num_crtcs > 1) {
+               /*
+                * On Gen3 dot clocks are setup through per-group registers,
+                * only available when the group has two channels.
+                */
+               rcrtc = &rcdu->crtcs[rgrp->index * 2];
+               num_crtcs = rgrp->num_crtcs;
+       }
+
+       if (!num_crtcs)
+               return;
+
+       didsr = DIDSR_CODE;
+       for (i = 0; i < num_crtcs; ++i, ++rcrtc) {
+               if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index))
+                       didsr |= DIDSR_LDCS_LVDS0(i)
+                             |  DIDSR_PDCS_CLK(i, 0);
+               else if (rcdu->info->dsi_clk_mask & BIT(rcrtc->index))
+                       didsr |= DIDSR_LDCS_DSI(i);
+               else
+                       didsr |= DIDSR_LDCS_DCLKIN(i)
+                             |  DIDSR_PDCS_CLK(i, 0);
+       }
+
+       rcar_du_group_write(rgrp, DIDSR, didsr);
+}
+
+static void rcar_du_group_setup(struct rcar_du_group *rgrp)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+       u32 defr7 = DEFR7_CODE;
+       u32 dorcr;
+
+       /* Enable extended features */
+       rcar_du_group_write(rgrp, DEFR, DEFR_CODE | DEFR_DEFE);
+       if (rcdu->info->gen < 3) {
+               rcar_du_group_write(rgrp, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
+               rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
+               rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE);
+       }
+       rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
+
+       if (rcdu->info->gen < 4)
+               rcar_du_group_setup_pins(rgrp);
+
+       if (rcdu->info->gen < 4) {
+               /*
+                * TODO: Handle routing of the DU output to CMM dynamically, as
+                * we should bypass CMM completely when no color management
+                * feature is used.
+                */
+               defr7 |= (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0) |
+                        (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0);
+               rcar_du_group_write(rgrp, DEFR7, defr7);
+       }
+
+       if (rcdu->info->gen >= 2) {
+               if (rcdu->info->gen < 4)
+                       rcar_du_group_setup_defr8(rgrp);
+               rcar_du_group_setup_didsr(rgrp);
+       }
+
+       if (rcdu->info->gen >= 3)
+               rcar_du_group_write(rgrp, DEFR10, DEFR10_CODE | DEFR10_DEFE10);
+
+       /*
+        * Use DS1PR and DS2PR to configure planes priorities and connects the
+        * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
+        *
+        * Groups that have a single channel have a hardcoded configuration. On
+        * Gen3 and newer, the documentation requires PG1T, DK1S and PG1D_DS1 to
+        * always be set in this case.
+        */
+       dorcr = DORCR_PG0D_DS0 | DORCR_DPRS;
+       if (rcdu->info->gen >= 3 && rgrp->num_crtcs == 1)
+               dorcr |= DORCR_PG1T | DORCR_DK1S | DORCR_PG1D_DS1;
+       rcar_du_group_write(rgrp, DORCR, dorcr);
+
+       /* Apply planes to CRTCs association. */
+       mutex_lock(&rgrp->lock);
+       rcar_du_group_write(rgrp, DPTSR, (rgrp->dptsr_planes << 16) |
+                           rgrp->dptsr_planes);
+       mutex_unlock(&rgrp->lock);
+}
+
+/*
+ * rcar_du_group_get - Acquire a reference to the DU channels group
+ *
+ * Acquiring the first reference setups core registers. A reference must be held
+ * before accessing any hardware registers.
+ *
+ * This function must be called with the DRM mode_config lock held.
+ *
+ * Return 0 in case of success or a negative error code otherwise.
+ */
+int rcar_du_group_get(struct rcar_du_group *rgrp)
+{
+       if (rgrp->use_count)
+               goto done;
+
+       rcar_du_group_setup(rgrp);
+
+done:
+       rgrp->use_count++;
+       return 0;
+}
+
+/*
+ * rcar_du_group_put - Release a reference to the DU
+ *
+ * This function must be called with the DRM mode_config lock held.
+ */
+void rcar_du_group_put(struct rcar_du_group *rgrp)
+{
+       --rgrp->use_count;
+}
+
+static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+
+       /*
+        * Group start/stop is controlled by the DRES and DEN bits of DSYSR0
+        * for the first group and DSYSR2 for the second group. On most DU
+        * instances, this maps to the first CRTC of the group, and we can just
+        * use rcar_du_crtc_dsysr_clr_set() to access the correct DSYSR. On
+        * M3-N, however, DU2 doesn't exist, but DSYSR2 does. We thus need to
+        * access the register directly using group read/write.
+        */
+       if (rcdu->info->channels_mask & BIT(rgrp->index * 2)) {
+               struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2];
+
+               rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_DRES | DSYSR_DEN,
+                                          start ? DSYSR_DEN : DSYSR_DRES);
+       } else {
+               rcar_du_group_write(rgrp, DSYSR,
+                                   start ? DSYSR_DEN : DSYSR_DRES);
+       }
+}
+
+void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
+{
+       /*
+        * Many of the configuration bits are only updated when the display
+        * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some
+        * of those bits could be pre-configured, but others (especially the
+        * bits related to plane assignment to display timing controllers) need
+        * to be modified at runtime.
+        *
+        * Restart the display controller if a start is requested. Sorry for the
+        * flicker. It should be possible to move most of the "DRES-update" bits
+        * setup to driver initialization time and minimize the number of cases
+        * when the display controller will have to be restarted.
+        */
+       if (start) {
+               if (rgrp->used_crtcs++ != 0)
+                       __rcar_du_group_start_stop(rgrp, false);
+               __rcar_du_group_start_stop(rgrp, true);
+       } else {
+               if (--rgrp->used_crtcs == 0)
+                       __rcar_du_group_start_stop(rgrp, false);
+       }
+}
+
+void rcar_du_group_restart(struct rcar_du_group *rgrp)
+{
+       rgrp->need_restart = false;
+
+       __rcar_du_group_start_stop(rgrp, false);
+       __rcar_du_group_start_stop(rgrp, true);
+}
+
+int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu)
+{
+       struct rcar_du_group *rgrp;
+       struct rcar_du_crtc *crtc;
+       unsigned int index;
+       int ret;
+
+       if (rcdu->info->gen < 2)
+               return 0;
+
+       /*
+        * RGB output routing to DPAD0 and VSP1D routing to DU0/1/2 are
+        * configured in the DEFR8 register of the first group on Gen2 and the
+        * last group on Gen3. As this function can be called with the DU
+        * channels of the corresponding CRTCs disabled, we need to enable the
+        * group clock before accessing the register.
+        */
+       index = rcdu->info->gen < 3 ? 0 : DIV_ROUND_UP(rcdu->num_crtcs, 2) - 1;
+       rgrp = &rcdu->groups[index];
+       crtc = &rcdu->crtcs[index * 2];
+
+       ret = clk_prepare_enable(crtc->clock);
+       if (ret < 0)
+               return ret;
+
+       rcar_du_group_setup_defr8(rgrp);
+
+       clk_disable_unprepare(crtc->clock);
+
+       return 0;
+}
+
+static void rcar_du_group_set_dpad_levels(struct rcar_du_group *rgrp)
+{
+       static const u32 doflr_values[2] = {
+               DOFLR_HSYCFL0 | DOFLR_VSYCFL0 | DOFLR_ODDFL0 |
+               DOFLR_DISPFL0 | DOFLR_CDEFL0  | DOFLR_RGBFL0,
+               DOFLR_HSYCFL1 | DOFLR_VSYCFL1 | DOFLR_ODDFL1 |
+               DOFLR_DISPFL1 | DOFLR_CDEFL1  | DOFLR_RGBFL1,
+       };
+       static const u32 dpad_mask = BIT(RCAR_DU_OUTPUT_DPAD1)
+                                  | BIT(RCAR_DU_OUTPUT_DPAD0);
+       struct rcar_du_device *rcdu = rgrp->dev;
+       u32 doflr = DOFLR_CODE;
+       unsigned int i;
+
+       if (rcdu->info->gen < 2)
+               return;
+
+       /*
+        * The DPAD outputs can't be controlled directly. However, the parallel
+        * output of the DU channels routed to DPAD can be set to fixed levels
+        * through the DOFLR group register. Use this to turn the DPAD on or off
+        * by driving fixed low-level signals at the output of any DU channel
+        * not routed to a DPAD output. This doesn't affect the DU output
+        * signals going to other outputs, such as the internal LVDS and HDMI
+        * encoders.
+        */
+
+       for (i = 0; i < rgrp->num_crtcs; ++i) {
+               struct rcar_du_crtc_state *rstate;
+               struct rcar_du_crtc *rcrtc;
+
+               rcrtc = &rcdu->crtcs[rgrp->index * 2 + i];
+               rstate = to_rcar_crtc_state(rcrtc->crtc.state);
+
+               if (!(rstate->outputs & dpad_mask))
+                       doflr |= doflr_values[i];
+       }
+
+       rcar_du_group_write(rgrp, DOFLR, doflr);
+}
+
+int rcar_du_group_set_routing(struct rcar_du_group *rgrp)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+       u32 dorcr = rcar_du_group_read(rgrp, DORCR);
+
+       dorcr &= ~(DORCR_PG1T | DORCR_DK1S | DORCR_PG1D_MASK);
+
+       /*
+        * Set the DPAD1 pins sources. Select CRTC 0 if explicitly requested and
+        * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1
+        * by default.
+        */
+       if (rcdu->dpad1_source == rgrp->index * 2)
+               dorcr |= DORCR_PG1D_DS0;
+       else
+               dorcr |= DORCR_PG1T | DORCR_DK1S | DORCR_PG1D_DS1;
+
+       rcar_du_group_write(rgrp, DORCR, dorcr);
+
+       rcar_du_group_set_dpad_levels(rgrp);
+
+       return rcar_du_set_dpad0_vsp1_routing(rgrp->dev);
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_group.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_group.h
new file mode 100644 (file)
index 0000000..55649ad
--- /dev/null
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit Planes and CRTCs Group
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_GROUP_H__
+#define __RCAR_DU_GROUP_H__
+
+#include <linux/mutex.h>
+
+#include "rcar_du_plane.h"
+
+struct rcar_du_device;
+
+/*
+ * struct rcar_du_group - CRTCs and planes group
+ * @dev: the DU device
+ * @mmio_offset: registers offset in the device memory map
+ * @index: group index
+ * @channels_mask: bitmask of populated DU channels in this group
+ * @cmms_mask: bitmask of available CMMs in this group
+ * @num_crtcs: number of CRTCs in this group (1 or 2)
+ * @use_count: number of users of the group (rcar_du_group_(get|put))
+ * @used_crtcs: number of CRTCs currently in use
+ * @lock: protects the dptsr_planes field and the DPTSR register
+ * @dptsr_planes: bitmask of planes driven by dot-clock and timing generator 1
+ * @num_planes: number of planes in the group
+ * @planes: planes handled by the group
+ * @need_restart: the group needs to be restarted due to a configuration change
+ */
+struct rcar_du_group {
+       struct rcar_du_device *dev;
+       unsigned int mmio_offset;
+       unsigned int index;
+
+       unsigned int channels_mask;
+       unsigned int cmms_mask;
+       unsigned int num_crtcs;
+       unsigned int use_count;
+       unsigned int used_crtcs;
+
+       struct mutex lock;
+       unsigned int dptsr_planes;
+
+       unsigned int num_planes;
+       struct rcar_du_plane planes[RCAR_DU_NUM_KMS_PLANES];
+       bool need_restart;
+};
+
+u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg);
+void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data);
+
+int rcar_du_group_get(struct rcar_du_group *rgrp);
+void rcar_du_group_put(struct rcar_du_group *rgrp);
+void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start);
+void rcar_du_group_restart(struct rcar_du_group *rgrp);
+int rcar_du_group_set_routing(struct rcar_du_group *rgrp);
+
+int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu);
+
+#endif /* __RCAR_DU_GROUP_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c
new file mode 100644 (file)
index 0000000..adfb36b
--- /dev/null
@@ -0,0 +1,1006 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/wait.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vsp.h"
+#include "rcar_du_writeback.h"
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct rcar_du_format_info rcar_du_format_infos[] = {
+       {
+               .fourcc = DRM_FORMAT_RGB565,
+               .v4l2 = V4L2_PIX_FMT_RGB565,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB1555,
+               .v4l2 = V4L2_PIX_FMT_ARGB555,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB1555,
+               .v4l2 = V4L2_PIX_FMT_XRGB555,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB8888,
+               .v4l2 = V4L2_PIX_FMT_XBGR32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_RGB888,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB8888,
+               .v4l2 = V4L2_PIX_FMT_ABGR32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_ARGB8888,
+       }, {
+               .fourcc = DRM_FORMAT_UYVY,
+               .v4l2 = V4L2_PIX_FMT_UYVY,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_YUYV,
+               .v4l2 = V4L2_PIX_FMT_YUYV,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_NV12,
+               .v4l2 = V4L2_PIX_FMT_NV12M,
+               .bpp = 12,
+               .planes = 2,
+               .hsub = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_NV21,
+               .v4l2 = V4L2_PIX_FMT_NV21M,
+               .bpp = 12,
+               .planes = 2,
+               .hsub = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_NV16,
+               .v4l2 = V4L2_PIX_FMT_NV16M,
+               .bpp = 16,
+               .planes = 2,
+               .hsub = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       },
+       /*
+        * The following formats are not supported on Gen2 and thus have no
+        * associated .pnmr or .edf settings.
+        */
+       {
+               .fourcc = DRM_FORMAT_RGB332,
+               .v4l2 = V4L2_PIX_FMT_RGB332,
+               .bpp = 8,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB4444,
+               .v4l2 = V4L2_PIX_FMT_ARGB444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB4444,
+               .v4l2 = V4L2_PIX_FMT_XRGB444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBA4444,
+               .v4l2 = V4L2_PIX_FMT_RGBA444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBX4444,
+               .v4l2 = V4L2_PIX_FMT_RGBX444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_ABGR4444,
+               .v4l2 = V4L2_PIX_FMT_ABGR444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_XBGR4444,
+               .v4l2 = V4L2_PIX_FMT_XBGR444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_BGRA4444,
+               .v4l2 = V4L2_PIX_FMT_BGRA444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_BGRX4444,
+               .v4l2 = V4L2_PIX_FMT_BGRX444,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBA5551,
+               .v4l2 = V4L2_PIX_FMT_RGBA555,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBX5551,
+               .v4l2 = V4L2_PIX_FMT_RGBX555,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_ABGR1555,
+               .v4l2 = V4L2_PIX_FMT_ABGR555,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_XBGR1555,
+               .v4l2 = V4L2_PIX_FMT_XBGR555,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_BGRA5551,
+               .v4l2 = V4L2_PIX_FMT_BGRA555,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_BGRX5551,
+               .v4l2 = V4L2_PIX_FMT_BGRX555,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_BGR888,
+               .v4l2 = V4L2_PIX_FMT_RGB24,
+               .bpp = 24,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGB888,
+               .v4l2 = V4L2_PIX_FMT_BGR24,
+               .bpp = 24,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBA8888,
+               .v4l2 = V4L2_PIX_FMT_BGRA32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBX8888,
+               .v4l2 = V4L2_PIX_FMT_BGRX32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_ABGR8888,
+               .v4l2 = V4L2_PIX_FMT_RGBA32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_XBGR8888,
+               .v4l2 = V4L2_PIX_FMT_RGBX32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_BGRA8888,
+               .v4l2 = V4L2_PIX_FMT_ARGB32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_BGRX8888,
+               .v4l2 = V4L2_PIX_FMT_XRGB32,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBX1010102,
+               .v4l2 = V4L2_PIX_FMT_RGBX1010102,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_RGBA1010102,
+               .v4l2 = V4L2_PIX_FMT_RGBA1010102,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB2101010,
+               .v4l2 = V4L2_PIX_FMT_ARGB2101010,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_YVYU,
+               .v4l2 = V4L2_PIX_FMT_YVYU,
+               .bpp = 16,
+               .planes = 1,
+               .hsub = 2,
+       }, {
+               .fourcc = DRM_FORMAT_NV61,
+               .v4l2 = V4L2_PIX_FMT_NV61M,
+               .bpp = 16,
+               .planes = 2,
+               .hsub = 2,
+       }, {
+               .fourcc = DRM_FORMAT_YUV420,
+               .v4l2 = V4L2_PIX_FMT_YUV420M,
+               .bpp = 12,
+               .planes = 3,
+               .hsub = 2,
+       }, {
+               .fourcc = DRM_FORMAT_YVU420,
+               .v4l2 = V4L2_PIX_FMT_YVU420M,
+               .bpp = 12,
+               .planes = 3,
+               .hsub = 2,
+       }, {
+               .fourcc = DRM_FORMAT_YUV422,
+               .v4l2 = V4L2_PIX_FMT_YUV422M,
+               .bpp = 16,
+               .planes = 3,
+               .hsub = 2,
+       }, {
+               .fourcc = DRM_FORMAT_YVU422,
+               .v4l2 = V4L2_PIX_FMT_YVU422M,
+               .bpp = 16,
+               .planes = 3,
+               .hsub = 2,
+       }, {
+               .fourcc = DRM_FORMAT_YUV444,
+               .v4l2 = V4L2_PIX_FMT_YUV444M,
+               .bpp = 24,
+               .planes = 3,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_YVU444,
+               .v4l2 = V4L2_PIX_FMT_YVU444M,
+               .bpp = 24,
+               .planes = 3,
+               .hsub = 1,
+       }, {
+               .fourcc = DRM_FORMAT_Y210,
+               .v4l2 = V4L2_PIX_FMT_Y210,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 2,
+       }, {
+               .fourcc = DRM_FORMAT_Y212,
+               .v4l2 = V4L2_PIX_FMT_Y212,
+               .bpp = 32,
+               .planes = 1,
+               .hsub = 2,
+       },
+};
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
+               if (rcar_du_format_infos[i].fourcc == fourcc)
+                       return &rcar_du_format_infos[i];
+       }
+
+       return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer
+ */
+
+static const struct drm_gem_object_funcs rcar_du_gem_funcs = {
+       .free = drm_gem_dma_object_free,
+       .print_info = drm_gem_dma_object_print_info,
+       .get_sg_table = drm_gem_dma_object_get_sg_table,
+       .vmap = drm_gem_dma_object_vmap,
+       .mmap = drm_gem_dma_object_mmap,
+       .vm_ops = &drm_gem_dma_vm_ops,
+};
+
+struct drm_gem_object *rcar_du_gem_prime_import_sg_table(struct drm_device *dev,
+                               struct dma_buf_attachment *attach,
+                               struct sg_table *sgt)
+{
+       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
+       struct drm_gem_dma_object *dma_obj;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
+               return drm_gem_dma_prime_import_sg_table(dev, attach, sgt);
+
+       /* Create a DMA GEM buffer. */
+       dma_obj = kzalloc(sizeof(*dma_obj), GFP_KERNEL);
+       if (!dma_obj)
+               return ERR_PTR(-ENOMEM);
+
+       gem_obj = &dma_obj->base;
+       gem_obj->funcs = &rcar_du_gem_funcs;
+
+       drm_gem_private_object_init(dev, gem_obj, attach->dmabuf->size);
+       dma_obj->map_noncoherent = false;
+
+       ret = drm_gem_create_mmap_offset(gem_obj);
+       if (ret) {
+               drm_gem_object_release(gem_obj);
+               kfree(dma_obj);
+               return ERR_PTR(ret);
+       }
+
+       dma_obj->dma_addr = 0;
+       dma_obj->sgt = sgt;
+
+       return gem_obj;
+}
+
+int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
+                       struct drm_mode_create_dumb *args)
+{
+       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
+       unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+       unsigned int align;
+
+       /*
+        * The R8A7779 DU requires a 16 pixels pitch alignment as documented,
+        * but the R8A7790 DU seems to require a 128 bytes pitch alignment.
+        */
+       if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B))
+               align = 128;
+       else
+               align = 16 * args->bpp / 8;
+
+       args->pitch = roundup(min_pitch, align);
+
+       return drm_gem_dma_dumb_create_internal(file, dev, args);
+}
+
+static struct drm_framebuffer *
+rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+                 const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
+       const struct rcar_du_format_info *format;
+       unsigned int chroma_pitch;
+       unsigned int max_pitch;
+       unsigned int align;
+       unsigned int i;
+
+       format = rcar_du_format_info(mode_cmd->pixel_format);
+       if (format == NULL) {
+               dev_dbg(dev->dev, "unsupported pixel format %p4cc\n",
+                       &mode_cmd->pixel_format);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (rcdu->info->gen < 3) {
+               /*
+                * On Gen2 the DU limits the pitch to 4095 pixels and requires
+                * buffers to be aligned to a 16 pixels boundary (or 128 bytes
+                * on some platforms).
+                */
+               unsigned int bpp = format->planes == 1 ? format->bpp / 8 : 1;
+
+               max_pitch = 4095 * bpp;
+
+               if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B))
+                       align = 128;
+               else
+                       align = 16 * bpp;
+       } else {
+               /*
+                * On Gen3 the memory interface is handled by the VSP that
+                * limits the pitch to 65535 bytes and has no alignment
+                * constraint.
+                */
+               max_pitch = 65535;
+               align = 1;
+       }
+
+       if (mode_cmd->pitches[0] & (align - 1) ||
+           mode_cmd->pitches[0] > max_pitch) {
+               dev_dbg(dev->dev, "invalid pitch value %u\n",
+                       mode_cmd->pitches[0]);
+               return ERR_PTR(-EINVAL);
+       }
+
+       /*
+        * Calculate the chroma plane(s) pitch using the horizontal subsampling
+        * factor. For semi-planar formats, the U and V planes are combined, the
+        * pitch must thus be doubled.
+        */
+       chroma_pitch = mode_cmd->pitches[0] / format->hsub;
+       if (format->planes == 2)
+               chroma_pitch *= 2;
+
+       for (i = 1; i < format->planes; ++i) {
+               if (mode_cmd->pitches[i] != chroma_pitch) {
+                       dev_dbg(dev->dev,
+                               "luma and chroma pitches are not compatible\n");
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       return drm_gem_fb_create(dev, file_priv, mode_cmd);
+}
+
+/* -----------------------------------------------------------------------------
+ * Atomic Check and Update
+ */
+
+static int rcar_du_atomic_check(struct drm_device *dev,
+                               struct drm_atomic_state *state)
+{
+       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
+       int ret;
+
+       ret = drm_atomic_helper_check(dev, state);
+       if (ret)
+               return ret;
+
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
+               return 0;
+
+       return rcar_du_atomic_check_planes(dev, state);
+}
+
+static void rcar_du_atomic_commit_tail(struct drm_atomic_state *old_state)
+{
+       struct drm_device *dev = old_state->dev;
+       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
+       struct drm_crtc_state *crtc_state;
+       struct drm_crtc *crtc;
+       unsigned int i;
+
+       /*
+        * Store RGB routing to DPAD0 and DPAD1, the hardware will be configured
+        * when starting the CRTCs.
+        */
+       rcdu->dpad1_source = -1;
+
+       for_each_new_crtc_in_state(old_state, crtc, crtc_state, i) {
+               struct rcar_du_crtc_state *rcrtc_state =
+                       to_rcar_crtc_state(crtc_state);
+               struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+               if (rcrtc_state->outputs & BIT(RCAR_DU_OUTPUT_DPAD0))
+                       rcdu->dpad0_source = rcrtc->index;
+
+               if (rcrtc_state->outputs & BIT(RCAR_DU_OUTPUT_DPAD1))
+                       rcdu->dpad1_source = rcrtc->index;
+       }
+
+       /* Apply the atomic update. */
+       drm_atomic_helper_commit_modeset_disables(dev, old_state);
+       drm_atomic_helper_commit_planes(dev, old_state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
+       drm_atomic_helper_commit_modeset_enables(dev, old_state);
+
+       drm_atomic_helper_commit_hw_done(old_state);
+       drm_atomic_helper_wait_for_flip_done(dev, old_state);
+
+       drm_atomic_helper_cleanup_planes(dev, old_state);
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+static const struct drm_mode_config_helper_funcs rcar_du_mode_config_helper = {
+       .atomic_commit_tail = rcar_du_atomic_commit_tail,
+};
+
+static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
+       .fb_create = rcar_du_fb_create,
+       .atomic_check = rcar_du_atomic_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
+                                    enum rcar_du_output output,
+                                    struct of_endpoint *ep)
+{
+       struct device_node *entity;
+       int ret;
+
+       /* Locate the connected entity and initialize the encoder. */
+       entity = of_graph_get_remote_port_parent(ep->local_node);
+       if (!entity) {
+               dev_dbg(rcdu->dev, "unconnected endpoint %pOF, skipping\n",
+                       ep->local_node);
+               return -ENODEV;
+       }
+
+       if (!of_device_is_available(entity)) {
+               dev_dbg(rcdu->dev,
+                       "connected entity %pOF is disabled, skipping\n",
+                       entity);
+               of_node_put(entity);
+               return -ENODEV;
+       }
+
+       ret = rcar_du_encoder_init(rcdu, output, entity);
+       if (ret && ret != -EPROBE_DEFER && ret != -ENOLINK)
+               dev_warn(rcdu->dev,
+                        "failed to initialize encoder %pOF on output %s (%d), skipping\n",
+                        entity, rcar_du_output_name(output), ret);
+
+       of_node_put(entity);
+
+       return ret;
+}
+
+static int rcar_du_encoders_init(struct rcar_du_device *rcdu)
+{
+       struct device_node *np = rcdu->dev->of_node;
+       struct device_node *ep_node;
+       unsigned int num_encoders = 0;
+
+       /*
+        * Iterate over the endpoints and create one encoder for each output
+        * pipeline.
+        */
+       for_each_endpoint_of_node(np, ep_node) {
+               enum rcar_du_output output;
+               struct of_endpoint ep;
+               unsigned int i;
+               int ret;
+
+               ret = of_graph_parse_endpoint(ep_node, &ep);
+               if (ret < 0) {
+                       of_node_put(ep_node);
+                       return ret;
+               }
+
+               /* Find the output route corresponding to the port number. */
+               for (i = 0; i < RCAR_DU_OUTPUT_MAX; ++i) {
+                       if (rcdu->info->routes[i].possible_crtcs &&
+                           rcdu->info->routes[i].port == ep.port) {
+                               output = i;
+                               break;
+                       }
+               }
+
+               if (i == RCAR_DU_OUTPUT_MAX) {
+                       dev_warn(rcdu->dev,
+                                "port %u references unexisting output, skipping\n",
+                                ep.port);
+                       continue;
+               }
+
+               /* Process the output pipeline. */
+               ret = rcar_du_encoders_init_one(rcdu, output, &ep);
+               if (ret < 0) {
+                       if (ret == -EPROBE_DEFER) {
+                               of_node_put(ep_node);
+                               return ret;
+                       }
+
+                       continue;
+               }
+
+               num_encoders++;
+       }
+
+       return num_encoders;
+}
+
+static int rcar_du_properties_init(struct rcar_du_device *rcdu)
+{
+       /*
+        * The color key is expressed as an RGB888 triplet stored in a 32-bit
+        * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
+        * or enable source color keying (1).
+        */
+       rcdu->props.colorkey =
+               drm_property_create_range(&rcdu->ddev, 0, "colorkey",
+                                         0, 0x01ffffff);
+       if (rcdu->props.colorkey == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int rcar_du_vsps_init(struct rcar_du_device *rcdu)
+{
+       const struct device_node *np = rcdu->dev->of_node;
+       const char *vsps_prop_name = "renesas,vsps";
+       struct of_phandle_args args;
+       struct {
+               struct device_node *np;
+               unsigned int crtcs_mask;
+       } vsps[RCAR_DU_MAX_VSPS] = { { NULL, }, };
+       unsigned int vsps_count = 0;
+       unsigned int cells;
+       unsigned int i;
+       int ret;
+
+       /*
+        * First parse the DT vsps property to populate the list of VSPs. Each
+        * entry contains a pointer to the VSP DT node and a bitmask of the
+        * connected DU CRTCs.
+        */
+       ret = of_property_count_u32_elems(np, vsps_prop_name);
+       if (ret < 0) {
+               /* Backward compatibility with old DTBs. */
+               vsps_prop_name = "vsps";
+               ret = of_property_count_u32_elems(np, vsps_prop_name);
+       }
+       cells = ret / rcdu->num_crtcs - 1;
+       if (cells > 1)
+               return -EINVAL;
+
+       for (i = 0; i < rcdu->num_crtcs; ++i) {
+               unsigned int j;
+
+               ret = of_parse_phandle_with_fixed_args(np, vsps_prop_name,
+                                                      cells, i, &args);
+               if (ret < 0)
+                       goto error;
+
+               /*
+                * Add the VSP to the list or update the corresponding existing
+                * entry if the VSP has already been added.
+                */
+               for (j = 0; j < vsps_count; ++j) {
+                       if (vsps[j].np == args.np)
+                               break;
+               }
+
+               if (j < vsps_count)
+                       of_node_put(args.np);
+               else
+                       vsps[vsps_count++].np = args.np;
+
+               vsps[j].crtcs_mask |= BIT(i);
+
+               /*
+                * Store the VSP pointer and pipe index in the CRTC. If the
+                * second cell of the 'renesas,vsps' specifier isn't present,
+                * default to 0 to remain compatible with older DT bindings.
+                */
+               rcdu->crtcs[i].vsp = &rcdu->vsps[j];
+               rcdu->crtcs[i].vsp_pipe = cells >= 1 ? args.args[0] : 0;
+       }
+
+       /*
+        * Then initialize all the VSPs from the node pointers and CRTCs bitmask
+        * computed previously.
+        */
+       for (i = 0; i < vsps_count; ++i) {
+               struct rcar_du_vsp *vsp = &rcdu->vsps[i];
+
+               vsp->index = i;
+               vsp->dev = rcdu;
+
+               ret = rcar_du_vsp_init(vsp, vsps[i].np, vsps[i].crtcs_mask);
+               if (ret < 0)
+                       goto error;
+       }
+
+       return 0;
+
+error:
+       for (i = 0; i < ARRAY_SIZE(vsps); ++i)
+               of_node_put(vsps[i].np);
+
+       return ret;
+}
+
+static int rcar_du_cmm_init(struct rcar_du_device *rcdu)
+{
+       const struct device_node *np = rcdu->dev->of_node;
+       unsigned int i;
+       int cells;
+
+       cells = of_property_count_u32_elems(np, "renesas,cmms");
+       if (cells == -EINVAL)
+               return 0;
+
+       if (cells > rcdu->num_crtcs) {
+               dev_err(rcdu->dev,
+                       "Invalid number of entries in 'renesas,cmms'\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < cells; ++i) {
+               struct platform_device *pdev;
+               struct device_link *link;
+               struct device_node *cmm;
+               int ret;
+
+               cmm = of_parse_phandle(np, "renesas,cmms", i);
+               if (!cmm) {
+                       dev_err(rcdu->dev,
+                               "Failed to parse 'renesas,cmms' property\n");
+                       return -EINVAL;
+               }
+
+               if (!of_device_is_available(cmm)) {
+                       /* It's fine to have a phandle to a non-enabled CMM. */
+                       of_node_put(cmm);
+                       continue;
+               }
+
+               pdev = of_find_device_by_node(cmm);
+               if (!pdev) {
+                       dev_err(rcdu->dev, "No device found for CMM%u\n", i);
+                       of_node_put(cmm);
+                       return -EINVAL;
+               }
+
+               of_node_put(cmm);
+
+               /*
+                * -ENODEV is used to report that the CMM config option is
+                * disabled: return 0 and let the DU continue probing.
+                */
+               ret = rcar_cmm_init(pdev);
+               if (ret) {
+                       platform_device_put(pdev);
+                       return ret == -ENODEV ? 0 : ret;
+               }
+
+               rcdu->cmms[i] = pdev;
+
+               /*
+                * Enforce suspend/resume ordering by making the CMM a provider
+                * of the DU: CMM is suspended after and resumed before the DU.
+                */
+               link = device_link_add(rcdu->dev, &pdev->dev, DL_FLAG_STATELESS);
+               if (!link) {
+                       dev_err(rcdu->dev,
+                               "Failed to create device link to CMM%u\n", i);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static void rcar_du_modeset_cleanup(struct drm_device *dev, void *res)
+{
+       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->cmms); ++i)
+               platform_device_put(rcdu->cmms[i]);
+}
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu)
+{
+       static const unsigned int mmio_offsets[] = {
+               DU0_REG_OFFSET, DU2_REG_OFFSET
+       };
+
+       struct drm_device *dev = &rcdu->ddev;
+       struct drm_encoder *encoder;
+       unsigned int dpad0_sources;
+       unsigned int num_encoders;
+       unsigned int num_groups;
+       unsigned int swindex;
+       unsigned int hwindex;
+       unsigned int i;
+       int ret;
+
+       ret = drmm_mode_config_init(dev);
+       if (ret)
+               return ret;
+
+       ret = drmm_add_action(&rcdu->ddev, rcar_du_modeset_cleanup, NULL);
+       if (ret)
+               return ret;
+
+       dev->mode_config.min_width = 0;
+       dev->mode_config.min_height = 0;
+       dev->mode_config.normalize_zpos = true;
+       dev->mode_config.funcs = &rcar_du_mode_config_funcs;
+       dev->mode_config.helper_private = &rcar_du_mode_config_helper;
+
+       if (rcdu->info->gen < 3) {
+               dev->mode_config.max_width = 4095;
+               dev->mode_config.max_height = 2047;
+       } else {
+               /*
+                * The Gen3 DU uses the VSP1 for memory access, and is limited
+                * to frame sizes of 8190x8190.
+                */
+               dev->mode_config.max_width = 8190;
+               dev->mode_config.max_height = 8190;
+       }
+
+       rcdu->num_crtcs = hweight8(rcdu->info->channels_mask);
+
+       ret = rcar_du_properties_init(rcdu);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Initialize vertical blanking interrupts handling. Start with vblank
+        * disabled for all CRTCs.
+        */
+       ret = drm_vblank_init(dev, rcdu->num_crtcs);
+       if (ret < 0)
+               return ret;
+
+       /* Initialize the groups. */
+       num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2);
+
+       for (i = 0; i < num_groups; ++i) {
+               struct rcar_du_group *rgrp = &rcdu->groups[i];
+
+               mutex_init(&rgrp->lock);
+
+               rgrp->dev = rcdu;
+               rgrp->mmio_offset = mmio_offsets[i];
+               rgrp->index = i;
+               /* Extract the channel mask for this group only. */
+               rgrp->channels_mask = (rcdu->info->channels_mask >> (2 * i))
+                                  & GENMASK(1, 0);
+               rgrp->num_crtcs = hweight8(rgrp->channels_mask);
+
+               /*
+                * If we have more than one CRTCs in this group pre-associate
+                * the low-order planes with CRTC 0 and the high-order planes
+                * with CRTC 1 to minimize flicker occurring when the
+                * association is changed.
+                */
+               rgrp->dptsr_planes = rgrp->num_crtcs > 1
+                                  ? (rcdu->info->gen >= 3 ? 0x04 : 0xf0)
+                                  : 0;
+
+               if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+                       ret = rcar_du_planes_init(rgrp);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       /* Initialize the compositors. */
+       if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) {
+               ret = rcar_du_vsps_init(rcdu);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Initialize the Color Management Modules. */
+       ret = rcar_du_cmm_init(rcdu);
+       if (ret)
+               return ret;
+
+       /* Create the CRTCs. */
+       for (swindex = 0, hwindex = 0; swindex < rcdu->num_crtcs; ++hwindex) {
+               struct rcar_du_group *rgrp;
+
+               /* Skip unpopulated DU channels. */
+               if (!(rcdu->info->channels_mask & BIT(hwindex)))
+                       continue;
+
+               rgrp = &rcdu->groups[hwindex / 2];
+
+               ret = rcar_du_crtc_create(rgrp, swindex++, hwindex);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Initialize the encoders. */
+       ret = rcar_du_encoders_init(rcdu);
+       if (ret < 0)
+               return ret;
+
+       if (ret == 0) {
+               dev_err(rcdu->dev, "error: no encoder could be initialized\n");
+               return -EINVAL;
+       }
+
+       num_encoders = ret;
+
+       /*
+        * Set the possible CRTCs and possible clones. There's always at least
+        * one way for all encoders to clone each other, set all bits in the
+        * possible clones field.
+        */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+               const struct rcar_du_output_routing *route =
+                       &rcdu->info->routes[renc->output];
+
+               encoder->possible_crtcs = route->possible_crtcs;
+               encoder->possible_clones = (1 << num_encoders) - 1;
+       }
+
+       /* Create the writeback connectors. */
+       if (rcdu->info->gen >= 3) {
+               for (i = 0; i < rcdu->num_crtcs; ++i) {
+                       struct rcar_du_crtc *rcrtc = &rcdu->crtcs[i];
+
+                       ret = rcar_du_writeback_init(rcdu, rcrtc);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       /*
+        * Initialize the default DPAD0 source to the index of the first DU
+        * channel that can be connected to DPAD0. The exact value doesn't
+        * matter as it should be overwritten by mode setting for the RGB
+        * output, but it is nonetheless required to ensure a valid initial
+        * hardware configuration on Gen3 where DU0 can't always be connected to
+        * DPAD0.
+        */
+       dpad0_sources = rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs;
+       rcdu->dpad0_source = ffs(dpad0_sources) - 1;
+
+       drm_mode_config_reset(dev);
+
+       drm_kms_helper_poll_init(dev);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.h
new file mode 100644 (file)
index 0000000..f31afee
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_KMS_H__
+#define __RCAR_DU_KMS_H__
+
+#include <linux/types.h>
+
+struct dma_buf_attachment;
+struct drm_file;
+struct drm_device;
+struct drm_gem_object;
+struct drm_mode_create_dumb;
+struct rcar_du_device;
+struct sg_table;
+
+struct rcar_du_format_info {
+       u32 fourcc;
+       u32 v4l2;
+       unsigned int bpp;
+       unsigned int planes;
+       unsigned int hsub;
+       unsigned int pnmr;
+       unsigned int edf;
+};
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu);
+
+int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
+                       struct drm_mode_create_dumb *args);
+
+struct drm_gem_object *rcar_du_gem_prime_import_sg_table(struct drm_device *dev,
+                               struct dma_buf_attachment *attach,
+                               struct sg_table *sgt);
+
+#endif /* __RCAR_DU_KMS_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c
new file mode 100644 (file)
index 0000000..d759e01
--- /dev/null
@@ -0,0 +1,831 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_dma_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_group.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Atomic hardware plane allocator
+ *
+ * The hardware plane allocator is solely based on the atomic plane states
+ * without keeping any external state to avoid races between .atomic_check()
+ * and .atomic_commit().
+ *
+ * The core idea is to avoid using a free planes bitmask that would need to be
+ * shared between check and commit handlers with a collective knowledge based on
+ * the allocated hardware plane(s) for each KMS plane. The allocator then loops
+ * over all plane states to compute the free planes bitmask, allocates hardware
+ * planes based on that bitmask, and stores the result back in the plane states.
+ *
+ * For this to work we need to access the current state of planes not touched by
+ * the atomic update. To ensure that it won't be modified, we need to lock all
+ * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
+ * updates from .atomic_check() up to completion (when swapping the states if
+ * the check step has succeeded) or rollback (when freeing the states if the
+ * check step has failed).
+ *
+ * Allocation is performed in the .atomic_check() handler and applied
+ * automatically when the core swaps the old and new states.
+ */
+
+static bool rcar_du_plane_needs_realloc(
+                               const struct rcar_du_plane_state *old_state,
+                               const struct rcar_du_plane_state *new_state)
+{
+       /*
+        * Lowering the number of planes doesn't strictly require reallocation
+        * as the extra hardware plane will be freed when committing, but doing
+        * so could lead to more fragmentation.
+        */
+       if (!old_state->format ||
+           old_state->format->planes != new_state->format->planes)
+               return true;
+
+       /* Reallocate hardware planes if the source has changed. */
+       if (old_state->source != new_state->source)
+               return true;
+
+       return false;
+}
+
+static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
+{
+       unsigned int mask;
+
+       if (state->hwindex == -1)
+               return 0;
+
+       mask = 1 << state->hwindex;
+       if (state->format->planes == 2)
+               mask |= 1 << ((state->hwindex + 1) % 8);
+
+       return mask;
+}
+
+/*
+ * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
+ * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
+ * DU0/1 plane 1.
+ *
+ * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
+ * and allocate planes in reverse index order otherwise to ensure maximum
+ * availability of planes 0 and 1.
+ *
+ * The caller is responsible for ensuring that the requested source is
+ * compatible with the DU revision.
+ */
+static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
+                                struct rcar_du_plane_state *state,
+                                unsigned int free)
+{
+       unsigned int num_planes = state->format->planes;
+       int fixed = -1;
+       int i;
+
+       if (state->source == RCAR_DU_PLANE_VSPD0) {
+               /* VSPD0 feeds plane 0 on DU0/1. */
+               if (plane->group->index != 0)
+                       return -EINVAL;
+
+               fixed = 0;
+       } else if (state->source == RCAR_DU_PLANE_VSPD1) {
+               /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
+               fixed = plane->group->index == 0 ? 1 : 0;
+       }
+
+       if (fixed >= 0)
+               return free & (1 << fixed) ? fixed : -EBUSY;
+
+       for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
+               if (!(free & (1 << i)))
+                       continue;
+
+               if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
+                       break;
+       }
+
+       return i < 0 ? -EBUSY : i;
+}
+
+int rcar_du_atomic_check_planes(struct drm_device *dev,
+                               struct drm_atomic_state *state)
+{
+       struct rcar_du_device *rcdu = to_rcar_du_device(dev);
+       unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
+       unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
+       bool needs_realloc = false;
+       unsigned int groups = 0;
+       unsigned int i;
+       struct drm_plane *drm_plane;
+       struct drm_plane_state *old_drm_plane_state;
+       struct drm_plane_state *new_drm_plane_state;
+
+       /* Check if hardware planes need to be reallocated. */
+       for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
+                                      new_drm_plane_state, i) {
+               struct rcar_du_plane_state *old_plane_state;
+               struct rcar_du_plane_state *new_plane_state;
+               struct rcar_du_plane *plane;
+               unsigned int index;
+
+               plane = to_rcar_plane(drm_plane);
+               old_plane_state = to_rcar_plane_state(old_drm_plane_state);
+               new_plane_state = to_rcar_plane_state(new_drm_plane_state);
+
+               dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__,
+                       plane->group->index, plane - plane->group->planes);
+
+               /*
+                * If the plane is being disabled we don't need to go through
+                * the full reallocation procedure. Just mark the hardware
+                * plane(s) as freed.
+                */
+               if (!new_plane_state->format) {
+                       dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
+                               __func__);
+                       index = plane - plane->group->planes;
+                       group_freed_planes[plane->group->index] |= 1 << index;
+                       new_plane_state->hwindex = -1;
+                       continue;
+               }
+
+               /*
+                * If the plane needs to be reallocated mark it as such, and
+                * mark the hardware plane(s) as free.
+                */
+               if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) {
+                       dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
+                               __func__);
+                       groups |= 1 << plane->group->index;
+                       needs_realloc = true;
+
+                       index = plane - plane->group->planes;
+                       group_freed_planes[plane->group->index] |= 1 << index;
+                       new_plane_state->hwindex = -1;
+               }
+       }
+
+       if (!needs_realloc)
+               return 0;
+
+       /*
+        * Grab all plane states for the groups that need reallocation to ensure
+        * locking and avoid racy updates. This serializes the update operation,
+        * but there's not much we can do about it as that's the hardware
+        * design.
+        *
+        * Compute the used planes mask for each group at the same time to avoid
+        * looping over the planes separately later.
+        */
+       while (groups) {
+               unsigned int index = ffs(groups) - 1;
+               struct rcar_du_group *group = &rcdu->groups[index];
+               unsigned int used_planes = 0;
+
+               dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
+                       __func__, index);
+
+               for (i = 0; i < group->num_planes; ++i) {
+                       struct rcar_du_plane *plane = &group->planes[i];
+                       struct rcar_du_plane_state *new_plane_state;
+                       struct drm_plane_state *s;
+
+                       s = drm_atomic_get_plane_state(state, &plane->plane);
+                       if (IS_ERR(s))
+                               return PTR_ERR(s);
+
+                       /*
+                        * If the plane has been freed in the above loop its
+                        * hardware planes must not be added to the used planes
+                        * bitmask. However, the current state doesn't reflect
+                        * the free state yet, as we've modified the new state
+                        * above. Use the local freed planes list to check for
+                        * that condition instead.
+                        */
+                       if (group_freed_planes[index] & (1 << i)) {
+                               dev_dbg(rcdu->dev,
+                                       "%s: plane (%u,%tu) has been freed, skipping\n",
+                                       __func__, plane->group->index,
+                                       plane - plane->group->planes);
+                               continue;
+                       }
+
+                       new_plane_state = to_rcar_plane_state(s);
+                       used_planes |= rcar_du_plane_hwmask(new_plane_state);
+
+                       dev_dbg(rcdu->dev,
+                               "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n",
+                               __func__, plane->group->index,
+                               plane - plane->group->planes,
+                               new_plane_state->format ?
+                               new_plane_state->format->planes : 0,
+                               new_plane_state->hwindex);
+               }
+
+               group_free_planes[index] = 0xff & ~used_planes;
+               groups &= ~(1 << index);
+
+               dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
+                       __func__, index, group_free_planes[index]);
+       }
+
+       /* Reallocate hardware planes for each plane that needs it. */
+       for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
+                                      new_drm_plane_state, i) {
+               struct rcar_du_plane_state *old_plane_state;
+               struct rcar_du_plane_state *new_plane_state;
+               struct rcar_du_plane *plane;
+               unsigned int crtc_planes;
+               unsigned int free;
+               int idx;
+
+               plane = to_rcar_plane(drm_plane);
+               old_plane_state = to_rcar_plane_state(old_drm_plane_state);
+               new_plane_state = to_rcar_plane_state(new_drm_plane_state);
+
+               dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__,
+                       plane->group->index, plane - plane->group->planes);
+
+               /*
+                * Skip planes that are being disabled or don't need to be
+                * reallocated.
+                */
+               if (!new_plane_state->format ||
+                   !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state))
+                       continue;
+
+               /*
+                * Try to allocate the plane from the free planes currently
+                * associated with the target CRTC to avoid restarting the CRTC
+                * group and thus minimize flicker. If it fails fall back to
+                * allocating from all free planes.
+                */
+               crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2
+                           ? plane->group->dptsr_planes
+                           : ~plane->group->dptsr_planes;
+               free = group_free_planes[plane->group->index];
+
+               idx = rcar_du_plane_hwalloc(plane, new_plane_state,
+                                           free & crtc_planes);
+               if (idx < 0)
+                       idx = rcar_du_plane_hwalloc(plane, new_plane_state,
+                                                   free);
+               if (idx < 0) {
+                       dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
+                               __func__);
+                       return idx;
+               }
+
+               dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
+                       __func__, new_plane_state->format->planes, idx);
+
+               new_plane_state->hwindex = idx;
+
+               group_free_planes[plane->group->index] &=
+                       ~rcar_du_plane_hwmask(new_plane_state);
+
+               dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
+                       __func__, plane->group->index,
+                       group_free_planes[plane->group->index]);
+       }
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Plane Setup
+ */
+
+#define RCAR_DU_COLORKEY_NONE          (0 << 24)
+#define RCAR_DU_COLORKEY_SOURCE                (1 << 24)
+#define RCAR_DU_COLORKEY_MASK          (1 << 24)
+
+static void rcar_du_plane_write(struct rcar_du_group *rgrp,
+                               unsigned int index, u32 reg, u32 data)
+{
+       rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
+                     data);
+}
+
+static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp,
+                                       const struct rcar_du_plane_state *state)
+{
+       unsigned int src_x = state->state.src.x1 >> 16;
+       unsigned int src_y = state->state.src.y1 >> 16;
+       unsigned int index = state->hwindex;
+       unsigned int pitch;
+       bool interlaced;
+       u32 dma[2];
+
+       interlaced = state->state.crtc->state->adjusted_mode.flags
+                  & DRM_MODE_FLAG_INTERLACE;
+
+       if (state->source == RCAR_DU_PLANE_MEMORY) {
+               struct drm_framebuffer *fb = state->state.fb;
+               struct drm_gem_dma_object *gem;
+               unsigned int i;
+
+               if (state->format->planes == 2)
+                       pitch = fb->pitches[0];
+               else
+                       pitch = fb->pitches[0] * 8 / state->format->bpp;
+
+               for (i = 0; i < state->format->planes; ++i) {
+                       gem = drm_fb_dma_get_gem_obj(fb, i);
+                       dma[i] = gem->dma_addr + fb->offsets[i];
+               }
+       } else {
+               pitch = drm_rect_width(&state->state.src) >> 16;
+               dma[0] = 0;
+               dma[1] = 0;
+       }
+
+       /*
+        * Memory pitch (expressed in pixels). Must be doubled for interlaced
+        * operation with 32bpp formats.
+        */
+       rcar_du_plane_write(rgrp, index, PnMWR,
+                           (interlaced && state->format->bpp == 32) ?
+                           pitch * 2 : pitch);
+
+       /*
+        * The Y position is expressed in raster line units and must be doubled
+        * for 32bpp formats, according to the R8A7790 datasheet. No mention of
+        * doubling the Y position is found in the R8A7779 datasheet, but the
+        * rule seems to apply there as well.
+        *
+        * Despite not being documented, doubling seem not to be needed when
+        * operating in interlaced mode.
+        *
+        * Similarly, for the second plane, NV12 and NV21 formats seem to
+        * require a halved Y position value, in both progressive and interlaced
+        * modes.
+        */
+       rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
+       rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
+                           (!interlaced && state->format->bpp == 32 ? 2 : 1));
+
+       rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);
+
+       if (state->format->planes == 2) {
+               index = (index + 1) % 8;
+
+               rcar_du_plane_write(rgrp, index, PnMWR, pitch);
+
+               rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
+               rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
+                                   (state->format->bpp == 16 ? 2 : 1) / 2);
+
+               rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);
+       }
+}
+
+static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,
+                                    unsigned int index,
+                                    const struct rcar_du_plane_state *state)
+{
+       u32 colorkey;
+       u32 pnmr;
+
+       /*
+        * The PnALPHAR register controls alpha-blending in 16bpp formats
+        * (ARGB1555 and XRGB1555).
+        *
+        * For ARGB, set the alpha value to 0, and enable alpha-blending when
+        * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
+        *
+        * For XRGB, set the alpha value to the plane-wide alpha value and
+        * enable alpha-blending regardless of the X bit value.
+        */
+       if (state->format->fourcc != DRM_FORMAT_XRGB1555)
+               rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
+       else
+               rcar_du_plane_write(rgrp, index, PnALPHAR,
+                                   PnALPHAR_ABIT_X | state->state.alpha >> 8);
+
+       pnmr = PnMR_BM_MD | state->format->pnmr;
+
+       /*
+        * Disable color keying when requested. YUV formats have the
+        * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
+        * automatically.
+        */
+       if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
+               pnmr |= PnMR_SPIM_TP_OFF;
+
+       /* For packed YUV formats we need to select the U/V order. */
+       if (state->format->fourcc == DRM_FORMAT_YUYV)
+               pnmr |= PnMR_YCDF_YUYV;
+
+       rcar_du_plane_write(rgrp, index, PnMR, pnmr);
+
+       switch (state->format->fourcc) {
+       case DRM_FORMAT_RGB565:
+               colorkey = ((state->colorkey & 0xf80000) >> 8)
+                        | ((state->colorkey & 0x00fc00) >> 5)
+                        | ((state->colorkey & 0x0000f8) >> 3);
+               rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
+               break;
+
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               colorkey = ((state->colorkey & 0xf80000) >> 9)
+                        | ((state->colorkey & 0x00f800) >> 6)
+                        | ((state->colorkey & 0x0000f8) >> 3);
+               rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
+               break;
+
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_ARGB8888:
+               rcar_du_plane_write(rgrp, index, PnTC3R,
+                                   PnTC3R_CODE | (state->colorkey & 0xffffff));
+               break;
+       }
+}
+
+static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp,
+                                           unsigned int index,
+                                           const struct rcar_du_plane_state *state)
+{
+       u32 ddcr2 = PnDDCR2_CODE;
+       u32 ddcr4;
+
+       /*
+        * Data format
+        *
+        * The data format is selected by the DDDF field in PnMR and the EDF
+        * field in DDCR4.
+        */
+
+       rcar_du_plane_setup_mode(rgrp, index, state);
+
+       if (state->format->planes == 2) {
+               if (state->hwindex != index) {
+                       if (state->format->fourcc == DRM_FORMAT_NV12 ||
+                           state->format->fourcc == DRM_FORMAT_NV21)
+                               ddcr2 |= PnDDCR2_Y420;
+
+                       if (state->format->fourcc == DRM_FORMAT_NV21)
+                               ddcr2 |= PnDDCR2_NV21;
+
+                       ddcr2 |= PnDDCR2_DIVU;
+               } else {
+                       ddcr2 |= PnDDCR2_DIVY;
+               }
+       }
+
+       rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
+
+       ddcr4 = state->format->edf | PnDDCR4_CODE;
+       if (state->source != RCAR_DU_PLANE_MEMORY)
+               ddcr4 |= PnDDCR4_VSPS;
+
+       rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
+}
+
+static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp,
+                                           unsigned int index,
+                                           const struct rcar_du_plane_state *state)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+       u32 pnmr = state->format->pnmr | PnMR_SPIM_TP_OFF;
+
+       if (rcdu->info->features & RCAR_DU_FEATURE_NO_BLENDING) {
+               /* No blending. ALP and EOR are not supported. */
+               pnmr &= ~(PnMR_SPIM_ALP | PnMR_SPIM_EOR);
+       }
+
+       rcar_du_plane_write(rgrp, index, PnMR, pnmr);
+
+       rcar_du_plane_write(rgrp, index, PnDDCR4,
+                           state->format->edf | PnDDCR4_CODE);
+
+       /*
+        * On Gen3, some DU channels have two planes, each being wired to a
+        * separate VSPD instance. The DU can then blend two planes. While
+        * this feature isn't used by the driver, issues related to alpha
+        * blending (such as incorrect colors or planes being invisible) may
+        * still occur if the PnALPHAR register has a stale value. Set the
+        * register to 0 to avoid this.
+        */
+
+       rcar_du_plane_write(rgrp, index, PnALPHAR, 0);
+}
+
+static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,
+                                      unsigned int index,
+                                      const struct rcar_du_plane_state *state)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+       const struct drm_rect *dst = &state->state.dst;
+
+       if (rcdu->info->gen < 3)
+               rcar_du_plane_setup_format_gen2(rgrp, index, state);
+       else
+               rcar_du_plane_setup_format_gen3(rgrp, index, state);
+
+       /* Destination position and size */
+       rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst));
+       rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst));
+       rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1);
+       rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1);
+
+       if (rcdu->info->gen < 3) {
+               /* Wrap-around and blinking, disabled */
+               rcar_du_plane_write(rgrp, index, PnWASPR, 0);
+               rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
+               rcar_du_plane_write(rgrp, index, PnBTR, 0);
+               rcar_du_plane_write(rgrp, index, PnMLR, 0);
+       }
+}
+
+void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
+                          const struct rcar_du_plane_state *state)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+
+       rcar_du_plane_setup_format(rgrp, state->hwindex, state);
+       if (state->format->planes == 2)
+               rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
+                                          state);
+
+       if (rcdu->info->gen >= 3)
+               return;
+
+       rcar_du_plane_setup_scanout(rgrp, state);
+
+       if (state->source == RCAR_DU_PLANE_VSPD1) {
+               unsigned int vspd1_sink = rgrp->index ? 2 : 0;
+
+               if (rcdu->vspd1_sink != vspd1_sink) {
+                       rcdu->vspd1_sink = vspd1_sink;
+                       rcar_du_set_dpad0_vsp1_routing(rcdu);
+
+                       /*
+                        * Changes to the VSP1 sink take effect on DRES and thus
+                        * need a restart of the group.
+                        */
+                       rgrp->need_restart = true;
+               }
+       }
+}
+
+int __rcar_du_plane_atomic_check(struct drm_plane *plane,
+                                struct drm_plane_state *state,
+                                const struct rcar_du_format_info **format)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_crtc_state *crtc_state;
+       int ret;
+
+       if (!state->crtc) {
+               /*
+                * The visible field is not reset by the DRM core but only
+                * updated by drm_plane_helper_check_state(), set it manually.
+                */
+               state->visible = false;
+               *format = NULL;
+               return 0;
+       }
+
+       crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+       if (IS_ERR(crtc_state))
+               return PTR_ERR(crtc_state);
+
+       ret = drm_atomic_helper_check_plane_state(state, crtc_state,
+                                                 DRM_PLANE_NO_SCALING,
+                                                 DRM_PLANE_NO_SCALING,
+                                                 true, true);
+       if (ret < 0)
+               return ret;
+
+       if (!state->visible) {
+               *format = NULL;
+               return 0;
+       }
+
+       *format = rcar_du_format_info(state->fb->format->format);
+       if (*format == NULL) {
+               dev_dbg(dev->dev, "%s: unsupported format %p4cc\n", __func__,
+                       &state->fb->format->format);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rcar_du_plane_atomic_check(struct drm_plane *plane,
+                                     struct drm_atomic_state *state)
+{
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+                                                                                plane);
+       struct rcar_du_plane_state *rstate = to_rcar_plane_state(new_plane_state);
+
+       return __rcar_du_plane_atomic_check(plane, new_plane_state,
+                                           &rstate->format);
+}
+
+static void rcar_du_plane_atomic_update(struct drm_plane *plane,
+                                       struct drm_atomic_state *state)
+{
+       struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+       struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
+       struct rcar_du_plane *rplane = to_rcar_plane(plane);
+       struct rcar_du_plane_state *old_rstate;
+       struct rcar_du_plane_state *new_rstate;
+
+       if (!new_state->visible)
+               return;
+
+       rcar_du_plane_setup(rplane);
+
+       /*
+        * Check whether the source has changed from memory to live source or
+        * from live source to memory. The source has been configured by the
+        * VSPS bit in the PnDDCR4 register. Although the datasheet states that
+        * the bit is updated during vertical blanking, it seems that updates
+        * only occur when the DU group is held in reset through the DSYSR.DRES
+        * bit. We thus need to restart the group if the source changes.
+        */
+       old_rstate = to_rcar_plane_state(old_state);
+       new_rstate = to_rcar_plane_state(new_state);
+
+       if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) !=
+           (new_rstate->source == RCAR_DU_PLANE_MEMORY))
+               rplane->group->need_restart = true;
+}
+
+static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
+       .atomic_check = rcar_du_plane_atomic_check,
+       .atomic_update = rcar_du_plane_atomic_update,
+};
+
+static struct drm_plane_state *
+rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+       struct rcar_du_plane_state *state;
+       struct rcar_du_plane_state *copy;
+
+       if (WARN_ON(!plane->state))
+               return NULL;
+
+       state = to_rcar_plane_state(plane->state);
+       copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+       if (copy == NULL)
+               return NULL;
+
+       __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
+
+       return &copy->state;
+}
+
+static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
+                                              struct drm_plane_state *state)
+{
+       __drm_atomic_helper_plane_destroy_state(state);
+       kfree(to_rcar_plane_state(state));
+}
+
+static void rcar_du_plane_reset(struct drm_plane *plane)
+{
+       struct rcar_du_plane_state *state;
+
+       if (plane->state) {
+               rcar_du_plane_atomic_destroy_state(plane, plane->state);
+               plane->state = NULL;
+       }
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return;
+
+       __drm_atomic_helper_plane_reset(plane, &state->state);
+
+       state->hwindex = -1;
+       state->source = RCAR_DU_PLANE_MEMORY;
+       state->colorkey = RCAR_DU_COLORKEY_NONE;
+}
+
+static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
+                                            struct drm_plane_state *state,
+                                            struct drm_property *property,
+                                            uint64_t val)
+{
+       struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
+       struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
+
+       if (property == rcdu->props.colorkey)
+               rstate->colorkey = val;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
+       const struct drm_plane_state *state, struct drm_property *property,
+       uint64_t *val)
+{
+       const struct rcar_du_plane_state *rstate =
+               container_of(state, const struct rcar_du_plane_state, state);
+       struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
+
+       if (property == rcdu->props.colorkey)
+               *val = rstate->colorkey;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct drm_plane_funcs rcar_du_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .reset = rcar_du_plane_reset,
+       .destroy = drm_plane_cleanup,
+       .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
+       .atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
+       .atomic_set_property = rcar_du_plane_atomic_set_property,
+       .atomic_get_property = rcar_du_plane_atomic_get_property,
+};
+
+static const uint32_t formats[] = {
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+};
+
+int rcar_du_planes_init(struct rcar_du_group *rgrp)
+{
+       struct rcar_du_device *rcdu = rgrp->dev;
+       unsigned int crtcs;
+       unsigned int i;
+       int ret;
+
+        /*
+         * Create one primary plane per CRTC in this group and seven overlay
+         * planes.
+         */
+       rgrp->num_planes = rgrp->num_crtcs + 7;
+
+       crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
+
+       for (i = 0; i < rgrp->num_planes; ++i) {
+               enum drm_plane_type type = i < rgrp->num_crtcs
+                                        ? DRM_PLANE_TYPE_PRIMARY
+                                        : DRM_PLANE_TYPE_OVERLAY;
+               struct rcar_du_plane *plane = &rgrp->planes[i];
+
+               plane->group = rgrp;
+
+               ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
+                                              crtcs, &rcar_du_plane_funcs,
+                                              formats, ARRAY_SIZE(formats),
+                                              NULL, type, NULL);
+               if (ret < 0)
+                       return ret;
+
+               drm_plane_helper_add(&plane->plane,
+                                    &rcar_du_plane_helper_funcs);
+
+               drm_plane_create_alpha_property(&plane->plane);
+
+               if (type == DRM_PLANE_TYPE_PRIMARY) {
+                       drm_plane_create_zpos_immutable_property(&plane->plane,
+                                                                0);
+               } else {
+                       drm_object_attach_property(&plane->plane.base,
+                                                  rcdu->props.colorkey,
+                                                  RCAR_DU_COLORKEY_NONE);
+                       drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
+               }
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h
new file mode 100644 (file)
index 0000000..f9893d7
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_PLANE_H__
+#define __RCAR_DU_PLANE_H__
+
+#include <drm/drm_plane.h>
+
+struct rcar_du_format_info;
+struct rcar_du_group;
+
+/*
+ * The RCAR DU has 8 hardware planes, shared between primary and overlay planes.
+ * As using overlay planes requires at least one of the CRTCs being enabled, no
+ * more than 7 overlay planes can be available. We thus create 1 primary plane
+ * per CRTC and 7 overlay planes, for a total of up to 9 KMS planes.
+ */
+#define RCAR_DU_NUM_KMS_PLANES         9
+#define RCAR_DU_NUM_HW_PLANES          8
+
+enum rcar_du_plane_source {
+       RCAR_DU_PLANE_MEMORY,
+       RCAR_DU_PLANE_VSPD0,
+       RCAR_DU_PLANE_VSPD1,
+};
+
+struct rcar_du_plane {
+       struct drm_plane plane;
+       struct rcar_du_group *group;
+};
+
+static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
+{
+       return container_of(plane, struct rcar_du_plane, plane);
+}
+
+/**
+ * struct rcar_du_plane_state - Driver-specific plane state
+ * @state: base DRM plane state
+ * @format: information about the pixel format used by the plane
+ * @hwindex: 0-based hardware plane index, -1 means unused
+ * @colorkey: value of the plane colorkey property
+ */
+struct rcar_du_plane_state {
+       struct drm_plane_state state;
+
+       const struct rcar_du_format_info *format;
+       int hwindex;
+       enum rcar_du_plane_source source;
+
+       unsigned int colorkey;
+};
+
+static inline struct rcar_du_plane_state *
+to_rcar_plane_state(struct drm_plane_state *state)
+{
+       return container_of(state, struct rcar_du_plane_state, state);
+}
+
+int rcar_du_atomic_check_planes(struct drm_device *dev,
+                               struct drm_atomic_state *state);
+
+int __rcar_du_plane_atomic_check(struct drm_plane *plane,
+                                struct drm_plane_state *state,
+                                const struct rcar_du_format_info **format);
+
+int rcar_du_planes_init(struct rcar_du_group *rgrp);
+
+void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
+                          const struct rcar_du_plane_state *state);
+
+static inline void rcar_du_plane_setup(struct rcar_du_plane *plane)
+{
+       struct rcar_du_plane_state *state =
+               to_rcar_plane_state(plane->plane.state);
+
+       return __rcar_du_plane_setup(plane->group, state);
+}
+
+#endif /* __RCAR_DU_PLANE_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_regs.h
new file mode 100644 (file)
index 0000000..391de66
--- /dev/null
@@ -0,0 +1,553 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R-Car Display Unit Registers Definitions
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_REGS_H__
+#define __RCAR_DU_REGS_H__
+
+#define DU0_REG_OFFSET         0x00000
+#define DU1_REG_OFFSET         0x30000
+#define DU2_REG_OFFSET         0x40000
+#define DU3_REG_OFFSET         0x70000
+
+/* -----------------------------------------------------------------------------
+ * Display Control Registers
+ */
+
+#define DSYSR                  0x00000 /* display 1 */
+#define DSYSR_ILTS             (1 << 29)
+#define DSYSR_DSEC             (1 << 20)
+#define DSYSR_IUPD             (1 << 16)
+#define DSYSR_DRES             (1 << 9)
+#define DSYSR_DEN              (1 << 8)
+#define DSYSR_TVM_MASTER       (0 << 6)
+#define DSYSR_TVM_SWITCH       (1 << 6)
+#define DSYSR_TVM_TVSYNC       (2 << 6)
+#define DSYSR_TVM_MASK         (3 << 6)
+#define DSYSR_SCM_INT_NONE     (0 << 4)
+#define DSYSR_SCM_INT_SYNC     (2 << 4)
+#define DSYSR_SCM_INT_VIDEO    (3 << 4)
+#define DSYSR_SCM_MASK         (3 << 4)
+
+#define DSMR                   0x00004
+#define DSMR_VSPM              (1 << 28)
+#define DSMR_ODPM              (1 << 27)
+#define DSMR_DIPM_DISP         (0 << 25)
+#define DSMR_DIPM_CSYNC                (1 << 25)
+#define DSMR_DIPM_DE           (3 << 25)
+#define DSMR_DIPM_MASK         (3 << 25)
+#define DSMR_CSPM              (1 << 24)
+#define DSMR_DIL               (1 << 19)
+#define DSMR_VSL               (1 << 18)
+#define DSMR_HSL               (1 << 17)
+#define DSMR_DDIS              (1 << 16)
+#define DSMR_CDEL              (1 << 15)
+#define DSMR_CDEM_CDE          (0 << 13)
+#define DSMR_CDEM_LOW          (2 << 13)
+#define DSMR_CDEM_HIGH         (3 << 13)
+#define DSMR_CDEM_MASK         (3 << 13)
+#define DSMR_CDED              (1 << 12)
+#define DSMR_ODEV              (1 << 8)
+#define DSMR_CSY_VH_OR         (0 << 6)
+#define DSMR_CSY_333           (2 << 6)
+#define DSMR_CSY_222           (3 << 6)
+#define DSMR_CSY_MASK          (3 << 6)
+
+#define DSSR                   0x00008
+#define DSSR_VC1FB_DSA0                (0 << 30)
+#define DSSR_VC1FB_DSA1                (1 << 30)
+#define DSSR_VC1FB_DSA2                (2 << 30)
+#define DSSR_VC1FB_INIT                (3 << 30)
+#define DSSR_VC1FB_MASK                (3 << 30)
+#define DSSR_VC0FB_DSA0                (0 << 28)
+#define DSSR_VC0FB_DSA1                (1 << 28)
+#define DSSR_VC0FB_DSA2                (2 << 28)
+#define DSSR_VC0FB_INIT                (3 << 28)
+#define DSSR_VC0FB_MASK                (3 << 28)
+#define DSSR_DFB(n)            (1 << ((n)+15))
+#define DSSR_TVR               (1 << 15)
+#define DSSR_FRM               (1 << 14)
+#define DSSR_VBK               (1 << 11)
+#define DSSR_RINT              (1 << 9)
+#define DSSR_HBK               (1 << 8)
+#define DSSR_ADC(n)            (1 << ((n)-1))
+
+#define DSRCR                  0x0000c
+#define DSRCR_TVCL             (1 << 15)
+#define DSRCR_FRCL             (1 << 14)
+#define DSRCR_VBCL             (1 << 11)
+#define DSRCR_RICL             (1 << 9)
+#define DSRCR_HBCL             (1 << 8)
+#define DSRCR_ADCL(n)          (1 << ((n)-1))
+#define DSRCR_MASK             0x0000cbff
+
+#define DIER                   0x00010
+#define DIER_TVE               (1 << 15)
+#define DIER_FRE               (1 << 14)
+#define DIER_VBE               (1 << 11)
+#define DIER_RIE               (1 << 9)
+#define DIER_HBE               (1 << 8)
+#define DIER_ADCE(n)           (1 << ((n)-1))
+
+#define CPCR                   0x00014
+#define CPCR_CP4CE             (1 << 19)
+#define CPCR_CP3CE             (1 << 18)
+#define CPCR_CP2CE             (1 << 17)
+#define CPCR_CP1CE             (1 << 16)
+
+#define DPPR                   0x00018
+#define DPPR_DPE(n)            (1 << ((n)*4-1))
+#define DPPR_DPS(n, p)         (((p)-1) << DPPR_DPS_SHIFT(n))
+#define DPPR_DPS_SHIFT(n)      (((n)-1)*4)
+#define DPPR_BPP16             (DPPR_DPE(8) | DPPR_DPS(8, 1))  /* plane1 */
+#define DPPR_BPP32_P1          (DPPR_DPE(7) | DPPR_DPS(7, 1))
+#define DPPR_BPP32_P2          (DPPR_DPE(8) | DPPR_DPS(8, 2))
+#define DPPR_BPP32             (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */
+
+#define DEFR                   0x00020
+#define DEFR_CODE              (0x7773 << 16)
+#define DEFR_EXSL              (1 << 12)
+#define DEFR_EXVL              (1 << 11)
+#define DEFR_EXUP              (1 << 5)
+#define DEFR_VCUP              (1 << 4)
+#define DEFR_DEFE              (1 << 0)
+
+#define DAPCR                  0x00024
+#define DAPCR_CODE             (0x7773 << 16)
+#define DAPCR_AP2E             (1 << 4)
+#define DAPCR_AP1E             (1 << 0)
+
+#define DCPCR                  0x00028
+#define DCPCR_CODE             (0x7773 << 16)
+#define DCPCR_CA2B             (1 << 13)
+#define DCPCR_CD2F             (1 << 12)
+#define DCPCR_DC2E             (1 << 8)
+#define DCPCR_CAB              (1 << 5)
+#define DCPCR_CDF              (1 << 4)
+#define DCPCR_DCE              (1 << 0)
+
+#define DEFR2                  0x00034
+#define DEFR2_CODE             (0x7775 << 16)
+#define DEFR2_DEFE2G           (1 << 0)
+
+#define DEFR3                  0x00038
+#define DEFR3_CODE             (0x7776 << 16)
+#define DEFR3_EVDA             (1 << 14)
+#define DEFR3_EVDM_1           (1 << 12)
+#define DEFR3_EVDM_2           (2 << 12)
+#define DEFR3_EVDM_3           (3 << 12)
+#define DEFR3_VMSM2_EMA                (1 << 6)
+#define DEFR3_VMSM1_ENA                (1 << 4)
+#define DEFR3_DEFE3            (1 << 0)
+
+#define DEFR4                  0x0003c
+#define DEFR4_CODE             (0x7777 << 16)
+#define DEFR4_LRUO             (1 << 5)
+#define DEFR4_SPCE             (1 << 4)
+
+#define DVCSR                  0x000d0
+#define DVCSR_VCnFB2_DSA0(n)   (0 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA1(n)   (1 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA2(n)   (2 << ((n)*2+16))
+#define DVCSR_VCnFB2_INIT(n)   (3 << ((n)*2+16))
+#define DVCSR_VCnFB2_MASK(n)   (3 << ((n)*2+16))
+#define DVCSR_VCnFB_DSA0(n)    (0 << ((n)*2))
+#define DVCSR_VCnFB_DSA1(n)    (1 << ((n)*2))
+#define DVCSR_VCnFB_DSA2(n)    (2 << ((n)*2))
+#define DVCSR_VCnFB_INIT(n)    (3 << ((n)*2))
+#define DVCSR_VCnFB_MASK(n)    (3 << ((n)*2))
+
+#define DEFR5                  0x000e0
+#define DEFR5_CODE             (0x66 << 24)
+#define DEFR5_YCRGB2_DIS       (0 << 14)
+#define DEFR5_YCRGB2_PRI1      (1 << 14)
+#define DEFR5_YCRGB2_PRI2      (2 << 14)
+#define DEFR5_YCRGB2_PRI3      (3 << 14)
+#define DEFR5_YCRGB2_MASK      (3 << 14)
+#define DEFR5_YCRGB1_DIS       (0 << 12)
+#define DEFR5_YCRGB1_PRI1      (1 << 12)
+#define DEFR5_YCRGB1_PRI2      (2 << 12)
+#define DEFR5_YCRGB1_PRI3      (3 << 12)
+#define DEFR5_YCRGB1_MASK      (3 << 12)
+#define DEFR5_DEFE5            (1 << 0)
+
+#define DDLTR                  0x000e4
+#define DDLTR_CODE             (0x7766 << 16)
+#define DDLTR_DLAR2            (1 << 6)
+#define DDLTR_DLAY2            (1 << 5)
+#define DDLTR_DLAY1            (1 << 1)
+
+#define DEFR6                  0x000e8
+#define DEFR6_CODE             (0x7778 << 16)
+#define DEFR6_ODPM12_DSMR      (0 << 10)
+#define DEFR6_ODPM12_DISP      (2 << 10)
+#define DEFR6_ODPM12_CDE       (3 << 10)
+#define DEFR6_ODPM12_MASK      (3 << 10)
+#define DEFR6_ODPM02_DSMR      (0 << 8)
+#define DEFR6_ODPM02_DISP      (2 << 8)
+#define DEFR6_ODPM02_CDE       (3 << 8)
+#define DEFR6_ODPM02_MASK      (3 << 8)
+#define DEFR6_TCNE1            (1 << 6)
+#define DEFR6_TCNE0            (1 << 4)
+#define DEFR6_MLOS1            (1 << 2)
+#define DEFR6_DEFAULT          (DEFR6_CODE | DEFR6_TCNE1)
+
+#define DEFR7                  0x000ec
+#define DEFR7_CODE             (0x7779 << 16)
+#define DEFR7_CMME1            BIT(6)
+#define DEFR7_CMME0            BIT(4)
+
+/* -----------------------------------------------------------------------------
+ * R8A7790-only Control Registers
+ */
+
+#define DD1SSR                 0x20008
+#define DD1SSR_TVR             (1 << 15)
+#define DD1SSR_FRM             (1 << 14)
+#define DD1SSR_BUF             (1 << 12)
+#define DD1SSR_VBK             (1 << 11)
+#define DD1SSR_RINT            (1 << 9)
+#define DD1SSR_HBK             (1 << 8)
+#define DD1SSR_ADC(n)          (1 << ((n)-1))
+
+#define DD1SRCR                        0x2000c
+#define DD1SRCR_TVR            (1 << 15)
+#define DD1SRCR_FRM            (1 << 14)
+#define DD1SRCR_BUF            (1 << 12)
+#define DD1SRCR_VBK            (1 << 11)
+#define DD1SRCR_RINT           (1 << 9)
+#define DD1SRCR_HBK            (1 << 8)
+#define DD1SRCR_ADC(n)         (1 << ((n)-1))
+
+#define DD1IER                 0x20010
+#define DD1IER_TVR             (1 << 15)
+#define DD1IER_FRM             (1 << 14)
+#define DD1IER_BUF             (1 << 12)
+#define DD1IER_VBK             (1 << 11)
+#define DD1IER_RINT            (1 << 9)
+#define DD1IER_HBK             (1 << 8)
+#define DD1IER_ADC(n)          (1 << ((n)-1))
+
+#define DEFR8                  0x20020
+#define DEFR8_CODE             (0x7790 << 16)
+#define DEFR8_VSCS             (1 << 6)
+#define DEFR8_DRGBS_DU(n)      ((n) << 4)
+#define DEFR8_DRGBS_MASK       (3 << 4)
+#define DEFR8_DEFE8            (1 << 0)
+
+#define DOFLR                  0x20024
+#define DOFLR_CODE             (0x7790 << 16)
+#define DOFLR_HSYCFL1          (1 << 13)
+#define DOFLR_VSYCFL1          (1 << 12)
+#define DOFLR_ODDFL1           (1 << 11)
+#define DOFLR_DISPFL1          (1 << 10)
+#define DOFLR_CDEFL1           (1 << 9)
+#define DOFLR_RGBFL1           (1 << 8)
+#define DOFLR_HSYCFL0          (1 << 5)
+#define DOFLR_VSYCFL0          (1 << 4)
+#define DOFLR_ODDFL0           (1 << 3)
+#define DOFLR_DISPFL0          (1 << 2)
+#define DOFLR_CDEFL0           (1 << 1)
+#define DOFLR_RGBFL0           (1 << 0)
+
+#define DIDSR                  0x20028
+#define DIDSR_CODE             (0x7790 << 16)
+#define DIDSR_LDCS_DCLKIN(n)   (0 << (8 + (n) * 2))
+#define DIDSR_LDCS_DSI(n)      (2 << (8 + (n) * 2))    /* V3U only */
+#define DIDSR_LDCS_LVDS0(n)    (2 << (8 + (n) * 2))
+#define DIDSR_LDCS_LVDS1(n)    (3 << (8 + (n) * 2))
+#define DIDSR_LDCS_MASK(n)     (3 << (8 + (n) * 2))
+#define DIDSR_PDCS_CLK(n, clk) (clk << ((n) * 2))
+#define DIDSR_PDCS_MASK(n)     (3 << ((n) * 2))
+
+#define DEFR10                 0x20038
+#define DEFR10_CODE            (0x7795 << 16)
+#define DEFR10_VSPF1_RGB       (0 << 14)
+#define DEFR10_VSPF1_YC                (1 << 14)
+#define DEFR10_DOCF1_RGB       (0 << 12)
+#define DEFR10_DOCF1_YC                (1 << 12)
+#define DEFR10_YCDF0_YCBCR444  (0 << 11)
+#define DEFR10_YCDF0_YCBCR422  (1 << 11)
+#define DEFR10_VSPF0_RGB       (0 << 10)
+#define DEFR10_VSPF0_YC                (1 << 10)
+#define DEFR10_DOCF0_RGB       (0 << 8)
+#define DEFR10_DOCF0_YC                (1 << 8)
+#define DEFR10_TSEL_H3_TCON1   (0 << 1) /* DEFR102 register only (DU2/DU3) */
+#define DEFR10_DEFE10          (1 << 0)
+
+#define DPLLCR                 0x20044
+#define DPLLCR_CODE            (0x95 << 24)
+#define DPLLCR_PLCS1           (1 << 23)
+#define DPLLCR_PLCS0           (1 << 21)
+#define DPLLCR_CLKE            (1 << 18)
+#define DPLLCR_FDPLL(n)                ((n) << 12)
+#define DPLLCR_N(n)            ((n) << 5)
+#define DPLLCR_M(n)            ((n) << 3)
+#define DPLLCR_STBY            (1 << 2)
+#define DPLLCR_INCS_DOTCLKIN0  (0 << 0)
+#define DPLLCR_INCS_DOTCLKIN1  (1 << 1)
+
+#define DPLLC2R                        0x20048
+#define DPLLC2R_CODE           (0x95 << 24)
+#define DPLLC2R_SELC           (1 << 12)
+#define DPLLC2R_M(n)           ((n) << 8)
+#define DPLLC2R_FDPLL(n)       ((n) << 0)
+
+/* -----------------------------------------------------------------------------
+ * Display Timing Generation Registers
+ */
+
+#define HDSR                   0x00040
+#define HDER                   0x00044
+#define VDSR                   0x00048
+#define VDER                   0x0004c
+#define HCR                    0x00050
+#define HSWR                   0x00054
+#define VCR                    0x00058
+#define VSPR                   0x0005c
+#define EQWR                   0x00060
+#define SPWR                   0x00064
+#define CLAMPSR                        0x00070
+#define CLAMPWR                        0x00074
+#define DESR                   0x00078
+#define DEWR                   0x0007c
+
+/* -----------------------------------------------------------------------------
+ * Display Attribute Registers
+ */
+
+#define CP1TR                  0x00080
+#define CP2TR                  0x00084
+#define CP3TR                  0x00088
+#define CP4TR                  0x0008c
+
+#define DOOR                   0x00090
+#define DOOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+#define CDER                   0x00094
+#define CDER_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+#define BPOR                   0x00098
+#define BPOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+
+#define RINTOFSR               0x0009c
+
+#define DSHPR                  0x000c8
+#define DSHPR_CODE             (0x7776 << 16)
+#define DSHPR_PRIH             (0xa << 4)
+#define DSHPR_PRIL_BPP16       (0x8 << 0)
+#define DSHPR_PRIL_BPP32       (0x9 << 0)
+
+/* -----------------------------------------------------------------------------
+ * Display Plane Registers
+ */
+
+#define PLANE_OFF              0x00100
+
+#define PnMR                   0x00100 /* plane 1 */
+#define PnMR_VISL_VIN0         (0 << 26)       /* use Video Input 0 */
+#define PnMR_VISL_VIN1         (1 << 26)       /* use Video Input 1 */
+#define PnMR_VISL_VIN2         (2 << 26)       /* use Video Input 2 */
+#define PnMR_VISL_VIN3         (3 << 26)       /* use Video Input 3 */
+#define PnMR_YCDF_YUYV         (1 << 20)       /* YUYV format */
+#define PnMR_TC_R              (0 << 17)       /* Tranparent color is PnTC1R */
+#define PnMR_TC_CP             (1 << 17)       /* Tranparent color is color palette */
+#define PnMR_WAE               (1 << 16)       /* Wrap around Enable */
+#define PnMR_SPIM_TP           (0 << 12)       /* Transparent Color */
+#define PnMR_SPIM_ALP          (1 << 12)       /* Alpha Blending */
+#define PnMR_SPIM_EOR          (2 << 12)       /* EOR */
+#define PnMR_SPIM_TP_OFF       (1 << 14)       /* No Transparent Color */
+#define PnMR_CPSL_CP1          (0 << 8)        /* Color Palette selected 1 */
+#define PnMR_CPSL_CP2          (1 << 8)        /* Color Palette selected 2 */
+#define PnMR_CPSL_CP3          (2 << 8)        /* Color Palette selected 3 */
+#define PnMR_CPSL_CP4          (3 << 8)        /* Color Palette selected 4 */
+#define PnMR_DC                        (1 << 7)        /* Display Area Change */
+#define PnMR_BM_MD             (0 << 4)        /* Manual Display Change Mode */
+#define PnMR_BM_AR             (1 << 4)        /* Auto Rendering Mode */
+#define PnMR_BM_AD             (2 << 4)        /* Auto Display Change Mode */
+#define PnMR_BM_VC             (3 << 4)        /* Video Capture Mode */
+#define PnMR_DDDF_8BPP         (0 << 0)        /* 8bit */
+#define PnMR_DDDF_16BPP                (1 << 0)        /* 16bit or 32bit */
+#define PnMR_DDDF_ARGB         (2 << 0)        /* ARGB */
+#define PnMR_DDDF_YC           (3 << 0)        /* YC */
+#define PnMR_DDDF_MASK         (3 << 0)
+
+#define PnMWR                  0x00104
+
+#define PnALPHAR               0x00108
+#define PnALPHAR_ABIT_1                (0 << 12)
+#define PnALPHAR_ABIT_0                (1 << 12)
+#define PnALPHAR_ABIT_X                (2 << 12)
+
+#define PnDSXR                 0x00110
+#define PnDSYR                 0x00114
+#define PnDPXR                 0x00118
+#define PnDPYR                 0x0011c
+
+#define PnDSA0R                        0x00120
+#define PnDSA1R                        0x00124
+#define PnDSA2R                        0x00128
+#define PnDSA_MASK             0xfffffff0
+
+#define PnSPXR                 0x00130
+#define PnSPYR                 0x00134
+#define PnWASPR                        0x00138
+#define PnWAMWR                        0x0013c
+
+#define PnBTR                  0x00140
+
+#define PnTC1R                 0x00144
+#define PnTC2R                 0x00148
+#define PnTC3R                 0x0014c
+#define PnTC3R_CODE            (0x66 << 24)
+
+#define PnMLR                  0x00150
+
+#define PnSWAPR                        0x00180
+#define PnSWAPR_DIGN           (1 << 4)
+#define PnSWAPR_SPQW           (1 << 3)
+#define PnSWAPR_SPLW           (1 << 2)
+#define PnSWAPR_SPWD           (1 << 1)
+#define PnSWAPR_SPBY           (1 << 0)
+
+#define PnDDCR                 0x00184
+#define PnDDCR_CODE            (0x7775 << 16)
+#define PnDDCR_LRGB1           (1 << 11)
+#define PnDDCR_LRGB0           (1 << 10)
+
+#define PnDDCR2                        0x00188
+#define PnDDCR2_CODE           (0x7776 << 16)
+#define PnDDCR2_NV21           (1 << 5)
+#define PnDDCR2_Y420           (1 << 4)
+#define PnDDCR2_DIVU           (1 << 1)
+#define PnDDCR2_DIVY           (1 << 0)
+
+#define PnDDCR4                        0x00190
+#define PnDDCR4_CODE           (0x7766 << 16)
+#define PnDDCR4_VSPS           (1 << 13)
+#define PnDDCR4_SDFS_RGB       (0 << 4)
+#define PnDDCR4_SDFS_YC                (5 << 4)
+#define PnDDCR4_SDFS_MASK      (7 << 4)
+#define PnDDCR4_EDF_NONE       (0 << 0)
+#define PnDDCR4_EDF_ARGB8888   (1 << 0)
+#define PnDDCR4_EDF_RGB888     (2 << 0)
+#define PnDDCR4_EDF_RGB666     (3 << 0)
+#define PnDDCR4_EDF_MASK       (7 << 0)
+
+#define APnMR                  0x0a100
+#define APnMR_WAE              (1 << 16)       /* Wrap around Enable */
+#define APnMR_DC               (1 << 7)        /* Display Area Change */
+#define APnMR_BM_MD            (0 << 4)        /* Manual Display Change Mode */
+#define APnMR_BM_AD            (2 << 4)        /* Auto Display Change Mode */
+
+#define APnMWR                 0x0a104
+
+#define APnDSXR                        0x0a110
+#define APnDSYR                        0x0a114
+#define APnDPXR                        0x0a118
+#define APnDPYR                        0x0a11c
+
+#define APnDSA0R               0x0a120
+#define APnDSA1R               0x0a124
+#define APnDSA2R               0x0a128
+
+#define APnSPXR                        0x0a130
+#define APnSPYR                        0x0a134
+#define APnWASPR               0x0a138
+#define APnWAMWR               0x0a13c
+
+#define APnBTR                 0x0a140
+
+#define APnMLR                 0x0a150
+#define APnSWAPR               0x0a180
+
+/* -----------------------------------------------------------------------------
+ * Display Capture Registers
+ */
+
+#define DCMR                   0x0c100
+#define DCMWR                  0x0c104
+#define DCSAR                  0x0c120
+#define DCMLR                  0x0c150
+
+/* -----------------------------------------------------------------------------
+ * Color Palette Registers
+ */
+
+#define CP1_000R               0x01000
+#define CP1_255R               0x013fc
+#define CP2_000R               0x02000
+#define CP2_255R               0x023fc
+#define CP3_000R               0x03000
+#define CP3_255R               0x033fc
+#define CP4_000R               0x04000
+#define CP4_255R               0x043fc
+
+/* -----------------------------------------------------------------------------
+ * External Synchronization Control Registers
+ */
+
+#define ESCR02                 0x10000
+#define ESCR13                 0x01000
+#define ESCR_DCLKOINV          (1 << 25)
+#define ESCR_DCLKSEL_DCLKIN    (0 << 20)
+#define ESCR_DCLKSEL_CLKS      (1 << 20)
+#define ESCR_DCLKSEL_MASK      (1 << 20)
+#define ESCR_DCLKDIS           (1 << 16)
+#define ESCR_SYNCSEL_OFF       (0 << 8)
+#define ESCR_SYNCSEL_EXVSYNC   (2 << 8)
+#define ESCR_SYNCSEL_EXHSYNC   (3 << 8)
+#define ESCR_FRQSEL_MASK       (0x3f << 0)
+
+#define OTAR02                 0x10004
+#define OTAR13                 0x01004
+
+/* -----------------------------------------------------------------------------
+ * Dual Display Output Control Registers
+ */
+
+#define DORCR                  0x11000
+#define DORCR_PG1T             (1 << 30)
+#define DORCR_DK1S             (1 << 28)
+#define DORCR_PG1D_DS0         (0 << 24)
+#define DORCR_PG1D_DS1         (1 << 24)
+#define DORCR_PG1D_FIX0                (2 << 24)
+#define DORCR_PG1D_DOOR                (3 << 24)
+#define DORCR_PG1D_MASK                (3 << 24)
+#define DORCR_DR0D             (1 << 21)
+#define DORCR_PG0D_DS0         (0 << 16)
+#define DORCR_PG0D_DS1         (1 << 16)
+#define DORCR_PG0D_FIX0                (2 << 16)
+#define DORCR_PG0D_DOOR                (3 << 16)
+#define DORCR_PG0D_MASK                (3 << 16)
+#define DORCR_RGPV             (1 << 4)
+#define DORCR_DPRS             (1 << 0)
+
+#define DPTSR                  0x11004
+#define DPTSR_PnDK(n)          (1 << ((n) + 16))
+#define DPTSR_PnTS(n)          (1 << (n))
+
+#define DAPTSR                 0x11008
+#define DAPTSR_APnDK(n)                (1 << ((n) + 16))
+#define DAPTSR_APnTS(n)                (1 << (n))
+
+#define DS1PR                  0x11020
+#define DS2PR                  0x11024
+
+/* -----------------------------------------------------------------------------
+ * YC-RGB Conversion Coefficient Registers
+ */
+
+#define YNCR                   0x11080
+#define YNOR                   0x11084
+#define CRNOR                  0x11088
+#define CBNOR                  0x1108c
+#define RCRCR                  0x11090
+#define GCRCR                  0x11094
+#define GCBCR                  0x11098
+#define BCBCR                  0x1109c
+
+#endif /* __RCAR_DU_REGS_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c
new file mode 100644 (file)
index 0000000..45c05d0
--- /dev/null
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Display Unit VSP-Based Compositor
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_vblank.h>
+
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_platform.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/vsp1.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_vsp.h"
+#include "rcar_du_writeback.h"
+
+static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
+{
+       struct rcar_du_crtc *crtc = private;
+
+       if (crtc->vblank_enable)
+               drm_crtc_handle_vblank(&crtc->crtc);
+
+       if (status & VSP1_DU_STATUS_COMPLETE)
+               rcar_du_crtc_finish_page_flip(crtc);
+       if (status & VSP1_DU_STATUS_WRITEBACK)
+               rcar_du_writeback_complete(crtc);
+
+       drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
+}
+
+void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
+{
+       const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
+       struct rcar_du_device *rcdu = crtc->dev;
+       struct vsp1_du_lif_config cfg = {
+               .width = mode->hdisplay,
+               .height = mode->vdisplay,
+               .interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE,
+               .callback = rcar_du_vsp_complete,
+               .callback_data = crtc,
+       };
+       struct rcar_du_plane_state state = {
+               .state = {
+                       .alpha = DRM_BLEND_ALPHA_OPAQUE,
+                       .crtc = &crtc->crtc,
+                       .dst.x1 = 0,
+                       .dst.y1 = 0,
+                       .dst.x2 = mode->hdisplay,
+                       .dst.y2 = mode->vdisplay,
+                       .src.x1 = 0,
+                       .src.y1 = 0,
+                       .src.x2 = mode->hdisplay << 16,
+                       .src.y2 = mode->vdisplay << 16,
+                       .zpos = 0,
+               },
+               .format = rcar_du_format_info(DRM_FORMAT_XRGB8888),
+               .source = RCAR_DU_PLANE_VSPD1,
+               .colorkey = 0,
+       };
+
+       if (rcdu->info->gen >= 3)
+               state.hwindex = (crtc->index % 2) ? 2 : 0;
+       else
+               state.hwindex = crtc->index % 2;
+
+       __rcar_du_plane_setup(crtc->group, &state);
+
+       vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
+}
+
+void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
+{
+       vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
+}
+
+void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
+{
+       vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
+}
+
+void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
+{
+       struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
+       struct rcar_du_crtc_state *state;
+
+       state = to_rcar_crtc_state(crtc->crtc.state);
+       cfg.crc = state->crc;
+
+       rcar_du_writeback_setup(crtc, &cfg.writeback);
+
+       vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
+}
+
+static const u32 rcar_du_vsp_formats[] = {
+       DRM_FORMAT_RGB332,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_BGR888,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_BGRA8888,
+       DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+       DRM_FORMAT_NV61,
+       DRM_FORMAT_YUV420,
+       DRM_FORMAT_YVU420,
+       DRM_FORMAT_YUV422,
+       DRM_FORMAT_YVU422,
+       DRM_FORMAT_YUV444,
+       DRM_FORMAT_YVU444,
+};
+
+/*
+ * Gen4 supports the same formats as above, and additionally 2-10-10-10 RGB
+ * formats and Y210 & Y212 formats.
+ */
+static const u32 rcar_du_vsp_formats_gen4[] = {
+       DRM_FORMAT_RGB332,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_BGR888,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_BGRA8888,
+       DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_RGBX1010102,
+       DRM_FORMAT_RGBA1010102,
+       DRM_FORMAT_ARGB2101010,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+       DRM_FORMAT_NV61,
+       DRM_FORMAT_YUV420,
+       DRM_FORMAT_YVU420,
+       DRM_FORMAT_YUV422,
+       DRM_FORMAT_YVU422,
+       DRM_FORMAT_YUV444,
+       DRM_FORMAT_YVU444,
+       DRM_FORMAT_Y210,
+       DRM_FORMAT_Y212,
+};
+
+static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
+{
+       struct rcar_du_vsp_plane_state *state =
+               to_rcar_vsp_plane_state(plane->plane.state);
+       struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
+       struct drm_framebuffer *fb = plane->plane.state->fb;
+       const struct rcar_du_format_info *format;
+       struct vsp1_du_atomic_config cfg = {
+               .pixelformat = 0,
+               .pitch = fb->pitches[0],
+               .alpha = state->state.alpha >> 8,
+               .zpos = state->state.zpos,
+       };
+       u32 fourcc = state->format->fourcc;
+       unsigned int i;
+
+       cfg.src.left = state->state.src.x1 >> 16;
+       cfg.src.top = state->state.src.y1 >> 16;
+       cfg.src.width = drm_rect_width(&state->state.src) >> 16;
+       cfg.src.height = drm_rect_height(&state->state.src) >> 16;
+
+       cfg.dst.left = state->state.dst.x1;
+       cfg.dst.top = state->state.dst.y1;
+       cfg.dst.width = drm_rect_width(&state->state.dst);
+       cfg.dst.height = drm_rect_height(&state->state.dst);
+
+       for (i = 0; i < state->format->planes; ++i)
+               cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
+                          + fb->offsets[i];
+
+       if (state->state.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE) {
+               switch (fourcc) {
+               case DRM_FORMAT_ARGB1555:
+                       fourcc = DRM_FORMAT_XRGB1555;
+                       break;
+
+               case DRM_FORMAT_ARGB4444:
+                       fourcc = DRM_FORMAT_XRGB4444;
+                       break;
+
+               case DRM_FORMAT_ARGB8888:
+                       fourcc = DRM_FORMAT_XRGB8888;
+                       break;
+               }
+       }
+
+       format = rcar_du_format_info(fourcc);
+       cfg.pixelformat = format->v4l2;
+
+       cfg.premult = state->state.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI;
+
+       vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
+                             plane->index, &cfg);
+}
+
+int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+                      struct sg_table sg_tables[3])
+{
+       struct rcar_du_device *rcdu = vsp->dev;
+       unsigned int i, j;
+       int ret;
+
+       for (i = 0; i < fb->format->num_planes; ++i) {
+               struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);
+               struct sg_table *sgt = &sg_tables[i];
+
+               if (gem->sgt) {
+                       struct scatterlist *src;
+                       struct scatterlist *dst;
+
+                       /*
+                        * If the GEM buffer has a scatter gather table, it has
+                        * been imported from a dma-buf and has no physical
+                        * address as it might not be physically contiguous.
+                        * Copy the original scatter gather table to map it to
+                        * the VSP.
+                        */
+                       ret = sg_alloc_table(sgt, gem->sgt->orig_nents,
+                                            GFP_KERNEL);
+                       if (ret)
+                               goto fail;
+
+                       src = gem->sgt->sgl;
+                       dst = sgt->sgl;
+                       for (j = 0; j < gem->sgt->orig_nents; ++j) {
+                               sg_set_page(dst, sg_page(src), src->length,
+                                           src->offset);
+                               src = sg_next(src);
+                               dst = sg_next(dst);
+                       }
+               } else {
+                       ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr,
+                                             gem->dma_addr, gem->base.size);
+                       if (ret)
+                               goto fail;
+               }
+
+               ret = vsp1_du_map_sg(vsp->vsp, sgt);
+               if (ret) {
+                       sg_free_table(sgt);
+                       goto fail;
+               }
+       }
+
+       return 0;
+
+fail:
+       while (i--) {
+               struct sg_table *sgt = &sg_tables[i];
+
+               vsp1_du_unmap_sg(vsp->vsp, sgt);
+               sg_free_table(sgt);
+       }
+
+       return ret;
+}
+
+static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
+                                       struct drm_plane_state *state)
+{
+       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
+       struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
+       int ret;
+
+       /*
+        * There's no need to prepare (and unprepare) the framebuffer when the
+        * plane is not visible, as it will not be displayed.
+        */
+       if (!state->visible)
+               return 0;
+
+       ret = rcar_du_vsp_map_fb(vsp, state->fb, rstate->sg_tables);
+       if (ret < 0)
+               return ret;
+
+       return drm_gem_plane_helper_prepare_fb(plane, state);
+}
+
+void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+                         struct sg_table sg_tables[3])
+{
+       unsigned int i;
+
+       for (i = 0; i < fb->format->num_planes; ++i) {
+               struct sg_table *sgt = &sg_tables[i];
+
+               vsp1_du_unmap_sg(vsp->vsp, sgt);
+               sg_free_table(sgt);
+       }
+}
+
+static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
+                                        struct drm_plane_state *state)
+{
+       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
+       struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
+
+       if (!state->visible)
+               return;
+
+       rcar_du_vsp_unmap_fb(vsp, state->fb, rstate->sg_tables);
+}
+
+static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
+                                         struct drm_atomic_state *state)
+{
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+                                                                                plane);
+       struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(new_plane_state);
+
+       return __rcar_du_plane_atomic_check(plane, new_plane_state,
+                                           &rstate->format);
+}
+
+static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
+                                       struct drm_atomic_state *state)
+{
+       struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+       struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
+       struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
+       struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
+
+       if (new_state->visible)
+               rcar_du_vsp_plane_setup(rplane);
+       else if (old_state->crtc)
+               vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
+                                     rplane->index, NULL);
+}
+
+static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
+       .prepare_fb = rcar_du_vsp_plane_prepare_fb,
+       .cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
+       .atomic_check = rcar_du_vsp_plane_atomic_check,
+       .atomic_update = rcar_du_vsp_plane_atomic_update,
+};
+
+static struct drm_plane_state *
+rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+       struct rcar_du_vsp_plane_state *copy;
+
+       if (WARN_ON(!plane->state))
+               return NULL;
+
+       copy = kzalloc(sizeof(*copy), GFP_KERNEL);
+       if (copy == NULL)
+               return NULL;
+
+       __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
+
+       return &copy->state;
+}
+
+static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
+                                                  struct drm_plane_state *state)
+{
+       __drm_atomic_helper_plane_destroy_state(state);
+       kfree(to_rcar_vsp_plane_state(state));
+}
+
+static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
+{
+       struct rcar_du_vsp_plane_state *state;
+
+       if (plane->state) {
+               rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
+               plane->state = NULL;
+       }
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return;
+
+       __drm_atomic_helper_plane_reset(plane, &state->state);
+}
+
+static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .reset = rcar_du_vsp_plane_reset,
+       .destroy = drm_plane_cleanup,
+       .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
+       .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
+};
+
+static void rcar_du_vsp_cleanup(struct drm_device *dev, void *res)
+{
+       struct rcar_du_vsp *vsp = res;
+       unsigned int i;
+
+       for (i = 0; i < vsp->num_planes; ++i) {
+               struct rcar_du_vsp_plane *plane = &vsp->planes[i];
+
+               drm_plane_cleanup(&plane->plane);
+       }
+
+       kfree(vsp->planes);
+
+       put_device(vsp->vsp);
+}
+
+int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
+                    unsigned int crtcs)
+{
+       struct rcar_du_device *rcdu = vsp->dev;
+       struct platform_device *pdev;
+       unsigned int num_crtcs = hweight32(crtcs);
+       unsigned int num_planes;
+       unsigned int i;
+       int ret;
+
+       /* Find the VSP device and initialize it. */
+       pdev = of_find_device_by_node(np);
+       if (!pdev)
+               return -ENXIO;
+
+       vsp->vsp = &pdev->dev;
+
+       ret = drmm_add_action_or_reset(&rcdu->ddev, rcar_du_vsp_cleanup, vsp);
+       if (ret < 0)
+               return ret;
+
+       ret = vsp1_du_init(vsp->vsp);
+       if (ret < 0)
+               return ret;
+
+       num_planes = rcdu->info->num_rpf;
+
+       vsp->planes = kcalloc(num_planes, sizeof(*vsp->planes), GFP_KERNEL);
+       if (!vsp->planes)
+               return -ENOMEM;
+
+       for (i = 0; i < num_planes; ++i) {
+               enum drm_plane_type type = i < num_crtcs
+                                        ? DRM_PLANE_TYPE_PRIMARY
+                                        : DRM_PLANE_TYPE_OVERLAY;
+               struct rcar_du_vsp_plane *plane = &vsp->planes[i];
+               unsigned int num_formats;
+               const u32 *formats;
+
+               if (rcdu->info->gen < 4) {
+                       num_formats = ARRAY_SIZE(rcar_du_vsp_formats);
+                       formats = rcar_du_vsp_formats;
+               } else {
+                       num_formats = ARRAY_SIZE(rcar_du_vsp_formats_gen4);
+                       formats = rcar_du_vsp_formats_gen4;
+               }
+
+               plane->vsp = vsp;
+               plane->index = i;
+
+               ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
+                                              crtcs, &rcar_du_vsp_plane_funcs,
+                                              formats, num_formats,
+                                              NULL, type, NULL);
+               if (ret < 0)
+                       return ret;
+
+               drm_plane_helper_add(&plane->plane,
+                                    &rcar_du_vsp_plane_helper_funcs);
+
+               drm_plane_create_alpha_property(&plane->plane);
+               drm_plane_create_zpos_property(&plane->plane, i, 0,
+                                              num_planes - 1);
+
+               drm_plane_create_blend_mode_property(&plane->plane,
+                                       BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                                       BIT(DRM_MODE_BLEND_PREMULTI) |
+                                       BIT(DRM_MODE_BLEND_COVERAGE));
+
+               vsp->num_planes++;
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.h
new file mode 100644 (file)
index 0000000..67630f0
--- /dev/null
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit VSP-Based Compositor
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_DU_VSP_H__
+#define __RCAR_DU_VSP_H__
+
+#include <drm/drm_plane.h>
+
+struct drm_framebuffer;
+struct rcar_du_format_info;
+struct rcar_du_vsp;
+struct sg_table;
+
+struct rcar_du_vsp_plane {
+       struct drm_plane plane;
+       struct rcar_du_vsp *vsp;
+       unsigned int index;
+};
+
+struct rcar_du_vsp {
+       unsigned int index;
+       struct device *vsp;
+       struct rcar_du_device *dev;
+       struct rcar_du_vsp_plane *planes;
+       unsigned int num_planes;
+};
+
+static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
+{
+       return container_of(p, struct rcar_du_vsp_plane, plane);
+}
+
+/**
+ * struct rcar_du_vsp_plane_state - Driver-specific plane state
+ * @state: base DRM plane state
+ * @format: information about the pixel format used by the plane
+ * @sg_tables: scatter-gather tables for the frame buffer memory
+ */
+struct rcar_du_vsp_plane_state {
+       struct drm_plane_state state;
+
+       const struct rcar_du_format_info *format;
+       struct sg_table sg_tables[3];
+};
+
+static inline struct rcar_du_vsp_plane_state *
+to_rcar_vsp_plane_state(struct drm_plane_state *state)
+{
+       return container_of(state, struct rcar_du_vsp_plane_state, state);
+}
+
+#ifdef CONFIG_DRM_RCAR_VSP
+int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
+                    unsigned int crtcs);
+void rcar_du_vsp_enable(struct rcar_du_crtc *crtc);
+void rcar_du_vsp_disable(struct rcar_du_crtc *crtc);
+void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc);
+void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc);
+int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+                      struct sg_table sg_tables[3]);
+void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
+                         struct sg_table sg_tables[3]);
+#else
+static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp,
+                                  struct device_node *np,
+                                  unsigned int crtcs)
+{
+       return -ENXIO;
+}
+static inline void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) { };
+static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { };
+static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { };
+static inline void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) { };
+static inline int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp,
+                                    struct drm_framebuffer *fb,
+                                    struct sg_table sg_tables[3])
+{
+       return -ENXIO;
+}
+static inline void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp,
+                                       struct drm_framebuffer *fb,
+                                       struct sg_table sg_tables[3])
+{
+}
+#endif
+
+#endif /* __RCAR_DU_VSP_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.c
new file mode 100644 (file)
index 0000000..8cd37d7
--- /dev/null
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R-Car Display Unit Writeback Support
+ *
+ * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_writeback.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_writeback.h"
+
+/**
+ * struct rcar_du_wb_conn_state - Driver-specific writeback connector state
+ * @state: base DRM connector state
+ * @format: format of the writeback framebuffer
+ */
+struct rcar_du_wb_conn_state {
+       struct drm_connector_state state;
+       const struct rcar_du_format_info *format;
+};
+
+#define to_rcar_wb_conn_state(s) \
+       container_of(s, struct rcar_du_wb_conn_state, state)
+
+/**
+ * struct rcar_du_wb_job - Driver-private data for writeback jobs
+ * @sg_tables: scatter-gather tables for the framebuffer memory
+ */
+struct rcar_du_wb_job {
+       struct sg_table sg_tables[3];
+};
+
+static int rcar_du_wb_conn_get_modes(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+
+       return drm_add_modes_noedid(connector, dev->mode_config.max_width,
+                                   dev->mode_config.max_height);
+}
+
+static int rcar_du_wb_prepare_job(struct drm_writeback_connector *connector,
+                                 struct drm_writeback_job *job)
+{
+       struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
+       struct rcar_du_wb_job *rjob;
+       int ret;
+
+       if (!job->fb)
+               return 0;
+
+       rjob = kzalloc(sizeof(*rjob), GFP_KERNEL);
+       if (!rjob)
+               return -ENOMEM;
+
+       /* Map the framebuffer to the VSP. */
+       ret = rcar_du_vsp_map_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
+       if (ret < 0) {
+               kfree(rjob);
+               return ret;
+       }
+
+       job->priv = rjob;
+       return 0;
+}
+
+static void rcar_du_wb_cleanup_job(struct drm_writeback_connector *connector,
+                                  struct drm_writeback_job *job)
+{
+       struct rcar_du_crtc *rcrtc = wb_to_rcar_crtc(connector);
+       struct rcar_du_wb_job *rjob = job->priv;
+
+       if (!job->fb)
+               return;
+
+       rcar_du_vsp_unmap_fb(rcrtc->vsp, job->fb, rjob->sg_tables);
+       kfree(rjob);
+}
+
+static const struct drm_connector_helper_funcs rcar_du_wb_conn_helper_funcs = {
+       .get_modes = rcar_du_wb_conn_get_modes,
+       .prepare_writeback_job = rcar_du_wb_prepare_job,
+       .cleanup_writeback_job = rcar_du_wb_cleanup_job,
+};
+
+static struct drm_connector_state *
+rcar_du_wb_conn_duplicate_state(struct drm_connector *connector)
+{
+       struct rcar_du_wb_conn_state *copy;
+
+       if (WARN_ON(!connector->state))
+               return NULL;
+
+       copy = kzalloc(sizeof(*copy), GFP_KERNEL);
+       if (!copy)
+               return NULL;
+
+       __drm_atomic_helper_connector_duplicate_state(connector, &copy->state);
+
+       return &copy->state;
+}
+
+static void rcar_du_wb_conn_destroy_state(struct drm_connector *connector,
+                                         struct drm_connector_state *state)
+{
+       __drm_atomic_helper_connector_destroy_state(state);
+       kfree(to_rcar_wb_conn_state(state));
+}
+
+static void rcar_du_wb_conn_reset(struct drm_connector *connector)
+{
+       struct rcar_du_wb_conn_state *state;
+
+       if (connector->state) {
+               rcar_du_wb_conn_destroy_state(connector, connector->state);
+               connector->state = NULL;
+       }
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return;
+
+       __drm_atomic_helper_connector_reset(connector, &state->state);
+}
+
+static const struct drm_connector_funcs rcar_du_wb_conn_funcs = {
+       .reset = rcar_du_wb_conn_reset,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = drm_connector_cleanup,
+       .atomic_duplicate_state = rcar_du_wb_conn_duplicate_state,
+       .atomic_destroy_state = rcar_du_wb_conn_destroy_state,
+};
+
+static int rcar_du_wb_enc_atomic_check(struct drm_encoder *encoder,
+                                      struct drm_crtc_state *crtc_state,
+                                      struct drm_connector_state *conn_state)
+{
+       struct rcar_du_wb_conn_state *wb_state =
+               to_rcar_wb_conn_state(conn_state);
+       const struct drm_display_mode *mode = &crtc_state->mode;
+       struct drm_device *dev = encoder->dev;
+       struct drm_framebuffer *fb;
+
+       if (!conn_state->writeback_job)
+               return 0;
+
+       fb = conn_state->writeback_job->fb;
+
+       /*
+        * Verify that the framebuffer format is supported and that its size
+        * matches the current mode.
+        */
+       if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) {
+               dev_dbg(dev->dev, "%s: invalid framebuffer size %ux%u\n",
+                       __func__, fb->width, fb->height);
+               return -EINVAL;
+       }
+
+       wb_state->format = rcar_du_format_info(fb->format->format);
+       if (wb_state->format == NULL) {
+               dev_dbg(dev->dev, "%s: unsupported format %p4cc\n", __func__,
+                       &fb->format->format);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct drm_encoder_helper_funcs rcar_du_wb_enc_helper_funcs = {
+       .atomic_check = rcar_du_wb_enc_atomic_check,
+};
+
+/*
+ * Only RGB formats are currently supported as the VSP outputs RGB to the DU
+ * and can't convert to YUV separately for writeback.
+ */
+static const u32 writeback_formats[] = {
+       DRM_FORMAT_RGB332,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_BGR888,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_BGRA8888,
+       DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+};
+
+int rcar_du_writeback_init(struct rcar_du_device *rcdu,
+                          struct rcar_du_crtc *rcrtc)
+{
+       struct drm_writeback_connector *wb_conn = &rcrtc->writeback;
+
+       drm_connector_helper_add(&wb_conn->base,
+                                &rcar_du_wb_conn_helper_funcs);
+
+       return drm_writeback_connector_init(&rcdu->ddev, wb_conn,
+                                           &rcar_du_wb_conn_funcs,
+                                           &rcar_du_wb_enc_helper_funcs,
+                                           writeback_formats,
+                                           ARRAY_SIZE(writeback_formats),
+                                           1 << drm_crtc_index(&rcrtc->crtc));
+}
+
+void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
+                            struct vsp1_du_writeback_config *cfg)
+{
+       struct rcar_du_wb_conn_state *wb_state;
+       struct drm_connector_state *state;
+       struct rcar_du_wb_job *rjob;
+       struct drm_framebuffer *fb;
+       unsigned int i;
+
+       state = rcrtc->writeback.base.state;
+       if (!state || !state->writeback_job)
+               return;
+
+       fb = state->writeback_job->fb;
+       rjob = state->writeback_job->priv;
+       wb_state = to_rcar_wb_conn_state(state);
+
+       cfg->pixelformat = wb_state->format->v4l2;
+       cfg->pitch = fb->pitches[0];
+
+       for (i = 0; i < wb_state->format->planes; ++i)
+               cfg->mem[i] = sg_dma_address(rjob->sg_tables[i].sgl)
+                           + fb->offsets[i];
+
+       drm_writeback_queue_job(&rcrtc->writeback, state);
+}
+
+void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc)
+{
+       drm_writeback_signal_completion(&rcrtc->writeback, 0);
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.h
new file mode 100644 (file)
index 0000000..a71c9c0
--- /dev/null
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * R-Car Display Unit Writeback Support
+ *
+ * Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#ifndef __RCAR_DU_WRITEBACK_H__
+#define __RCAR_DU_WRITEBACK_H__
+
+#include <drm/drm_plane.h>
+
+struct rcar_du_crtc;
+struct rcar_du_device;
+struct vsp1_du_atomic_pipe_config;
+
+#ifdef CONFIG_DRM_RCAR_WRITEBACK
+int rcar_du_writeback_init(struct rcar_du_device *rcdu,
+                          struct rcar_du_crtc *rcrtc);
+void rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
+                            struct vsp1_du_writeback_config *cfg);
+void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc);
+#else
+static inline int rcar_du_writeback_init(struct rcar_du_device *rcdu,
+                                        struct rcar_du_crtc *rcrtc)
+{
+       return -ENXIO;
+}
+static inline void
+rcar_du_writeback_setup(struct rcar_du_crtc *rcrtc,
+                       struct vsp1_du_writeback_config *cfg)
+{
+}
+static inline void rcar_du_writeback_complete(struct rcar_du_crtc *rcrtc)
+{
+}
+#endif
+
+#endif /* __RCAR_DU_WRITEBACK_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c
new file mode 100644 (file)
index 0000000..18ed149
--- /dev/null
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * R-Car Gen3 HDMI PHY
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_modes.h>
+
+#define RCAR_HDMI_PHY_OPMODE_PLLCFG    0x06    /* Mode of operation and PLL dividers */
+#define RCAR_HDMI_PHY_PLLCURRGMPCTRL   0x10    /* PLL current and Gmp (conductance) */
+#define RCAR_HDMI_PHY_PLLDIVCTRL       0x11    /* PLL dividers */
+
+struct rcar_hdmi_phy_params {
+       unsigned long mpixelclock;
+       u16 opmode_div; /* Mode of operation and PLL dividers */
+       u16 curr_gmp;   /* PLL current and Gmp (conductance) */
+       u16 div;        /* PLL dividers */
+};
+
+static const struct rcar_hdmi_phy_params rcar_hdmi_phy_params[] = {
+       { 35500000,  0x0003, 0x0344, 0x0328 },
+       { 44900000,  0x0003, 0x0285, 0x0128 },
+       { 71000000,  0x0002, 0x1184, 0x0314 },
+       { 90000000,  0x0002, 0x1144, 0x0114 },
+       { 140250000, 0x0001, 0x20c4, 0x030a },
+       { 182750000, 0x0001, 0x2084, 0x010a },
+       { 281250000, 0x0000, 0x0084, 0x0305 },
+       { 297000000, 0x0000, 0x0084, 0x0105 },
+       { ~0UL,      0x0000, 0x0000, 0x0000 },
+};
+
+static enum drm_mode_status
+rcar_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
+                    const struct drm_display_info *info,
+                    const struct drm_display_mode *mode)
+{
+       /*
+        * The maximum supported clock frequency is 297 MHz, as shown in the PHY
+        * parameters table.
+        */
+       if (mode->clock > 297000)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, void *data,
+                                  unsigned long mpixelclock)
+{
+       const struct rcar_hdmi_phy_params *params = rcar_hdmi_phy_params;
+
+       for (; params->mpixelclock != ~0UL; ++params) {
+               if (mpixelclock <= params->mpixelclock)
+                       break;
+       }
+
+       if (params->mpixelclock == ~0UL)
+               return -EINVAL;
+
+       dw_hdmi_phy_i2c_write(hdmi, params->opmode_div,
+                             RCAR_HDMI_PHY_OPMODE_PLLCFG);
+       dw_hdmi_phy_i2c_write(hdmi, params->curr_gmp,
+                             RCAR_HDMI_PHY_PLLCURRGMPCTRL);
+       dw_hdmi_phy_i2c_write(hdmi, params->div, RCAR_HDMI_PHY_PLLDIVCTRL);
+
+       return 0;
+}
+
+static const struct dw_hdmi_plat_data rcar_dw_hdmi_plat_data = {
+       .output_port = 1,
+       .mode_valid = rcar_hdmi_mode_valid,
+       .configure_phy  = rcar_hdmi_phy_configure,
+};
+
+static int rcar_dw_hdmi_probe(struct platform_device *pdev)
+{
+       struct dw_hdmi *hdmi;
+
+       hdmi = dw_hdmi_probe(pdev, &rcar_dw_hdmi_plat_data);
+       if (IS_ERR(hdmi))
+               return PTR_ERR(hdmi);
+
+       platform_set_drvdata(pdev, hdmi);
+
+       return 0;
+}
+
+static int rcar_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 rcar_dw_hdmi_of_table[] = {
+       { .compatible = "renesas,rcar-gen3-hdmi" },
+       { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rcar_dw_hdmi_of_table);
+
+static struct platform_driver rcar_dw_hdmi_platform_driver = {
+       .probe          = rcar_dw_hdmi_probe,
+       .remove         = rcar_dw_hdmi_remove,
+       .driver         = {
+               .name   = "rcar-dw-hdmi",
+               .of_match_table = rcar_dw_hdmi_of_table,
+       },
+};
+
+module_platform_driver(rcar_dw_hdmi_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R-Car Gen3 HDMI Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
new file mode 100644 (file)
index 0000000..ca215b5
--- /dev/null
@@ -0,0 +1,1035 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R-Car LVDS Encoder
+ *
+ * Copyright (C) 2013-2018 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "rcar_lvds.h"
+#include "rcar_lvds_regs.h"
+
+struct rcar_lvds;
+
+/* Keep in sync with the LVDCR0.LVMD hardware register values. */
+enum rcar_lvds_mode {
+       RCAR_LVDS_MODE_JEIDA = 0,
+       RCAR_LVDS_MODE_MIRROR = 1,
+       RCAR_LVDS_MODE_VESA = 4,
+};
+
+enum rcar_lvds_link_type {
+       RCAR_LVDS_SINGLE_LINK = 0,
+       RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 1,
+       RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 2,
+};
+
+#define RCAR_LVDS_QUIRK_LANES          BIT(0)  /* LVDS lanes 1 and 3 inverted */
+#define RCAR_LVDS_QUIRK_GEN3_LVEN      BIT(1)  /* LVEN bit needs to be set on R8A77970/R8A7799x */
+#define RCAR_LVDS_QUIRK_PWD            BIT(2)  /* PWD bit available (all of Gen3 but E3) */
+#define RCAR_LVDS_QUIRK_EXT_PLL                BIT(3)  /* Has extended PLL */
+#define RCAR_LVDS_QUIRK_DUAL_LINK      BIT(4)  /* Supports dual-link operation */
+
+struct rcar_lvds_device_info {
+       unsigned int gen;
+       unsigned int quirks;
+       void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq);
+};
+
+struct rcar_lvds {
+       struct device *dev;
+       const struct rcar_lvds_device_info *info;
+       struct reset_control *rstc;
+
+       struct drm_bridge bridge;
+
+       struct drm_bridge *next_bridge;
+       struct drm_panel *panel;
+
+       void __iomem *mmio;
+       struct {
+               struct clk *mod;                /* CPG module clock */
+               struct clk *extal;              /* External clock */
+               struct clk *dotclkin[2];        /* External DU clocks */
+       } clocks;
+
+       struct drm_bridge *companion;
+       enum rcar_lvds_link_type link_type;
+};
+
+#define bridge_to_rcar_lvds(b) \
+       container_of(b, struct rcar_lvds, bridge)
+
+static u32 rcar_lvds_read(struct rcar_lvds *lvds, u32 reg)
+{
+       return ioread32(lvds->mmio + reg);
+}
+
+static void rcar_lvds_write(struct rcar_lvds *lvds, u32 reg, u32 data)
+{
+       iowrite32(data, lvds->mmio + reg);
+}
+
+/* -----------------------------------------------------------------------------
+ * PLL Setup
+ */
+
+static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int freq)
+{
+       u32 val;
+
+       if (freq < 39000000)
+               val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
+       else if (freq < 61000000)
+               val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
+       else if (freq < 121000000)
+               val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M;
+       else
+               val = LVDPLLCR_PLLDLYCNT_150M;
+
+       rcar_lvds_write(lvds, LVDPLLCR, val);
+}
+
+static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int freq)
+{
+       u32 val;
+
+       if (freq < 42000000)
+               val = LVDPLLCR_PLLDIVCNT_42M;
+       else if (freq < 85000000)
+               val = LVDPLLCR_PLLDIVCNT_85M;
+       else if (freq < 128000000)
+               val = LVDPLLCR_PLLDIVCNT_128M;
+       else
+               val = LVDPLLCR_PLLDIVCNT_148M;
+
+       rcar_lvds_write(lvds, LVDPLLCR, val);
+}
+
+struct pll_info {
+       unsigned long diff;
+       unsigned int pll_m;
+       unsigned int pll_n;
+       unsigned int pll_e;
+       unsigned int div;
+       u32 clksel;
+};
+
+static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk,
+                                    unsigned long target, struct pll_info *pll,
+                                    u32 clksel, bool dot_clock_only)
+{
+       unsigned int div7 = dot_clock_only ? 1 : 7;
+       unsigned long output;
+       unsigned long fin;
+       unsigned int m_min;
+       unsigned int m_max;
+       unsigned int m;
+       int error;
+
+       if (!clk)
+               return;
+
+       /*
+        * The LVDS PLL is made of a pre-divider and a multiplier (strangely
+        * enough called M and N respectively), followed by a post-divider E.
+        *
+        *         ,-----.         ,-----.     ,-----.         ,-----.
+        * Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout
+        *         `-----'     ,-> |     |     `-----'   |     `-----'
+        *                     |   `-----'               |
+        *                     |         ,-----.         |
+        *                     `-------- | 1/N | <-------'
+        *                               `-----'
+        *
+        * The clock output by the PLL is then further divided by a programmable
+        * divider DIV to achieve the desired target frequency. Finally, an
+        * optional fixed /7 divider is used to convert the bit clock to a pixel
+        * clock (as LVDS transmits 7 bits per lane per clock sample).
+        *
+        *          ,-------.     ,-----.     |\
+        * Fout --> | 1/DIV | --> | 1/7 | --> | |
+        *          `-------'  |  `-----'     | | --> dot clock
+        *                     `------------> | |
+        *                                    |/
+        *
+        * The /7 divider is optional, it is enabled when the LVDS PLL is used
+        * to drive the LVDS encoder, and disabled when  used to generate a dot
+        * clock for the DU RGB output, without using the LVDS encoder.
+        *
+        * The PLL allowed input frequency range is 12 MHz to 192 MHz.
+        */
+
+       fin = clk_get_rate(clk);
+       if (fin < 12000000 || fin > 192000000)
+               return;
+
+       /*
+        * The comparison frequency range is 12 MHz to 24 MHz, which limits the
+        * allowed values for the pre-divider M (normal range 1-8).
+        *
+        * Fpfd = Fin / M
+        */
+       m_min = max_t(unsigned int, 1, DIV_ROUND_UP(fin, 24000000));
+       m_max = min_t(unsigned int, 8, fin / 12000000);
+
+       for (m = m_min; m <= m_max; ++m) {
+               unsigned long fpfd;
+               unsigned int n_min;
+               unsigned int n_max;
+               unsigned int n;
+
+               /*
+                * The VCO operating range is 900 Mhz to 1800 MHz, which limits
+                * the allowed values for the multiplier N (normal range
+                * 60-120).
+                *
+                * Fvco = Fin * N / M
+                */
+               fpfd = fin / m;
+               n_min = max_t(unsigned int, 60, DIV_ROUND_UP(900000000, fpfd));
+               n_max = min_t(unsigned int, 120, 1800000000 / fpfd);
+
+               for (n = n_min; n < n_max; ++n) {
+                       unsigned long fvco;
+                       unsigned int e_min;
+                       unsigned int e;
+
+                       /*
+                        * The output frequency is limited to 1039.5 MHz,
+                        * limiting again the allowed values for the
+                        * post-divider E (normal value 1, 2 or 4).
+                        *
+                        * Fout = Fvco / E
+                        */
+                       fvco = fpfd * n;
+                       e_min = fvco > 1039500000 ? 1 : 0;
+
+                       for (e = e_min; e < 3; ++e) {
+                               unsigned long fout;
+                               unsigned long diff;
+                               unsigned int div;
+
+                               /*
+                                * Finally we have a programable divider after
+                                * the PLL, followed by a an optional fixed /7
+                                * divider.
+                                */
+                               fout = fvco / (1 << e) / div7;
+                               div = max(1UL, DIV_ROUND_CLOSEST(fout, target));
+                               diff = abs(fout / div - target);
+
+                               if (diff < pll->diff) {
+                                       pll->diff = diff;
+                                       pll->pll_m = m;
+                                       pll->pll_n = n;
+                                       pll->pll_e = e;
+                                       pll->div = div;
+                                       pll->clksel = clksel;
+
+                                       if (diff == 0)
+                                               goto done;
+                               }
+                       }
+               }
+       }
+
+done:
+       output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e)
+              / div7 / pll->div;
+       error = (long)(output - target) * 10000 / (long)target;
+
+       dev_dbg(lvds->dev,
+               "%pC %lu Hz -> Fout %lu Hz (target %lu Hz, error %d.%02u%%), PLL M/N/E/DIV %u/%u/%u/%u\n",
+               clk, fin, output, target, error / 100,
+               error < 0 ? -error % 100 : error % 100,
+               pll->pll_m, pll->pll_n, pll->pll_e, pll->div);
+}
+
+static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds,
+                                     unsigned int freq, bool dot_clock_only)
+{
+       struct pll_info pll = { .diff = (unsigned long)-1 };
+       u32 lvdpllcr;
+
+       rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll,
+                                LVDPLLCR_CKSEL_DU_DOTCLKIN(0), dot_clock_only);
+       rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll,
+                                LVDPLLCR_CKSEL_DU_DOTCLKIN(1), dot_clock_only);
+       rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll,
+                                LVDPLLCR_CKSEL_EXTAL, dot_clock_only);
+
+       lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT
+                | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1);
+
+       if (pll.pll_e > 0)
+               lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL
+                        |  LVDPLLCR_PLLE(pll.pll_e - 1);
+
+       if (dot_clock_only)
+               lvdpllcr |= LVDPLLCR_OCKSEL;
+
+       rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
+
+       if (pll.div > 1)
+               /*
+                * The DIVRESET bit is a misnomer, setting it to 1 deasserts the
+                * divisor reset.
+                */
+               rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL |
+                               LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1));
+       else
+               rcar_lvds_write(lvds, LVDDIV, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Enable/disable
+ */
+
+static enum rcar_lvds_mode rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds,
+                                       const struct drm_connector *connector)
+{
+       const struct drm_display_info *info;
+       enum rcar_lvds_mode mode;
+
+       /*
+        * There is no API yet to retrieve LVDS mode from a bridge, only panels
+        * are supported.
+        */
+       if (!lvds->panel)
+               return RCAR_LVDS_MODE_JEIDA;
+
+       info = &connector->display_info;
+       if (!info->num_bus_formats || !info->bus_formats) {
+               dev_warn(lvds->dev,
+                        "no LVDS bus format reported, using JEIDA\n");
+               return RCAR_LVDS_MODE_JEIDA;
+       }
+
+       switch (info->bus_formats[0]) {
+       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+       case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+               mode = RCAR_LVDS_MODE_JEIDA;
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+               mode = RCAR_LVDS_MODE_VESA;
+               break;
+       default:
+               dev_warn(lvds->dev,
+                        "unsupported LVDS bus format 0x%04x, using JEIDA\n",
+                        info->bus_formats[0]);
+               return RCAR_LVDS_MODE_JEIDA;
+       }
+
+       if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
+               mode |= RCAR_LVDS_MODE_MIRROR;
+
+       return mode;
+}
+
+static void rcar_lvds_enable(struct drm_bridge *bridge,
+                            struct drm_atomic_state *state,
+                            struct drm_crtc *crtc,
+                            struct drm_connector *connector)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+       u32 lvdhcr;
+       u32 lvdcr0;
+       int ret;
+
+       ret = pm_runtime_resume_and_get(lvds->dev);
+       if (ret)
+               return;
+
+       /* Enable the companion LVDS encoder in dual-link mode. */
+       if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion)
+               rcar_lvds_enable(lvds->companion, state, crtc, connector);
+
+       /*
+        * Hardcode the channels and control signals routing for now.
+        *
+        * HSYNC -> CTRL0
+        * VSYNC -> CTRL1
+        * DISP  -> CTRL2
+        * 0     -> CTRL3
+        */
+       rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
+                       LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
+                       LVDCTRCR_CTR0SEL_HSYNC);
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_LANES)
+               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
+                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
+       else
+               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
+                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
+
+       rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) {
+               u32 lvdstripe = 0;
+
+               if (lvds->link_type != RCAR_LVDS_SINGLE_LINK) {
+                       /*
+                        * By default we generate even pixels from the primary
+                        * encoder and odd pixels from the companion encoder.
+                        * Swap pixels around if the sink requires odd pixels
+                        * from the primary encoder and even pixels from the
+                        * companion encoder.
+                        */
+                       bool swap_pixels = lvds->link_type ==
+                               RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+
+                       /*
+                        * Configure vertical stripe since we are dealing with
+                        * an LVDS dual-link connection.
+                        *
+                        * ST_SWAP is reserved for the companion encoder, only
+                        * set it in the primary encoder.
+                        */
+                       lvdstripe = LVDSTRIPE_ST_ON
+                                 | (lvds->companion && swap_pixels ?
+                                    LVDSTRIPE_ST_SWAP : 0);
+               }
+               rcar_lvds_write(lvds, LVDSTRIPE, lvdstripe);
+       }
+
+       /*
+        * PLL clock configuration on all instances but the companion in
+        * dual-link mode.
+        *
+        * The extended PLL has been turned on by an explicit call to
+        * rcar_lvds_pclk_enable() from the DU driver.
+        */
+       if ((lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) &&
+           !(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
+               const struct drm_crtc_state *crtc_state =
+                       drm_atomic_get_new_crtc_state(state, crtc);
+               const struct drm_display_mode *mode =
+                       &crtc_state->adjusted_mode;
+
+               lvds->info->pll_setup(lvds, mode->clock * 1000);
+       }
+
+       /* Set the LVDS mode and select the input. */
+       lvdcr0 = rcar_lvds_get_lvds_mode(lvds, connector) << LVDCR0_LVMD_SHIFT;
+
+       if (lvds->bridge.encoder) {
+               if (drm_crtc_index(crtc) == 2)
+                       lvdcr0 |= LVDCR0_DUSEL;
+       }
+
+       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+
+       /* Turn all the channels on. */
+       rcar_lvds_write(lvds, LVDCR1,
+                       LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
+                       LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
+
+       if (lvds->info->gen < 3) {
+               /* Enable LVDS operation and turn the bias circuitry on. */
+               lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
+               /*
+                * Turn the PLL on (simple PLL only, extended PLL is fully
+                * controlled through LVDPLLCR).
+                */
+               lvdcr0 |= LVDCR0_PLLON;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) {
+               /* Set LVDS normal mode. */
+               lvdcr0 |= LVDCR0_PWD;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) {
+               /*
+                * Turn on the LVDS PHY. On D3, the LVEN and LVRES bit must be
+                * set at the same time, so don't write the register yet.
+                */
+               lvdcr0 |= LVDCR0_LVEN;
+               if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_PWD))
+                       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
+               /* Wait for the PLL startup delay (simple PLL only). */
+               usleep_range(100, 150);
+       }
+
+       /* Turn the output on. */
+       lvdcr0 |= LVDCR0_LVRES;
+       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+}
+
+static void rcar_lvds_disable(struct drm_bridge *bridge)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+       u32 lvdcr0;
+
+       /*
+        * Clear the LVDCR0 bits in the order specified by the hardware
+        * documentation, ending with a write of 0 to the full register to
+        * clear all remaining bits.
+        */
+       lvdcr0 = rcar_lvds_read(lvds, LVDCR0);
+
+       lvdcr0 &= ~LVDCR0_LVRES;
+       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) {
+               lvdcr0 &= ~LVDCR0_LVEN;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) {
+               lvdcr0 &= ~LVDCR0_PWD;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
+               lvdcr0 &= ~LVDCR0_PLLON;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       rcar_lvds_write(lvds, LVDCR0, 0);
+       rcar_lvds_write(lvds, LVDCR1, 0);
+
+       /* The extended PLL is turned off in rcar_lvds_pclk_disable(). */
+       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))
+               rcar_lvds_write(lvds, LVDPLLCR, 0);
+
+       /* Disable the companion LVDS encoder in dual-link mode. */
+       if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion)
+               rcar_lvds_disable(lvds->companion);
+
+       pm_runtime_put_sync(lvds->dev);
+}
+
+/* -----------------------------------------------------------------------------
+ * Clock - D3/E3 only
+ */
+
+int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq,
+                         bool dot_clk_only)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+       int ret;
+
+       if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)))
+               return -ENODEV;
+
+       dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq);
+
+       ret = pm_runtime_resume_and_get(lvds->dev);
+       if (ret)
+               return ret;
+
+       rcar_lvds_pll_setup_d3_e3(lvds, freq, dot_clk_only);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rcar_lvds_pclk_enable);
+
+void rcar_lvds_pclk_disable(struct drm_bridge *bridge, bool dot_clk_only)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+
+       if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)))
+               return;
+
+       dev_dbg(lvds->dev, "disabling LVDS PLL\n");
+
+       if (!dot_clk_only)
+               rcar_lvds_disable(bridge);
+
+       rcar_lvds_write(lvds, LVDPLLCR, 0);
+
+       pm_runtime_put_sync(lvds->dev);
+}
+EXPORT_SYMBOL_GPL(rcar_lvds_pclk_disable);
+
+/* -----------------------------------------------------------------------------
+ * Bridge
+ */
+
+static void rcar_lvds_atomic_enable(struct drm_bridge *bridge,
+                                   struct drm_bridge_state *old_bridge_state)
+{
+       struct drm_atomic_state *state = old_bridge_state->base.state;
+       struct drm_connector *connector;
+       struct drm_crtc *crtc;
+
+       connector = drm_atomic_get_new_connector_for_encoder(state,
+                                                            bridge->encoder);
+       crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+
+       rcar_lvds_enable(bridge, state, crtc, connector);
+}
+
+static void rcar_lvds_atomic_disable(struct drm_bridge *bridge,
+                                    struct drm_bridge_state *old_bridge_state)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+
+       /*
+        * For D3 and E3, disabling the LVDS encoder before the DU would stall
+        * the DU, causing a vblank wait timeout when stopping the DU. This has
+        * been traced to clearing the LVEN bit, but the exact reason is
+        * unknown. Keep the encoder enabled, it will be disabled by an explicit
+        * call to rcar_lvds_pclk_disable() from the DU driver.
+        *
+        * We could clear the LVRES bit already to disable the LVDS output, but
+        * that's likely pointless.
+        */
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)
+               return;
+
+       rcar_lvds_disable(bridge);
+}
+
+static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge,
+                                const struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+       int min_freq;
+
+       /*
+        * The internal LVDS encoder has a restricted clock frequency operating
+        * range, from 5MHz to 148.5MHz on D3 and E3, and from 31MHz to
+        * 148.5MHz on all other platforms. Clamp the clock accordingly.
+        */
+       min_freq = lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL ? 5000 : 31000;
+       adjusted_mode->clock = clamp(adjusted_mode->clock, min_freq, 148500);
+
+       return true;
+}
+
+static int rcar_lvds_attach(struct drm_bridge *bridge,
+                           enum drm_bridge_attach_flags flags)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+
+       if (!lvds->next_bridge)
+               return 0;
+
+       return drm_bridge_attach(bridge->encoder, lvds->next_bridge, bridge,
+                                flags);
+}
+
+static const struct drm_bridge_funcs rcar_lvds_bridge_ops = {
+       .attach = rcar_lvds_attach,
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset = drm_atomic_helper_bridge_reset,
+       .atomic_enable = rcar_lvds_atomic_enable,
+       .atomic_disable = rcar_lvds_atomic_disable,
+       .mode_fixup = rcar_lvds_mode_fixup,
+};
+
+bool rcar_lvds_dual_link(struct drm_bridge *bridge)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+
+       return lvds->link_type != RCAR_LVDS_SINGLE_LINK;
+}
+EXPORT_SYMBOL_GPL(rcar_lvds_dual_link);
+
+bool rcar_lvds_is_connected(struct drm_bridge *bridge)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+
+       return lvds->next_bridge != NULL;
+}
+EXPORT_SYMBOL_GPL(rcar_lvds_is_connected);
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int rcar_lvds_parse_dt_companion(struct rcar_lvds *lvds)
+{
+       const struct of_device_id *match;
+       struct device_node *companion;
+       struct device_node *port0, *port1;
+       struct rcar_lvds *companion_lvds;
+       struct device *dev = lvds->dev;
+       int dual_link;
+       int ret = 0;
+
+       /* Locate the companion LVDS encoder for dual-link operation, if any. */
+       companion = of_parse_phandle(dev->of_node, "renesas,companion", 0);
+       if (!companion)
+               return 0;
+
+       /*
+        * Sanity check: the companion encoder must have the same compatible
+        * string.
+        */
+       match = of_match_device(dev->driver->of_match_table, dev);
+       if (!of_device_is_compatible(companion, match->compatible)) {
+               dev_err(dev, "Companion LVDS encoder is invalid\n");
+               ret = -ENXIO;
+               goto done;
+       }
+
+       /*
+        * We need to work out if the sink is expecting us to function in
+        * dual-link mode. We do this by looking at the DT port nodes we are
+        * connected to, if they are marked as expecting even pixels and
+        * odd pixels than we need to enable vertical stripe output.
+        */
+       port0 = of_graph_get_port_by_id(dev->of_node, 1);
+       port1 = of_graph_get_port_by_id(companion, 1);
+       dual_link = drm_of_lvds_get_dual_link_pixel_order(port0, port1);
+       of_node_put(port0);
+       of_node_put(port1);
+
+       switch (dual_link) {
+       case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
+               lvds->link_type = RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+               break;
+       case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
+               lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
+               break;
+       default:
+               /*
+                * Early dual-link bridge specific implementations populate the
+                * timings field of drm_bridge. If the flag is set, we assume
+                * that we are expected to generate even pixels from the primary
+                * encoder, and odd pixels from the companion encoder.
+                */
+               if (lvds->next_bridge->timings &&
+                   lvds->next_bridge->timings->dual_link)
+                       lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
+               else
+                       lvds->link_type = RCAR_LVDS_SINGLE_LINK;
+       }
+
+       if (lvds->link_type == RCAR_LVDS_SINGLE_LINK) {
+               dev_dbg(dev, "Single-link configuration detected\n");
+               goto done;
+       }
+
+       lvds->companion = of_drm_find_bridge(companion);
+       if (!lvds->companion) {
+               ret = -EPROBE_DEFER;
+               goto done;
+       }
+
+       dev_dbg(dev,
+               "Dual-link configuration detected (companion encoder %pOF)\n",
+               companion);
+
+       if (lvds->link_type == RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
+               dev_dbg(dev, "Data swapping required\n");
+
+       /*
+        * FIXME: We should not be messing with the companion encoder private
+        * data from the primary encoder, we should rather let the companion
+        * encoder work things out on its own. However, the companion encoder
+        * doesn't hold a reference to the primary encoder, and
+        * drm_of_lvds_get_dual_link_pixel_order needs to be given references
+        * to the output ports of both encoders, therefore leave it like this
+        * for the time being.
+        */
+       companion_lvds = bridge_to_rcar_lvds(lvds->companion);
+       companion_lvds->link_type = lvds->link_type;
+
+done:
+       of_node_put(companion);
+
+       return ret;
+}
+
+static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
+{
+       int ret;
+
+       ret = drm_of_find_panel_or_bridge(lvds->dev->of_node, 1, 0,
+                                         &lvds->panel, &lvds->next_bridge);
+       if (ret)
+               goto done;
+
+       if (lvds->panel) {
+               lvds->next_bridge = devm_drm_panel_bridge_add(lvds->dev,
+                                                             lvds->panel);
+               if (IS_ERR_OR_NULL(lvds->next_bridge)) {
+                       ret = -EINVAL;
+                       goto done;
+               }
+       }
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK)
+               ret = rcar_lvds_parse_dt_companion(lvds);
+
+done:
+       /*
+        * On D3/E3 the LVDS encoder provides a clock to the DU, which can be
+        * used for the DPAD output even when the LVDS output is not connected.
+        * Don't fail probe in that case as the DU will need the bridge to
+        * control the clock.
+        */
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)
+               return ret == -ENODEV ? 0 : ret;
+
+       return ret;
+}
+
+static struct clk *rcar_lvds_get_clock(struct rcar_lvds *lvds, const char *name,
+                                      bool optional)
+{
+       struct clk *clk;
+
+       clk = devm_clk_get(lvds->dev, name);
+       if (!IS_ERR(clk))
+               return clk;
+
+       if (PTR_ERR(clk) == -ENOENT && optional)
+               return NULL;
+
+       dev_err_probe(lvds->dev, PTR_ERR(clk), "failed to get %s clock\n",
+                     name ? name : "module");
+
+       return clk;
+}
+
+static int rcar_lvds_get_clocks(struct rcar_lvds *lvds)
+{
+       lvds->clocks.mod = rcar_lvds_get_clock(lvds, NULL, false);
+       if (IS_ERR(lvds->clocks.mod))
+               return PTR_ERR(lvds->clocks.mod);
+
+       /*
+        * LVDS encoders without an extended PLL have no external clock inputs.
+        */
+       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))
+               return 0;
+
+       lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true);
+       if (IS_ERR(lvds->clocks.extal))
+               return PTR_ERR(lvds->clocks.extal);
+
+       lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true);
+       if (IS_ERR(lvds->clocks.dotclkin[0]))
+               return PTR_ERR(lvds->clocks.dotclkin[0]);
+
+       lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true);
+       if (IS_ERR(lvds->clocks.dotclkin[1]))
+               return PTR_ERR(lvds->clocks.dotclkin[1]);
+
+       /* At least one input to the PLL must be available. */
+       if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] &&
+           !lvds->clocks.dotclkin[1]) {
+               dev_err(lvds->dev,
+                       "no input clock (extal, dclkin.0 or dclkin.1)\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct rcar_lvds_device_info rcar_lvds_r8a7790es1_info = {
+       .gen = 2,
+       .quirks = RCAR_LVDS_QUIRK_LANES,
+       .pll_setup = rcar_lvds_pll_setup_gen2,
+};
+
+static const struct soc_device_attribute lvds_quirk_matches[] = {
+       {
+               .soc_id = "r8a7790", .revision = "ES1.*",
+               .data = &rcar_lvds_r8a7790es1_info,
+       },
+       { /* sentinel */ }
+};
+
+static int rcar_lvds_probe(struct platform_device *pdev)
+{
+       const struct soc_device_attribute *attr;
+       struct rcar_lvds *lvds;
+       int ret;
+
+       lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+       if (lvds == NULL)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, lvds);
+
+       lvds->dev = &pdev->dev;
+       lvds->info = of_device_get_match_data(&pdev->dev);
+
+       attr = soc_device_match(lvds_quirk_matches);
+       if (attr)
+               lvds->info = attr->data;
+
+       ret = rcar_lvds_parse_dt(lvds);
+       if (ret < 0)
+               return ret;
+
+       lvds->bridge.funcs = &rcar_lvds_bridge_ops;
+       lvds->bridge.of_node = pdev->dev.of_node;
+
+       lvds->mmio = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(lvds->mmio))
+               return PTR_ERR(lvds->mmio);
+
+       ret = rcar_lvds_get_clocks(lvds);
+       if (ret < 0)
+               return ret;
+
+       lvds->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+       if (IS_ERR(lvds->rstc))
+               return dev_err_probe(&pdev->dev, PTR_ERR(lvds->rstc),
+                                    "failed to get cpg reset\n");
+
+       pm_runtime_enable(&pdev->dev);
+
+       drm_bridge_add(&lvds->bridge);
+
+       return 0;
+}
+
+static int rcar_lvds_remove(struct platform_device *pdev)
+{
+       struct rcar_lvds *lvds = platform_get_drvdata(pdev);
+
+       drm_bridge_remove(&lvds->bridge);
+
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct rcar_lvds_device_info rcar_lvds_gen2_info = {
+       .gen = 2,
+       .pll_setup = rcar_lvds_pll_setup_gen2,
+};
+
+static const struct rcar_lvds_device_info rcar_lvds_gen3_info = {
+       .gen = 3,
+       .quirks = RCAR_LVDS_QUIRK_PWD,
+       .pll_setup = rcar_lvds_pll_setup_gen3,
+};
+
+static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = {
+       .gen = 3,
+       .quirks = RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_GEN3_LVEN,
+       .pll_setup = rcar_lvds_pll_setup_gen2,
+};
+
+static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = {
+       .gen = 3,
+       .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL
+               | RCAR_LVDS_QUIRK_DUAL_LINK,
+};
+
+static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = {
+       .gen = 3,
+       .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD
+               | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK,
+};
+
+static const struct of_device_id rcar_lvds_of_table[] = {
+       { .compatible = "renesas,r8a7742-lvds", .data = &rcar_lvds_gen2_info },
+       { .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info },
+       { .compatible = "renesas,r8a7744-lvds", .data = &rcar_lvds_gen2_info },
+       { .compatible = "renesas,r8a774a1-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a774b1-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a774c0-lvds", .data = &rcar_lvds_r8a77990_info },
+       { .compatible = "renesas,r8a774e1-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_gen2_info },
+       { .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info },
+       { .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info },
+       { .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a77961-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a77965-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info },
+       { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info },
+       { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info },
+       { .compatible = "renesas,r8a77995-lvds", .data = &rcar_lvds_r8a77995_info },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, rcar_lvds_of_table);
+
+static int rcar_lvds_runtime_suspend(struct device *dev)
+{
+       struct rcar_lvds *lvds = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(lvds->clocks.mod);
+
+       reset_control_assert(lvds->rstc);
+
+       return 0;
+}
+
+static int rcar_lvds_runtime_resume(struct device *dev)
+{
+       struct rcar_lvds *lvds = dev_get_drvdata(dev);
+       int ret;
+
+       ret = reset_control_deassert(lvds->rstc);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(lvds->clocks.mod);
+       if (ret < 0)
+               goto err_reset_assert;
+
+       return 0;
+
+err_reset_assert:
+       reset_control_assert(lvds->rstc);
+
+       return ret;
+}
+
+static const struct dev_pm_ops rcar_lvds_pm_ops = {
+       SET_RUNTIME_PM_OPS(rcar_lvds_runtime_suspend, rcar_lvds_runtime_resume, NULL)
+};
+
+static struct platform_driver rcar_lvds_platform_driver = {
+       .probe          = rcar_lvds_probe,
+       .remove         = rcar_lvds_remove,
+       .driver         = {
+               .name   = "rcar-lvds",
+               .pm     = &rcar_lvds_pm_ops,
+               .of_match_table = rcar_lvds_of_table,
+       },
+};
+
+module_platform_driver(rcar_lvds_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R-Car LVDS Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.h b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.h
new file mode 100644 (file)
index 0000000..887c635
--- /dev/null
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R-Car LVDS Encoder
+ *
+ * Copyright (C) 2013-2018 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_LVDS_H__
+#define __RCAR_LVDS_H__
+
+struct drm_bridge;
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
+int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq,
+                         bool dot_clk_only);
+void rcar_lvds_pclk_disable(struct drm_bridge *bridge, bool dot_clk_only);
+bool rcar_lvds_dual_link(struct drm_bridge *bridge);
+bool rcar_lvds_is_connected(struct drm_bridge *bridge);
+#else
+static inline int rcar_lvds_pclk_enable(struct drm_bridge *bridge,
+                                       unsigned long freq, bool dot_clk_only)
+{
+       return -ENOSYS;
+}
+static inline void rcar_lvds_pclk_disable(struct drm_bridge *bridge,
+                                         bool dot_clock_only)
+{
+}
+static inline bool rcar_lvds_dual_link(struct drm_bridge *bridge)
+{
+       return false;
+}
+static inline bool rcar_lvds_is_connected(struct drm_bridge *bridge)
+{
+       return false;
+}
+#endif /* CONFIG_DRM_RCAR_LVDS */
+
+#endif /* __RCAR_LVDS_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds_regs.h
new file mode 100644 (file)
index 0000000..ab0406a
--- /dev/null
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R-Car LVDS Interface Registers Definitions
+ *
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __RCAR_LVDS_REGS_H__
+#define __RCAR_LVDS_REGS_H__
+
+#define LVDCR0                         0x0000
+#define LVDCR0_DUSEL                   (1 << 15)
+#define LVDCR0_DMD                     (1 << 12)               /* Gen2 only */
+#define LVDCR0_LVMD_MASK               (0xf << 8)
+#define LVDCR0_LVMD_SHIFT              8
+#define LVDCR0_PLLON                   (1 << 4)
+#define LVDCR0_PWD                     (1 << 2)                /* Gen3 only */
+#define LVDCR0_BEN                     (1 << 2)                /* Gen2 only */
+#define LVDCR0_LVEN                    (1 << 1)
+#define LVDCR0_LVRES                   (1 << 0)
+
+#define LVDCR1                         0x0004
+#define LVDCR1_CKSEL                   (1 << 15)               /* Gen2 only */
+#define LVDCR1_CHSTBY(n)               (3 << (2 + (n) * 2))
+#define LVDCR1_CLKSTBY                 (3 << 0)
+
+#define LVDPLLCR                       0x0008
+/* Gen2 & V3M */
+#define LVDPLLCR_CEEN                  (1 << 14)
+#define LVDPLLCR_FBEN                  (1 << 13)
+#define LVDPLLCR_COSEL                 (1 << 12)
+#define LVDPLLCR_PLLDLYCNT_150M                (0x1bf << 0)
+#define LVDPLLCR_PLLDLYCNT_121M                (0x22c << 0)
+#define LVDPLLCR_PLLDLYCNT_60M         (0x77b << 0)
+#define LVDPLLCR_PLLDLYCNT_38M         (0x69a << 0)
+#define LVDPLLCR_PLLDLYCNT_MASK                (0x7ff << 0)
+/* Gen3 but V3M,D3 and E3 */
+#define LVDPLLCR_PLLDIVCNT_42M         (0x014cb << 0)
+#define LVDPLLCR_PLLDIVCNT_85M         (0x00a45 << 0)
+#define LVDPLLCR_PLLDIVCNT_128M                (0x006c3 << 0)
+#define LVDPLLCR_PLLDIVCNT_148M                (0x046c1 << 0)
+#define LVDPLLCR_PLLDIVCNT_MASK                (0x7ffff << 0)
+/* D3 and E3 */
+#define LVDPLLCR_PLLON                 (1 << 22)
+#define LVDPLLCR_PLLSEL_PLL0           (0 << 20)
+#define LVDPLLCR_PLLSEL_LVX            (1 << 20)
+#define LVDPLLCR_PLLSEL_PLL1           (2 << 20)
+#define LVDPLLCR_CKSEL_LVX             (1 << 17)
+#define LVDPLLCR_CKSEL_EXTAL           (3 << 17)
+#define LVDPLLCR_CKSEL_DU_DOTCLKIN(n)  ((5 + (n) * 2) << 17)
+#define LVDPLLCR_OCKSEL                        (1 << 16)
+#define LVDPLLCR_STP_CLKOUTE           (1 << 14)
+#define LVDPLLCR_OUTCLKSEL             (1 << 12)
+#define LVDPLLCR_CLKOUT                        (1 << 11)
+#define LVDPLLCR_PLLE(n)               ((n) << 10)
+#define LVDPLLCR_PLLN(n)               ((n) << 3)
+#define LVDPLLCR_PLLM(n)               ((n) << 0)
+
+#define LVDCTRCR                       0x000c
+#define LVDCTRCR_CTR3SEL_ZERO          (0 << 12)
+#define LVDCTRCR_CTR3SEL_ODD           (1 << 12)
+#define LVDCTRCR_CTR3SEL_CDE           (2 << 12)
+#define LVDCTRCR_CTR3SEL_MASK          (7 << 12)
+#define LVDCTRCR_CTR2SEL_DISP          (0 << 8)
+#define LVDCTRCR_CTR2SEL_ODD           (1 << 8)
+#define LVDCTRCR_CTR2SEL_CDE           (2 << 8)
+#define LVDCTRCR_CTR2SEL_HSYNC         (3 << 8)
+#define LVDCTRCR_CTR2SEL_VSYNC         (4 << 8)
+#define LVDCTRCR_CTR2SEL_MASK          (7 << 8)
+#define LVDCTRCR_CTR1SEL_VSYNC         (0 << 4)
+#define LVDCTRCR_CTR1SEL_DISP          (1 << 4)
+#define LVDCTRCR_CTR1SEL_ODD           (2 << 4)
+#define LVDCTRCR_CTR1SEL_CDE           (3 << 4)
+#define LVDCTRCR_CTR1SEL_HSYNC         (4 << 4)
+#define LVDCTRCR_CTR1SEL_MASK          (7 << 4)
+#define LVDCTRCR_CTR0SEL_HSYNC         (0 << 0)
+#define LVDCTRCR_CTR0SEL_VSYNC         (1 << 0)
+#define LVDCTRCR_CTR0SEL_DISP          (2 << 0)
+#define LVDCTRCR_CTR0SEL_ODD           (3 << 0)
+#define LVDCTRCR_CTR0SEL_CDE           (4 << 0)
+#define LVDCTRCR_CTR0SEL_MASK          (7 << 0)
+
+#define LVDCHCR                                0x0010
+#define LVDCHCR_CHSEL_CH(n, c)         ((((c) - (n)) & 3) << ((n) * 4))
+#define LVDCHCR_CHSEL_MASK(n)          (3 << ((n) * 4))
+
+/* All registers below are specific to D3 and E3 */
+#define LVDSTRIPE                      0x0014
+#define LVDSTRIPE_ST_TRGSEL_DISP       (0 << 2)
+#define LVDSTRIPE_ST_TRGSEL_HSYNC_R    (1 << 2)
+#define LVDSTRIPE_ST_TRGSEL_HSYNC_F    (2 << 2)
+#define LVDSTRIPE_ST_SWAP              (1 << 1)
+#define LVDSTRIPE_ST_ON                        (1 << 0)
+
+#define LVDSCR                         0x0018
+#define LVDSCR_DEPTH(n)                        (((n) - 1) << 29)
+#define LVDSCR_BANDSET                 (1 << 28)
+#define LVDSCR_TWGCNT(n)               ((((n) - 256) / 16) << 24)
+#define LVDSCR_SDIV(n)                 ((n) << 22)
+#define LVDSCR_MODE                    (1 << 21)
+#define LVDSCR_RSTN                    (1 << 20)
+
+#define LVDDIV                         0x001c
+#define LVDDIV_DIVSEL                  (1 << 8)
+#define LVDDIV_DIVRESET                        (1 << 7)
+#define LVDDIV_DIVSTP                  (1 << 6)
+#define LVDDIV_DIV(n)                  ((n) << 0)
+
+#endif /* __RCAR_LVDS_REGS_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
new file mode 100644 (file)
index 0000000..e10e4d4
--- /dev/null
@@ -0,0 +1,1106 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R-Car MIPI DSI Encoder
+ *
+ * Copyright (C) 2020 Renesas Electronics Corporation
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include "rcar_mipi_dsi.h"
+#include "rcar_mipi_dsi_regs.h"
+
+#define MHZ(v) ((u32)((v) * 1000000U))
+
+enum rcar_mipi_dsi_hw_model {
+       RCAR_DSI_V3U,
+       RCAR_DSI_V4H,
+};
+
+struct rcar_mipi_dsi_device_info {
+       enum rcar_mipi_dsi_hw_model model;
+
+       const struct dsi_clk_config *clk_cfg;
+
+       u8 clockset2_m_offset;
+
+       u8 n_min;
+       u8 n_max;
+       u8 n_mul;
+       unsigned long fpfd_min;
+       unsigned long fpfd_max;
+       u16 m_min;
+       u16 m_max;
+       unsigned long fout_min;
+       unsigned long fout_max;
+};
+
+struct rcar_mipi_dsi {
+       struct device *dev;
+       const struct rcar_mipi_dsi_device_info *info;
+       struct reset_control *rstc;
+
+       struct mipi_dsi_host host;
+       struct drm_bridge bridge;
+       struct drm_bridge *next_bridge;
+       struct drm_connector connector;
+
+       void __iomem *mmio;
+       struct {
+               struct clk *mod;
+               struct clk *pll;
+               struct clk *dsi;
+       } clocks;
+
+       enum mipi_dsi_pixel_format format;
+       unsigned int num_data_lanes;
+       unsigned int lanes;
+};
+
+struct dsi_setup_info {
+       unsigned long hsfreq;
+       u16 hsfreqrange;
+
+       unsigned long fout;
+       u16 m;
+       u16 n;
+       u16 vclk_divider;
+       const struct dsi_clk_config *clkset;
+};
+
+static inline struct rcar_mipi_dsi *
+bridge_to_rcar_mipi_dsi(struct drm_bridge *bridge)
+{
+       return container_of(bridge, struct rcar_mipi_dsi, bridge);
+}
+
+static inline struct rcar_mipi_dsi *
+host_to_rcar_mipi_dsi(struct mipi_dsi_host *host)
+{
+       return container_of(host, struct rcar_mipi_dsi, host);
+}
+
+static const u32 hsfreqrange_table[][2] = {
+       {   MHZ(80), 0x00 }, {   MHZ(90), 0x10 }, {  MHZ(100), 0x20 },
+       {  MHZ(110), 0x30 }, {  MHZ(120), 0x01 }, {  MHZ(130), 0x11 },
+       {  MHZ(140), 0x21 }, {  MHZ(150), 0x31 }, {  MHZ(160), 0x02 },
+       {  MHZ(170), 0x12 }, {  MHZ(180), 0x22 }, {  MHZ(190), 0x32 },
+       {  MHZ(205), 0x03 }, {  MHZ(220), 0x13 }, {  MHZ(235), 0x23 },
+       {  MHZ(250), 0x33 }, {  MHZ(275), 0x04 }, {  MHZ(300), 0x14 },
+       {  MHZ(325), 0x25 }, {  MHZ(350), 0x35 }, {  MHZ(400), 0x05 },
+       {  MHZ(450), 0x16 }, {  MHZ(500), 0x26 }, {  MHZ(550), 0x37 },
+       {  MHZ(600), 0x07 }, {  MHZ(650), 0x18 }, {  MHZ(700), 0x28 },
+       {  MHZ(750), 0x39 }, {  MHZ(800), 0x09 }, {  MHZ(850), 0x19 },
+       {  MHZ(900), 0x29 }, {  MHZ(950), 0x3a }, { MHZ(1000), 0x0a },
+       { MHZ(1050), 0x1a }, { MHZ(1100), 0x2a }, { MHZ(1150), 0x3b },
+       { MHZ(1200), 0x0b }, { MHZ(1250), 0x1b }, { MHZ(1300), 0x2b },
+       { MHZ(1350), 0x3c }, { MHZ(1400), 0x0c }, { MHZ(1450), 0x1c },
+       { MHZ(1500), 0x2c }, { MHZ(1550), 0x3d }, { MHZ(1600), 0x0d },
+       { MHZ(1650), 0x1d }, { MHZ(1700), 0x2e }, { MHZ(1750), 0x3e },
+       { MHZ(1800), 0x0e }, { MHZ(1850), 0x1e }, { MHZ(1900), 0x2f },
+       { MHZ(1950), 0x3f }, { MHZ(2000), 0x0f }, { MHZ(2050), 0x40 },
+       { MHZ(2100), 0x41 }, { MHZ(2150), 0x42 }, { MHZ(2200), 0x43 },
+       { MHZ(2250), 0x44 }, { MHZ(2300), 0x45 }, { MHZ(2350), 0x46 },
+       { MHZ(2400), 0x47 }, { MHZ(2450), 0x48 }, { MHZ(2500), 0x49 },
+       { /* sentinel */ },
+};
+
+struct dsi_clk_config {
+       u32 min_freq;
+       u32 max_freq;
+       u8 vco_cntrl;
+       u8 cpbias_cntrl;
+       u8 gmp_cntrl;
+       u8 int_cntrl;
+       u8 prop_cntrl;
+};
+
+static const struct dsi_clk_config dsi_clk_cfg_v3u[] = {
+       {   MHZ(40),    MHZ(55), 0x3f, 0x10, 0x01, 0x00, 0x0b },
+       {   MHZ(52.5),  MHZ(80), 0x39, 0x10, 0x01, 0x00, 0x0b },
+       {   MHZ(80),   MHZ(110), 0x2f, 0x10, 0x01, 0x00, 0x0b },
+       {  MHZ(105),   MHZ(160), 0x29, 0x10, 0x01, 0x00, 0x0b },
+       {  MHZ(160),   MHZ(220), 0x1f, 0x10, 0x01, 0x00, 0x0b },
+       {  MHZ(210),   MHZ(320), 0x19, 0x10, 0x01, 0x00, 0x0b },
+       {  MHZ(320),   MHZ(440), 0x0f, 0x10, 0x01, 0x00, 0x0b },
+       {  MHZ(420),   MHZ(660), 0x09, 0x10, 0x01, 0x00, 0x0b },
+       {  MHZ(630),  MHZ(1149), 0x03, 0x10, 0x01, 0x00, 0x0b },
+       { MHZ(1100),  MHZ(1152), 0x01, 0x10, 0x01, 0x00, 0x0b },
+       { MHZ(1150),  MHZ(1250), 0x01, 0x10, 0x01, 0x00, 0x0c },
+       { /* sentinel */ },
+};
+
+static const struct dsi_clk_config dsi_clk_cfg_v4h[] = {
+       {   MHZ(40),    MHZ(45.31),  0x2b, 0x00, 0x00, 0x08, 0x0a },
+       {   MHZ(45.31), MHZ(54.66),  0x28, 0x00, 0x00, 0x08, 0x0a },
+       {   MHZ(54.66), MHZ(62.5),   0x28, 0x00, 0x00, 0x08, 0x0a },
+       {   MHZ(62.5),  MHZ(75),     0x27, 0x00, 0x00, 0x08, 0x0a },
+       {   MHZ(75),    MHZ(90.63),  0x23, 0x00, 0x00, 0x08, 0x0a },
+       {   MHZ(90.63), MHZ(109.37), 0x20, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(109.37), MHZ(125),    0x20, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(125),    MHZ(150),    0x1f, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(150),    MHZ(181.25), 0x1b, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(181.25), MHZ(218.75), 0x18, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(218.75), MHZ(250),    0x18, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(250),    MHZ(300),    0x17, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(300),    MHZ(362.5),  0x13, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(362.5),  MHZ(455.48), 0x10, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(455.48), MHZ(500),    0x10, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(500),    MHZ(600),    0x0f, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(600),    MHZ(725),    0x0b, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(725),    MHZ(875),    0x08, 0x00, 0x00, 0x08, 0x0a },
+       {  MHZ(875),   MHZ(1000),    0x08, 0x00, 0x00, 0x08, 0x0a },
+       { MHZ(1000),   MHZ(1200),    0x07, 0x00, 0x00, 0x08, 0x0a },
+       { MHZ(1200),   MHZ(1250),    0x03, 0x00, 0x00, 0x08, 0x0a },
+       { /* sentinel */ },
+};
+
+static void rcar_mipi_dsi_write(struct rcar_mipi_dsi *dsi, u32 reg, u32 data)
+{
+       iowrite32(data, dsi->mmio + reg);
+}
+
+static u32 rcar_mipi_dsi_read(struct rcar_mipi_dsi *dsi, u32 reg)
+{
+       return ioread32(dsi->mmio + reg);
+}
+
+static void rcar_mipi_dsi_clr(struct rcar_mipi_dsi *dsi, u32 reg, u32 clr)
+{
+       rcar_mipi_dsi_write(dsi, reg, rcar_mipi_dsi_read(dsi, reg) & ~clr);
+}
+
+static void rcar_mipi_dsi_set(struct rcar_mipi_dsi *dsi, u32 reg, u32 set)
+{
+       rcar_mipi_dsi_write(dsi, reg, rcar_mipi_dsi_read(dsi, reg) | set);
+}
+
+static int rcar_mipi_dsi_write_phtw(struct rcar_mipi_dsi *dsi, u32 phtw)
+{
+       u32 status;
+       int ret;
+
+       rcar_mipi_dsi_write(dsi, PHTW, phtw);
+
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               !(status & (PHTW_DWEN | PHTW_CWEN)),
+                               2000, 10000, false, dsi, PHTW);
+       if (ret < 0) {
+               dev_err(dsi->dev, "PHY test interface write timeout (0x%08x)\n",
+                       phtw);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int rcar_mipi_dsi_write_phtw_arr(struct rcar_mipi_dsi *dsi,
+                                       const u32 *phtw, unsigned int size)
+{
+       for (unsigned int i = 0; i < size; i++) {
+               int ret = rcar_mipi_dsi_write_phtw(dsi, phtw[i]);
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+#define WRITE_PHTW(...)                                               \
+       ({                                                            \
+               static const u32 phtw[] = { __VA_ARGS__ };            \
+               int ret;                                              \
+               ret = rcar_mipi_dsi_write_phtw_arr(dsi, phtw,         \
+                                                  ARRAY_SIZE(phtw)); \
+               ret;                                                  \
+       })
+
+static int rcar_mipi_dsi_init_phtw_v3u(struct rcar_mipi_dsi *dsi)
+{
+       return WRITE_PHTW(0x01020114, 0x01600115, 0x01030116, 0x0102011d,
+                         0x011101a4, 0x018601a4, 0x014201a0, 0x010001a3,
+                         0x0101011f);
+}
+
+static int rcar_mipi_dsi_post_init_phtw_v3u(struct rcar_mipi_dsi *dsi)
+{
+       return WRITE_PHTW(0x010c0130, 0x010c0140, 0x010c0150, 0x010c0180,
+                         0x010c0190, 0x010a0160, 0x010a0170, 0x01800164,
+                         0x01800174);
+}
+
+static int rcar_mipi_dsi_init_phtw_v4h(struct rcar_mipi_dsi *dsi,
+                                      const struct dsi_setup_info *setup_info)
+{
+       int ret;
+
+       if (setup_info->hsfreq < MHZ(450)) {
+               ret = WRITE_PHTW(0x01010100, 0x011b01ac);
+               if (ret)
+                       return ret;
+       }
+
+       ret = WRITE_PHTW(0x01010100, 0x01030173, 0x01000174, 0x01500175,
+                        0x01030176, 0x01040166, 0x010201ad);
+       if (ret)
+               return ret;
+
+       if (setup_info->hsfreq <= MHZ(1000))
+               ret = WRITE_PHTW(0x01020100, 0x01910170, 0x01020171,
+                                0x01110172);
+       else if (setup_info->hsfreq <= MHZ(1500))
+               ret = WRITE_PHTW(0x01020100, 0x01980170, 0x01030171,
+                                0x01100172);
+       else if (setup_info->hsfreq <= MHZ(2500))
+               ret = WRITE_PHTW(0x01020100, 0x0144016b, 0x01000172);
+       else
+               return -EINVAL;
+
+       if (ret)
+               return ret;
+
+       if (dsi->lanes <= 1) {
+               ret = WRITE_PHTW(0x01070100, 0x010e010b);
+               if (ret)
+                       return ret;
+       }
+
+       if (dsi->lanes <= 2) {
+               ret = WRITE_PHTW(0x01090100, 0x010e010b);
+               if (ret)
+                       return ret;
+       }
+
+       if (dsi->lanes <= 3) {
+               ret = WRITE_PHTW(0x010b0100, 0x010e010b);
+               if (ret)
+                       return ret;
+       }
+
+       if (setup_info->hsfreq <= MHZ(1500)) {
+               ret = WRITE_PHTW(0x01010100, 0x01c0016e);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int
+rcar_mipi_dsi_post_init_phtw_v4h(struct rcar_mipi_dsi *dsi,
+                                const struct dsi_setup_info *setup_info)
+{
+       u32 status;
+       int ret;
+
+       if (setup_info->hsfreq <= MHZ(1500)) {
+               WRITE_PHTW(0x01020100, 0x00000180);
+
+               ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                                       status & PHTR_TEST, 2000, 10000, false,
+                                       dsi, PHTR);
+               if (ret < 0) {
+                       dev_err(dsi->dev, "failed to test PHTR\n");
+                       return ret;
+               }
+
+               WRITE_PHTW(0x01010100, 0x0100016e);
+       }
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware Setup
+ */
+
+static void rcar_mipi_dsi_pll_calc(struct rcar_mipi_dsi *dsi,
+                                  unsigned long fin_rate,
+                                  unsigned long fout_target,
+                                  struct dsi_setup_info *setup_info)
+{
+       unsigned int best_err = -1;
+       const struct rcar_mipi_dsi_device_info *info = dsi->info;
+
+       for (unsigned int n = info->n_min; n <= info->n_max; n++) {
+               unsigned long fpfd;
+
+               fpfd = fin_rate / n;
+
+               if (fpfd < info->fpfd_min || fpfd > info->fpfd_max)
+                       continue;
+
+               for (unsigned int m = info->m_min; m <= info->m_max; m++) {
+                       unsigned int err;
+                       u64 fout;
+
+                       fout = div64_u64((u64)fpfd * m, dsi->info->n_mul);
+
+                       if (fout < info->fout_min || fout > info->fout_max)
+                               continue;
+
+                       fout = div64_u64(fout, setup_info->vclk_divider);
+
+                       if (fout < setup_info->clkset->min_freq ||
+                           fout > setup_info->clkset->max_freq)
+                               continue;
+
+                       err = abs((long)(fout - fout_target) * 10000 /
+                                 (long)fout_target);
+                       if (err < best_err) {
+                               setup_info->m = m;
+                               setup_info->n = n;
+                               setup_info->fout = (unsigned long)fout;
+                               best_err = err;
+
+                               if (err == 0)
+                                       return;
+                       }
+               }
+       }
+}
+
+static void rcar_mipi_dsi_parameters_calc(struct rcar_mipi_dsi *dsi,
+                                         struct clk *clk, unsigned long target,
+                                         struct dsi_setup_info *setup_info)
+{
+
+       const struct dsi_clk_config *clk_cfg;
+       unsigned long fout_target;
+       unsigned long fin_rate;
+       unsigned int i;
+       unsigned int err;
+
+       /*
+        * Calculate Fout = dot clock * ColorDepth / (2 * Lane Count)
+        * The range out Fout is [40 - 1250] Mhz
+        */
+       fout_target = target * mipi_dsi_pixel_format_to_bpp(dsi->format)
+                   / (2 * dsi->lanes);
+       if (fout_target < MHZ(40) || fout_target > MHZ(1250))
+               return;
+
+       /* Find PLL settings */
+       for (clk_cfg = dsi->info->clk_cfg; clk_cfg->min_freq != 0; clk_cfg++) {
+               if (fout_target > clk_cfg->min_freq &&
+                   fout_target <= clk_cfg->max_freq) {
+                       setup_info->clkset = clk_cfg;
+                       break;
+               }
+       }
+
+       fin_rate = clk_get_rate(clk);
+
+       switch (dsi->info->model) {
+       case RCAR_DSI_V3U:
+       default:
+               setup_info->vclk_divider = 1 << ((clk_cfg->vco_cntrl >> 4) & 0x3);
+               break;
+
+       case RCAR_DSI_V4H:
+               setup_info->vclk_divider = 1 << (((clk_cfg->vco_cntrl >> 3) & 0x7) + 1);
+               break;
+       }
+
+       rcar_mipi_dsi_pll_calc(dsi, fin_rate, fout_target, setup_info);
+
+       /* Find hsfreqrange */
+       setup_info->hsfreq = setup_info->fout * 2;
+       for (i = 0; i < ARRAY_SIZE(hsfreqrange_table); i++) {
+               if (hsfreqrange_table[i][0] >= setup_info->hsfreq) {
+                       setup_info->hsfreqrange = hsfreqrange_table[i][1];
+                       break;
+               }
+       }
+
+       err = abs((long)(setup_info->fout - fout_target) * 10000 / (long)fout_target);
+
+       dev_dbg(dsi->dev,
+               "Fout = %u * %lu / (%u * %u * %u) = %lu (target %lu Hz, error %d.%02u%%)\n",
+               setup_info->m, fin_rate, dsi->info->n_mul, setup_info->n,
+               setup_info->vclk_divider, setup_info->fout, fout_target,
+               err / 100, err % 100);
+
+       dev_dbg(dsi->dev,
+               "vco_cntrl = 0x%x\tprop_cntrl = 0x%x\thsfreqrange = 0x%x\n",
+               clk_cfg->vco_cntrl, clk_cfg->prop_cntrl,
+               setup_info->hsfreqrange);
+}
+
+static void rcar_mipi_dsi_set_display_timing(struct rcar_mipi_dsi *dsi,
+                                            const struct drm_display_mode *mode)
+{
+       u32 setr;
+       u32 vprmset0r;
+       u32 vprmset1r;
+       u32 vprmset2r;
+       u32 vprmset3r;
+       u32 vprmset4r;
+
+       /* Configuration for Pixel Stream and Packet Header */
+       if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 24)
+               rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB24);
+       else if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 18)
+               rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB18);
+       else if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 16)
+               rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB16);
+       else {
+               dev_warn(dsi->dev, "unsupported format");
+               return;
+       }
+
+       /* Configuration for Blanking sequence and Input Pixel */
+       setr = TXVMSETR_HSABPEN_EN | TXVMSETR_HBPBPEN_EN
+            | TXVMSETR_HFPBPEN_EN | TXVMSETR_SYNSEQ_PULSES
+            | TXVMSETR_PIXWDTH | TXVMSETR_VSTPM;
+       rcar_mipi_dsi_write(dsi, TXVMSETR, setr);
+
+       /* Configuration for Video Parameters */
+       vprmset0r = (mode->flags & DRM_MODE_FLAG_PVSYNC ?
+                    TXVMVPRMSET0R_VSPOL_HIG : TXVMVPRMSET0R_VSPOL_LOW)
+                 | (mode->flags & DRM_MODE_FLAG_PHSYNC ?
+                    TXVMVPRMSET0R_HSPOL_HIG : TXVMVPRMSET0R_HSPOL_LOW)
+                 | TXVMVPRMSET0R_CSPC_RGB | TXVMVPRMSET0R_BPP_24;
+
+       vprmset1r = TXVMVPRMSET1R_VACTIVE(mode->vdisplay)
+                 | TXVMVPRMSET1R_VSA(mode->vsync_end - mode->vsync_start);
+
+       vprmset2r = TXVMVPRMSET2R_VFP(mode->vsync_start - mode->vdisplay)
+                 | TXVMVPRMSET2R_VBP(mode->vtotal - mode->vsync_end);
+
+       vprmset3r = TXVMVPRMSET3R_HACTIVE(mode->hdisplay)
+                 | TXVMVPRMSET3R_HSA(mode->hsync_end - mode->hsync_start);
+
+       vprmset4r = TXVMVPRMSET4R_HFP(mode->hsync_start - mode->hdisplay)
+                 | TXVMVPRMSET4R_HBP(mode->htotal - mode->hsync_end);
+
+       rcar_mipi_dsi_write(dsi, TXVMVPRMSET0R, vprmset0r);
+       rcar_mipi_dsi_write(dsi, TXVMVPRMSET1R, vprmset1r);
+       rcar_mipi_dsi_write(dsi, TXVMVPRMSET2R, vprmset2r);
+       rcar_mipi_dsi_write(dsi, TXVMVPRMSET3R, vprmset3r);
+       rcar_mipi_dsi_write(dsi, TXVMVPRMSET4R, vprmset4r);
+}
+
+static int rcar_mipi_dsi_startup(struct rcar_mipi_dsi *dsi,
+                                const struct drm_display_mode *mode)
+{
+       struct dsi_setup_info setup_info = {};
+       unsigned int timeout;
+       int ret;
+       int dsi_format;
+       u32 phy_setup;
+       u32 clockset2, clockset3;
+       u32 ppisetr;
+       u32 vclkset;
+
+       /* Checking valid format */
+       dsi_format = mipi_dsi_pixel_format_to_bpp(dsi->format);
+       if (dsi_format < 0) {
+               dev_warn(dsi->dev, "invalid format");
+               return -EINVAL;
+       }
+
+       /* Parameters Calculation */
+       rcar_mipi_dsi_parameters_calc(dsi, dsi->clocks.pll,
+                                     mode->clock * 1000, &setup_info);
+
+       /* LPCLK enable */
+       rcar_mipi_dsi_set(dsi, LPCLKSET, LPCLKSET_CKEN);
+
+       /* CFGCLK enabled */
+       rcar_mipi_dsi_set(dsi, CFGCLKSET, CFGCLKSET_CKEN);
+
+       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_RSTZ);
+       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_SHUTDOWNZ);
+
+       rcar_mipi_dsi_set(dsi, PHTC, PHTC_TESTCLR);
+       rcar_mipi_dsi_clr(dsi, PHTC, PHTC_TESTCLR);
+
+       /* PHY setting */
+       phy_setup = rcar_mipi_dsi_read(dsi, PHYSETUP);
+       phy_setup &= ~PHYSETUP_HSFREQRANGE_MASK;
+       phy_setup |= PHYSETUP_HSFREQRANGE(setup_info.hsfreqrange);
+       rcar_mipi_dsi_write(dsi, PHYSETUP, phy_setup);
+
+       switch (dsi->info->model) {
+       case RCAR_DSI_V3U:
+       default:
+               ret = rcar_mipi_dsi_init_phtw_v3u(dsi);
+               if (ret < 0)
+                       return ret;
+               break;
+
+       case RCAR_DSI_V4H:
+               ret = rcar_mipi_dsi_init_phtw_v4h(dsi, &setup_info);
+               if (ret < 0)
+                       return ret;
+               break;
+       }
+
+       /* PLL Clock Setting */
+       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_SHADOW_CLEAR);
+       rcar_mipi_dsi_set(dsi, CLOCKSET1, CLOCKSET1_SHADOW_CLEAR);
+       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_SHADOW_CLEAR);
+
+       clockset2 = CLOCKSET2_M(setup_info.m - dsi->info->clockset2_m_offset)
+                 | CLOCKSET2_N(setup_info.n - 1)
+                 | CLOCKSET2_VCO_CNTRL(setup_info.clkset->vco_cntrl);
+       clockset3 = CLOCKSET3_PROP_CNTRL(setup_info.clkset->prop_cntrl)
+                 | CLOCKSET3_INT_CNTRL(setup_info.clkset->int_cntrl)
+                 | CLOCKSET3_CPBIAS_CNTRL(setup_info.clkset->cpbias_cntrl)
+                 | CLOCKSET3_GMP_CNTRL(setup_info.clkset->gmp_cntrl);
+       rcar_mipi_dsi_write(dsi, CLOCKSET2, clockset2);
+       rcar_mipi_dsi_write(dsi, CLOCKSET3, clockset3);
+
+       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_UPDATEPLL);
+       rcar_mipi_dsi_set(dsi, CLOCKSET1, CLOCKSET1_UPDATEPLL);
+       udelay(10);
+       rcar_mipi_dsi_clr(dsi, CLOCKSET1, CLOCKSET1_UPDATEPLL);
+
+       ppisetr = PPISETR_DLEN_3 | PPISETR_CLEN;
+       rcar_mipi_dsi_write(dsi, PPISETR, ppisetr);
+
+       rcar_mipi_dsi_set(dsi, PHYSETUP, PHYSETUP_SHUTDOWNZ);
+       rcar_mipi_dsi_set(dsi, PHYSETUP, PHYSETUP_RSTZ);
+       usleep_range(400, 500);
+
+       /* Checking PPI clock status register */
+       for (timeout = 10; timeout > 0; --timeout) {
+               if ((rcar_mipi_dsi_read(dsi, PPICLSR) & PPICLSR_STPST) &&
+                   (rcar_mipi_dsi_read(dsi, PPIDLSR) & PPIDLSR_STPST) &&
+                   (rcar_mipi_dsi_read(dsi, CLOCKSET1) & CLOCKSET1_LOCK))
+                       break;
+
+               usleep_range(1000, 2000);
+       }
+
+       if (!timeout) {
+               dev_err(dsi->dev, "failed to enable PPI clock\n");
+               return -ETIMEDOUT;
+       }
+
+       switch (dsi->info->model) {
+       case RCAR_DSI_V3U:
+       default:
+               ret = rcar_mipi_dsi_post_init_phtw_v3u(dsi);
+               if (ret < 0)
+                       return ret;
+               break;
+
+       case RCAR_DSI_V4H:
+               ret = rcar_mipi_dsi_post_init_phtw_v4h(dsi, &setup_info);
+               if (ret < 0)
+                       return ret;
+               break;
+       }
+
+       /* Enable DOT clock */
+       vclkset = VCLKSET_CKEN;
+       rcar_mipi_dsi_write(dsi, VCLKSET, vclkset);
+
+       if (dsi_format == 24)
+               vclkset |= VCLKSET_BPP_24;
+       else if (dsi_format == 18)
+               vclkset |= VCLKSET_BPP_18;
+       else if (dsi_format == 16)
+               vclkset |= VCLKSET_BPP_16;
+       else {
+               dev_warn(dsi->dev, "unsupported format");
+               return -EINVAL;
+       }
+
+       vclkset |= VCLKSET_COLOR_RGB | VCLKSET_LANE(dsi->lanes - 1);
+
+       switch (dsi->info->model) {
+       case RCAR_DSI_V3U:
+       default:
+               vclkset |= VCLKSET_DIV_V3U(__ffs(setup_info.vclk_divider));
+               break;
+
+       case RCAR_DSI_V4H:
+               vclkset |= VCLKSET_DIV_V4H(__ffs(setup_info.vclk_divider) - 1);
+               break;
+       }
+
+       rcar_mipi_dsi_write(dsi, VCLKSET, vclkset);
+
+       /* After setting VCLKSET register, enable VCLKEN */
+       rcar_mipi_dsi_set(dsi, VCLKEN, VCLKEN_CKEN);
+
+       dev_dbg(dsi->dev, "DSI device is started\n");
+
+       return 0;
+}
+
+static void rcar_mipi_dsi_shutdown(struct rcar_mipi_dsi *dsi)
+{
+       /* Disable VCLKEN */
+       rcar_mipi_dsi_write(dsi, VCLKSET, 0);
+
+       /* Disable DOT clock */
+       rcar_mipi_dsi_write(dsi, VCLKSET, 0);
+
+       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_RSTZ);
+       rcar_mipi_dsi_clr(dsi, PHYSETUP, PHYSETUP_SHUTDOWNZ);
+
+       /* CFGCLK disable */
+       rcar_mipi_dsi_clr(dsi, CFGCLKSET, CFGCLKSET_CKEN);
+
+       /* LPCLK disable */
+       rcar_mipi_dsi_clr(dsi, LPCLKSET, LPCLKSET_CKEN);
+
+       dev_dbg(dsi->dev, "DSI device is shutdown\n");
+}
+
+static int rcar_mipi_dsi_clk_enable(struct rcar_mipi_dsi *dsi)
+{
+       int ret;
+
+       reset_control_deassert(dsi->rstc);
+
+       ret = clk_prepare_enable(dsi->clocks.mod);
+       if (ret < 0)
+               goto err_reset;
+
+       ret = clk_prepare_enable(dsi->clocks.dsi);
+       if (ret < 0)
+               goto err_clock;
+
+       return 0;
+
+err_clock:
+       clk_disable_unprepare(dsi->clocks.mod);
+err_reset:
+       reset_control_assert(dsi->rstc);
+       return ret;
+}
+
+static void rcar_mipi_dsi_clk_disable(struct rcar_mipi_dsi *dsi)
+{
+       clk_disable_unprepare(dsi->clocks.dsi);
+       clk_disable_unprepare(dsi->clocks.mod);
+
+       reset_control_assert(dsi->rstc);
+}
+
+static int rcar_mipi_dsi_start_hs_clock(struct rcar_mipi_dsi *dsi)
+{
+       /*
+        * In HW manual, we need to check TxDDRClkHS-Q Stable? but it dont
+        * write how to check. So we skip this check in this patch
+        */
+       u32 status;
+       int ret;
+
+       /* Start HS clock. */
+       rcar_mipi_dsi_set(dsi, PPICLCR, PPICLCR_TXREQHS);
+
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               status & PPICLSR_TOHS,
+                               2000, 10000, false, dsi, PPICLSR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "failed to enable HS clock\n");
+               return ret;
+       }
+
+       rcar_mipi_dsi_set(dsi, PPICLSCR, PPICLSCR_TOHS);
+
+       return 0;
+}
+
+static int rcar_mipi_dsi_start_video(struct rcar_mipi_dsi *dsi)
+{
+       u32 status;
+       int ret;
+
+       /* Wait for the link to be ready. */
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               !(status & (LINKSR_LPBUSY | LINKSR_HSBUSY)),
+                               2000, 10000, false, dsi, LINKSR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "Link failed to become ready\n");
+               return ret;
+       }
+
+       /* De-assert video FIFO clear. */
+       rcar_mipi_dsi_clr(dsi, TXVMCR, TXVMCR_VFCLR);
+
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               status & TXVMSR_VFRDY,
+                               2000, 10000, false, dsi, TXVMSR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "Failed to de-assert video FIFO clear\n");
+               return ret;
+       }
+
+       /* Enable transmission in video mode. */
+       rcar_mipi_dsi_set(dsi, TXVMCR, TXVMCR_EN_VIDEO);
+
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               status & TXVMSR_RDY,
+                               2000, 10000, false, dsi, TXVMSR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "Failed to enable video transmission\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void rcar_mipi_dsi_stop_video(struct rcar_mipi_dsi *dsi)
+{
+       u32 status;
+       int ret;
+
+       /* Disable transmission in video mode. */
+       rcar_mipi_dsi_clr(dsi, TXVMCR, TXVMCR_EN_VIDEO);
+
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               !(status & TXVMSR_ACT),
+                               2000, 100000, false, dsi, TXVMSR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "Failed to disable video transmission\n");
+               return;
+       }
+
+       /* Assert video FIFO clear. */
+       rcar_mipi_dsi_set(dsi, TXVMCR, TXVMCR_VFCLR);
+
+       ret = read_poll_timeout(rcar_mipi_dsi_read, status,
+                               !(status & TXVMSR_VFRDY),
+                               2000, 100000, false, dsi, TXVMSR);
+       if (ret < 0) {
+               dev_err(dsi->dev, "Failed to assert video FIFO clear\n");
+               return;
+       }
+}
+
+/* -----------------------------------------------------------------------------
+ * Bridge
+ */
+
+static int rcar_mipi_dsi_attach(struct drm_bridge *bridge,
+                               enum drm_bridge_attach_flags flags)
+{
+       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
+
+       return drm_bridge_attach(bridge->encoder, dsi->next_bridge, bridge,
+                                flags);
+}
+
+static void rcar_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
+                                       struct drm_bridge_state *old_bridge_state)
+{
+       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
+
+       rcar_mipi_dsi_start_video(dsi);
+}
+
+static void rcar_mipi_dsi_atomic_disable(struct drm_bridge *bridge,
+                                        struct drm_bridge_state *old_bridge_state)
+{
+       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
+
+       rcar_mipi_dsi_stop_video(dsi);
+}
+
+void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
+                              struct drm_atomic_state *state)
+{
+       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
+       const struct drm_display_mode *mode;
+       struct drm_connector *connector;
+       struct drm_crtc *crtc;
+       int ret;
+
+       connector = drm_atomic_get_new_connector_for_encoder(state,
+                                                            bridge->encoder);
+       crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+       mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;
+
+       ret = rcar_mipi_dsi_clk_enable(dsi);
+       if (ret < 0) {
+               dev_err(dsi->dev, "failed to enable DSI clocks\n");
+               return;
+       }
+
+       ret = rcar_mipi_dsi_startup(dsi, mode);
+       if (ret < 0)
+               goto err_dsi_startup;
+
+       rcar_mipi_dsi_set_display_timing(dsi, mode);
+
+       ret = rcar_mipi_dsi_start_hs_clock(dsi);
+       if (ret < 0)
+               goto err_dsi_start_hs;
+
+       return;
+
+err_dsi_start_hs:
+       rcar_mipi_dsi_shutdown(dsi);
+err_dsi_startup:
+       rcar_mipi_dsi_clk_disable(dsi);
+}
+EXPORT_SYMBOL_GPL(rcar_mipi_dsi_pclk_enable);
+
+void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge)
+{
+       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
+
+       rcar_mipi_dsi_shutdown(dsi);
+       rcar_mipi_dsi_clk_disable(dsi);
+}
+EXPORT_SYMBOL_GPL(rcar_mipi_dsi_pclk_disable);
+
+static enum drm_mode_status
+rcar_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+                               const struct drm_display_info *info,
+                               const struct drm_display_mode *mode)
+{
+       if (mode->clock > 297000)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static const struct drm_bridge_funcs rcar_mipi_dsi_bridge_ops = {
+       .attach = rcar_mipi_dsi_attach,
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset = drm_atomic_helper_bridge_reset,
+       .atomic_enable = rcar_mipi_dsi_atomic_enable,
+       .atomic_disable = rcar_mipi_dsi_atomic_disable,
+       .mode_valid = rcar_mipi_dsi_bridge_mode_valid,
+};
+
+/* -----------------------------------------------------------------------------
+ * Host setting
+ */
+
+static int rcar_mipi_dsi_host_attach(struct mipi_dsi_host *host,
+                                    struct mipi_dsi_device *device)
+{
+       struct rcar_mipi_dsi *dsi = host_to_rcar_mipi_dsi(host);
+       int ret;
+
+       if (device->lanes > dsi->num_data_lanes)
+               return -EINVAL;
+
+       dsi->lanes = device->lanes;
+       dsi->format = device->format;
+
+       dsi->next_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node,
+                                                 1, 0);
+       if (IS_ERR(dsi->next_bridge)) {
+               ret = PTR_ERR(dsi->next_bridge);
+               dev_err(dsi->dev, "failed to get next bridge: %d\n", ret);
+               return ret;
+       }
+
+       /* Initialize the DRM bridge. */
+       dsi->bridge.funcs = &rcar_mipi_dsi_bridge_ops;
+       dsi->bridge.of_node = dsi->dev->of_node;
+       drm_bridge_add(&dsi->bridge);
+
+       return 0;
+}
+
+static int rcar_mipi_dsi_host_detach(struct mipi_dsi_host *host,
+                                       struct mipi_dsi_device *device)
+{
+       struct rcar_mipi_dsi *dsi = host_to_rcar_mipi_dsi(host);
+
+       drm_bridge_remove(&dsi->bridge);
+
+       return 0;
+}
+
+static const struct mipi_dsi_host_ops rcar_mipi_dsi_host_ops = {
+       .attach = rcar_mipi_dsi_host_attach,
+       .detach = rcar_mipi_dsi_host_detach,
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int rcar_mipi_dsi_parse_dt(struct rcar_mipi_dsi *dsi)
+{
+       int ret;
+
+       ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4);
+       if (ret < 0) {
+               dev_err(dsi->dev, "missing or invalid data-lanes property\n");
+               return ret;
+       }
+
+       dsi->num_data_lanes = ret;
+       return 0;
+}
+
+static struct clk *rcar_mipi_dsi_get_clock(struct rcar_mipi_dsi *dsi,
+                                          const char *name,
+                                          bool optional)
+{
+       struct clk *clk;
+
+       clk = devm_clk_get(dsi->dev, name);
+       if (!IS_ERR(clk))
+               return clk;
+
+       if (PTR_ERR(clk) == -ENOENT && optional)
+               return NULL;
+
+       dev_err_probe(dsi->dev, PTR_ERR(clk), "failed to get %s clock\n",
+                     name ? name : "module");
+
+       return clk;
+}
+
+static int rcar_mipi_dsi_get_clocks(struct rcar_mipi_dsi *dsi)
+{
+       dsi->clocks.mod = rcar_mipi_dsi_get_clock(dsi, NULL, false);
+       if (IS_ERR(dsi->clocks.mod))
+               return PTR_ERR(dsi->clocks.mod);
+
+       dsi->clocks.pll = rcar_mipi_dsi_get_clock(dsi, "pll", true);
+       if (IS_ERR(dsi->clocks.pll))
+               return PTR_ERR(dsi->clocks.pll);
+
+       dsi->clocks.dsi = rcar_mipi_dsi_get_clock(dsi, "dsi", true);
+       if (IS_ERR(dsi->clocks.dsi))
+               return PTR_ERR(dsi->clocks.dsi);
+
+       if (!dsi->clocks.pll && !dsi->clocks.dsi) {
+               dev_err(dsi->dev, "no input clock (pll, dsi)\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rcar_mipi_dsi_probe(struct platform_device *pdev)
+{
+       struct rcar_mipi_dsi *dsi;
+       struct resource *mem;
+       int ret;
+
+       dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+       if (dsi == NULL)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, dsi);
+
+       dsi->dev = &pdev->dev;
+       dsi->info = of_device_get_match_data(&pdev->dev);
+
+       ret = rcar_mipi_dsi_parse_dt(dsi);
+       if (ret < 0)
+               return ret;
+
+       /* Acquire resources. */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dsi->mmio = devm_ioremap_resource(dsi->dev, mem);
+       if (IS_ERR(dsi->mmio))
+               return PTR_ERR(dsi->mmio);
+
+       ret = rcar_mipi_dsi_get_clocks(dsi);
+       if (ret < 0)
+               return ret;
+
+       dsi->rstc = devm_reset_control_get(dsi->dev, NULL);
+       if (IS_ERR(dsi->rstc)) {
+               dev_err(dsi->dev, "failed to get cpg reset\n");
+               return PTR_ERR(dsi->rstc);
+       }
+
+       /* Initialize the DSI host. */
+       dsi->host.dev = dsi->dev;
+       dsi->host.ops = &rcar_mipi_dsi_host_ops;
+       ret = mipi_dsi_host_register(&dsi->host);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int rcar_mipi_dsi_remove(struct platform_device *pdev)
+{
+       struct rcar_mipi_dsi *dsi = platform_get_drvdata(pdev);
+
+       mipi_dsi_host_unregister(&dsi->host);
+
+       return 0;
+}
+
+static const struct rcar_mipi_dsi_device_info v3u_data = {
+       .model = RCAR_DSI_V3U,
+       .clk_cfg = dsi_clk_cfg_v3u,
+       .clockset2_m_offset = 2,
+       .n_min = 3,
+       .n_max = 8,
+       .n_mul = 1,
+       .fpfd_min = MHZ(2),
+       .fpfd_max = MHZ(8),
+       .m_min = 64,
+       .m_max = 625,
+       .fout_min = MHZ(320),
+       .fout_max = MHZ(1250),
+};
+
+static const struct rcar_mipi_dsi_device_info v4h_data = {
+       .model = RCAR_DSI_V4H,
+       .clk_cfg = dsi_clk_cfg_v4h,
+       .clockset2_m_offset = 0,
+       .n_min = 1,
+       .n_max = 8,
+       .n_mul = 2,
+       .fpfd_min = MHZ(8),
+       .fpfd_max = MHZ(24),
+       .m_min = 167,
+       .m_max = 1000,
+       .fout_min = MHZ(2000),
+       .fout_max = MHZ(4000),
+};
+
+static const struct of_device_id rcar_mipi_dsi_of_table[] = {
+       { .compatible = "renesas,r8a779a0-dsi-csi2-tx", .data = &v3u_data },
+       { .compatible = "renesas,r8a779g0-dsi-csi2-tx", .data = &v4h_data },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, rcar_mipi_dsi_of_table);
+
+static struct platform_driver rcar_mipi_dsi_platform_driver = {
+       .probe          = rcar_mipi_dsi_probe,
+       .remove         = rcar_mipi_dsi_remove,
+       .driver         = {
+               .name   = "rcar-mipi-dsi",
+               .of_match_table = rcar_mipi_dsi_of_table,
+       },
+};
+
+module_platform_driver(rcar_mipi_dsi_platform_driver);
+
+MODULE_DESCRIPTION("Renesas R-Car MIPI DSI Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.h b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.h
new file mode 100644 (file)
index 0000000..528a196
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R-Car DSI Encoder
+ *
+ * Copyright (C) 2022 Renesas Electronics Corporation
+ *
+ * Contact: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+ */
+
+#ifndef __RCAR_MIPI_DSI_H__
+#define __RCAR_MIPI_DSI_H__
+
+struct drm_atomic_state;
+struct drm_bridge;
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_MIPI_DSI)
+void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
+                              struct drm_atomic_state *state);
+void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge);
+#else
+static inline void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
+                                            struct drm_atomic_state *state)
+{
+}
+
+static inline void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge)
+{
+}
+#endif /* CONFIG_DRM_RCAR_MIPI_DSI */
+
+#endif /* __RCAR_MIPI_DSI_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
new file mode 100644 (file)
index 0000000..f8114d1
--- /dev/null
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R-Car MIPI DSI Interface Registers Definitions
+ *
+ * Copyright (C) 2020 Renesas Electronics Corporation
+ */
+
+#ifndef __RCAR_MIPI_DSI_REGS_H__
+#define __RCAR_MIPI_DSI_REGS_H__
+
+#define LINKSR                         0x010
+#define LINKSR_LPBUSY                  (1 << 1)
+#define LINKSR_HSBUSY                  (1 << 0)
+
+/*
+ * Video Mode Register
+ */
+#define TXVMSETR                       0x180
+#define TXVMSETR_SYNSEQ_PULSES         (0 << 16)
+#define TXVMSETR_SYNSEQ_EVENTS         (1 << 16)
+#define TXVMSETR_VSTPM                 (1 << 15)
+#define TXVMSETR_PIXWDTH               (1 << 8)
+#define TXVMSETR_VSEN_EN               (1 << 4)
+#define TXVMSETR_VSEN_DIS              (0 << 4)
+#define TXVMSETR_HFPBPEN_EN            (1 << 2)
+#define TXVMSETR_HFPBPEN_DIS           (0 << 2)
+#define TXVMSETR_HBPBPEN_EN            (1 << 1)
+#define TXVMSETR_HBPBPEN_DIS           (0 << 1)
+#define TXVMSETR_HSABPEN_EN            (1 << 0)
+#define TXVMSETR_HSABPEN_DIS           (0 << 0)
+
+#define TXVMCR                         0x190
+#define TXVMCR_VFCLR                   (1 << 12)
+#define TXVMCR_EN_VIDEO                        (1 << 0)
+
+#define TXVMSR                         0x1a0
+#define TXVMSR_STR                     (1 << 16)
+#define TXVMSR_VFRDY                   (1 << 12)
+#define TXVMSR_ACT                     (1 << 8)
+#define TXVMSR_RDY                     (1 << 0)
+
+#define TXVMSCR                                0x1a4
+#define TXVMSCR_STR                    (1 << 16)
+
+#define TXVMPSPHSETR                   0x1c0
+#define TXVMPSPHSETR_DT_RGB16          (0x0e << 16)
+#define TXVMPSPHSETR_DT_RGB18          (0x1e << 16)
+#define TXVMPSPHSETR_DT_RGB18_LS       (0x2e << 16)
+#define TXVMPSPHSETR_DT_RGB24          (0x3e << 16)
+#define TXVMPSPHSETR_DT_YCBCR16                (0x2c << 16)
+
+#define TXVMVPRMSET0R                  0x1d0
+#define TXVMVPRMSET0R_HSPOL_HIG                (0 << 17)
+#define TXVMVPRMSET0R_HSPOL_LOW                (1 << 17)
+#define TXVMVPRMSET0R_VSPOL_HIG                (0 << 16)
+#define TXVMVPRMSET0R_VSPOL_LOW                (1 << 16)
+#define TXVMVPRMSET0R_CSPC_RGB         (0 << 4)
+#define TXVMVPRMSET0R_CSPC_YCbCr       (1 << 4)
+#define TXVMVPRMSET0R_BPP_16           (0 << 0)
+#define TXVMVPRMSET0R_BPP_18           (1 << 0)
+#define TXVMVPRMSET0R_BPP_24           (2 << 0)
+
+#define TXVMVPRMSET1R                  0x1d4
+#define TXVMVPRMSET1R_VACTIVE(x)       (((x) & 0x7fff) << 16)
+#define TXVMVPRMSET1R_VSA(x)           (((x) & 0xfff) << 0)
+
+#define TXVMVPRMSET2R                  0x1d8
+#define TXVMVPRMSET2R_VFP(x)           (((x) & 0x1fff) << 16)
+#define TXVMVPRMSET2R_VBP(x)           (((x) & 0x1fff) << 0)
+
+#define TXVMVPRMSET3R                  0x1dc
+#define TXVMVPRMSET3R_HACTIVE(x)       (((x) & 0x7fff) << 16)
+#define TXVMVPRMSET3R_HSA(x)           (((x) & 0xfff) << 0)
+
+#define TXVMVPRMSET4R                  0x1e0
+#define TXVMVPRMSET4R_HFP(x)           (((x) & 0x1fff) << 16)
+#define TXVMVPRMSET4R_HBP(x)           (((x) & 0x1fff) << 0)
+
+/*
+ * PHY-Protocol Interface (PPI) Registers
+ */
+#define PPISETR                                0x700
+#define PPISETR_DLEN_0                 (0x1 << 0)
+#define PPISETR_DLEN_1                 (0x3 << 0)
+#define PPISETR_DLEN_2                 (0x7 << 0)
+#define PPISETR_DLEN_3                 (0xf << 0)
+#define PPISETR_CLEN                   (1 << 8)
+
+#define PPICLCR                                0x710
+#define PPICLCR_TXREQHS                        (1 << 8)
+#define PPICLCR_TXULPSEXT              (1 << 1)
+#define PPICLCR_TXULPSCLK              (1 << 0)
+
+#define PPICLSR                                0x720
+#define PPICLSR_HSTOLP                 (1 << 27)
+#define PPICLSR_TOHS                   (1 << 26)
+#define PPICLSR_STPST                  (1 << 0)
+
+#define PPICLSCR                       0x724
+#define PPICLSCR_HSTOLP                        (1 << 27)
+#define PPICLSCR_TOHS                  (1 << 26)
+
+#define PPIDLSR                                0x760
+#define PPIDLSR_STPST                  (0xf << 0)
+
+/*
+ * Clocks registers
+ */
+#define LPCLKSET                       0x1000
+#define LPCLKSET_CKEN                  (1 << 8)
+#define LPCLKSET_LPCLKDIV(x)           (((x) & 0x3f) << 0)
+
+#define CFGCLKSET                      0x1004
+#define CFGCLKSET_CKEN                 (1 << 8)
+#define CFGCLKSET_CFGCLKDIV(x)         (((x) & 0x3f) << 0)
+
+#define DOTCLKDIV                      0x1008
+#define DOTCLKDIV_CKEN                 (1 << 8)
+#define DOTCLKDIV_DOTCLKDIV(x)         (((x) & 0x3f) << 0)
+
+#define VCLKSET                                0x100c
+#define VCLKSET_CKEN                   (1 << 16)
+#define VCLKSET_COLOR_RGB              (0 << 8)
+#define VCLKSET_COLOR_YCC              (1 << 8)
+#define VCLKSET_DIV_V3U(x)             (((x) & 0x3) << 4)
+#define VCLKSET_DIV_V4H(x)             (((x) & 0x7) << 4)
+#define VCLKSET_BPP_16                 (0 << 2)
+#define VCLKSET_BPP_18                 (1 << 2)
+#define VCLKSET_BPP_18L                        (2 << 2)
+#define VCLKSET_BPP_24                 (3 << 2)
+#define VCLKSET_LANE(x)                        (((x) & 0x3) << 0)
+
+#define VCLKEN                         0x1010
+#define VCLKEN_CKEN                    (1 << 0)
+
+#define PHYSETUP                       0x1014
+#define PHYSETUP_HSFREQRANGE(x)                (((x) & 0x7f) << 16)
+#define PHYSETUP_HSFREQRANGE_MASK      (0x7f << 16)
+#define PHYSETUP_CFGCLKFREQRANGE(x)    (((x) & 0x3f) << 8)
+#define PHYSETUP_SHUTDOWNZ             (1 << 1)
+#define PHYSETUP_RSTZ                  (1 << 0)
+
+#define CLOCKSET1                      0x101c
+#define CLOCKSET1_LOCK_PHY             (1 << 17)
+#define CLOCKSET1_LOCK                 (1 << 16)
+#define CLOCKSET1_CLKSEL               (1 << 8)
+#define CLOCKSET1_CLKINSEL_EXTAL       (0 << 2)
+#define CLOCKSET1_CLKINSEL_DIG         (1 << 2)
+#define CLOCKSET1_CLKINSEL_DU          (1 << 3)
+#define CLOCKSET1_SHADOW_CLEAR         (1 << 1)
+#define CLOCKSET1_UPDATEPLL            (1 << 0)
+
+#define CLOCKSET2                      0x1020
+#define CLOCKSET2_M(x)                 (((x) & 0xfff) << 16)
+#define CLOCKSET2_VCO_CNTRL(x)         (((x) & 0x3f) << 8)
+#define CLOCKSET2_N(x)                 (((x) & 0xf) << 0)
+
+#define CLOCKSET3                      0x1024
+#define CLOCKSET3_PROP_CNTRL(x)                (((x) & 0x3f) << 24)
+#define CLOCKSET3_INT_CNTRL(x)         (((x) & 0x3f) << 16)
+#define CLOCKSET3_CPBIAS_CNTRL(x)      (((x) & 0x7f) << 8)
+#define CLOCKSET3_GMP_CNTRL(x)         (((x) & 0x3) << 0)
+
+#define PHTW                           0x1034
+#define PHTW_DWEN                      (1 << 24)
+#define PHTW_TESTDIN_DATA(x)           (((x) & 0xff) << 16)
+#define PHTW_CWEN                      (1 << 8)
+#define PHTW_TESTDIN_CODE(x)           (((x) & 0xff) << 0)
+
+#define PHTR                           0x1038
+#define PHTR_TEST                      (1 << 16)
+
+#define PHTC                           0x103c
+#define PHTC_TESTCLR                   (1 << 0)
+
+#endif /* __RCAR_MIPI_DSI_REGS_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c
new file mode 100644 (file)
index 0000000..aa95b85
--- /dev/null
@@ -0,0 +1,816 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G2L MIPI DSI Encoder Driver
+ *
+ * Copyright (C) 2022 Renesas Electronics Corporation
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include "rzg2l_mipi_dsi_regs.h"
+
+struct rzg2l_mipi_dsi {
+       struct device *dev;
+       void __iomem *mmio;
+
+       struct reset_control *rstc;
+       struct reset_control *arstc;
+       struct reset_control *prstc;
+
+       struct mipi_dsi_host host;
+       struct drm_bridge bridge;
+       struct drm_bridge *next_bridge;
+
+       struct clk *vclk;
+
+       enum mipi_dsi_pixel_format format;
+       unsigned int num_data_lanes;
+       unsigned int lanes;
+       unsigned long mode_flags;
+};
+
+static inline struct rzg2l_mipi_dsi *
+bridge_to_rzg2l_mipi_dsi(struct drm_bridge *bridge)
+{
+       return container_of(bridge, struct rzg2l_mipi_dsi, bridge);
+}
+
+static inline struct rzg2l_mipi_dsi *
+host_to_rzg2l_mipi_dsi(struct mipi_dsi_host *host)
+{
+       return container_of(host, struct rzg2l_mipi_dsi, host);
+}
+
+struct rzg2l_mipi_dsi_timings {
+       unsigned long hsfreq_max;
+       u32 t_init;
+       u32 tclk_prepare;
+       u32 ths_prepare;
+       u32 tclk_zero;
+       u32 tclk_pre;
+       u32 tclk_post;
+       u32 tclk_trail;
+       u32 ths_zero;
+       u32 ths_trail;
+       u32 ths_exit;
+       u32 tlpx;
+};
+
+static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = {
+       {
+               .hsfreq_max = 80000,
+               .t_init = 79801,
+               .tclk_prepare = 8,
+               .ths_prepare = 13,
+               .tclk_zero = 33,
+               .tclk_pre = 24,
+               .tclk_post = 94,
+               .tclk_trail = 10,
+               .ths_zero = 23,
+               .ths_trail = 17,
+               .ths_exit = 13,
+               .tlpx = 6,
+       },
+       {
+               .hsfreq_max = 125000,
+               .t_init = 79801,
+               .tclk_prepare = 8,
+               .ths_prepare = 12,
+               .tclk_zero = 33,
+               .tclk_pre = 15,
+               .tclk_post = 94,
+               .tclk_trail = 10,
+               .ths_zero = 23,
+               .ths_trail = 17,
+               .ths_exit = 13,
+               .tlpx = 6,
+       },
+       {
+               .hsfreq_max = 250000,
+               .t_init = 79801,
+               .tclk_prepare = 8,
+               .ths_prepare = 12,
+               .tclk_zero = 33,
+               .tclk_pre = 13,
+               .tclk_post = 94,
+               .tclk_trail = 10,
+               .ths_zero = 23,
+               .ths_trail = 16,
+               .ths_exit = 13,
+               .tlpx = 6,
+       },
+       {
+               .hsfreq_max = 360000,
+               .t_init = 79801,
+               .tclk_prepare = 8,
+               .ths_prepare = 10,
+               .tclk_zero = 33,
+               .tclk_pre = 4,
+               .tclk_post = 35,
+               .tclk_trail = 7,
+               .ths_zero = 16,
+               .ths_trail = 9,
+               .ths_exit = 13,
+               .tlpx = 6,
+       },
+       {
+               .hsfreq_max = 720000,
+               .t_init = 79801,
+               .tclk_prepare = 8,
+               .ths_prepare = 9,
+               .tclk_zero = 33,
+               .tclk_pre = 4,
+               .tclk_post = 35,
+               .tclk_trail = 7,
+               .ths_zero = 16,
+               .ths_trail = 9,
+               .ths_exit = 13,
+               .tlpx = 6,
+       },
+       {
+               .hsfreq_max = 1500000,
+               .t_init = 79801,
+               .tclk_prepare = 8,
+               .ths_prepare = 9,
+               .tclk_zero = 33,
+               .tclk_pre = 4,
+               .tclk_post = 35,
+               .tclk_trail = 7,
+               .ths_zero = 16,
+               .ths_trail = 9,
+               .ths_exit = 13,
+               .tlpx = 6,
+       },
+};
+
+static void rzg2l_mipi_dsi_phy_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data)
+{
+       iowrite32(data, dsi->mmio + reg);
+}
+
+static void rzg2l_mipi_dsi_link_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data)
+{
+       iowrite32(data, dsi->mmio + LINK_REG_OFFSET + reg);
+}
+
+static u32 rzg2l_mipi_dsi_phy_read(struct rzg2l_mipi_dsi *dsi, u32 reg)
+{
+       return ioread32(dsi->mmio + reg);
+}
+
+static u32 rzg2l_mipi_dsi_link_read(struct rzg2l_mipi_dsi *dsi, u32 reg)
+{
+       return ioread32(dsi->mmio + LINK_REG_OFFSET + reg);
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware Setup
+ */
+
+static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi,
+                                   unsigned long hsfreq)
+{
+       const struct rzg2l_mipi_dsi_timings *dphy_timings;
+       unsigned int i;
+       u32 dphyctrl0;
+       u32 dphytim0;
+       u32 dphytim1;
+       u32 dphytim2;
+       u32 dphytim3;
+       int ret;
+
+       /* All DSI global operation timings are set with recommended setting */
+       for (i = 0; i < ARRAY_SIZE(rzg2l_mipi_dsi_global_timings); ++i) {
+               dphy_timings = &rzg2l_mipi_dsi_global_timings[i];
+               if (hsfreq <= dphy_timings->hsfreq_max)
+                       break;
+       }
+
+       /* Initializing DPHY before accessing LINK */
+       dphyctrl0 = DSIDPHYCTRL0_CAL_EN_HSRX_OFS | DSIDPHYCTRL0_CMN_MASTER_EN |
+                   DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 | DSIDPHYCTRL0_EN_BGR;
+
+       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0);
+       usleep_range(20, 30);
+
+       dphyctrl0 |= DSIDPHYCTRL0_EN_LDO1200;
+       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0);
+       usleep_range(10, 20);
+
+       dphytim0 = DSIDPHYTIM0_TCLK_MISS(0) |
+                  DSIDPHYTIM0_T_INIT(dphy_timings->t_init);
+       dphytim1 = DSIDPHYTIM1_THS_PREPARE(dphy_timings->ths_prepare) |
+                  DSIDPHYTIM1_TCLK_PREPARE(dphy_timings->tclk_prepare) |
+                  DSIDPHYTIM1_THS_SETTLE(0) |
+                  DSIDPHYTIM1_TCLK_SETTLE(0);
+       dphytim2 = DSIDPHYTIM2_TCLK_TRAIL(dphy_timings->tclk_trail) |
+                  DSIDPHYTIM2_TCLK_POST(dphy_timings->tclk_post) |
+                  DSIDPHYTIM2_TCLK_PRE(dphy_timings->tclk_pre) |
+                  DSIDPHYTIM2_TCLK_ZERO(dphy_timings->tclk_zero);
+       dphytim3 = DSIDPHYTIM3_TLPX(dphy_timings->tlpx) |
+                  DSIDPHYTIM3_THS_EXIT(dphy_timings->ths_exit) |
+                  DSIDPHYTIM3_THS_TRAIL(dphy_timings->ths_trail) |
+                  DSIDPHYTIM3_THS_ZERO(dphy_timings->ths_zero);
+
+       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM0, dphytim0);
+       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM1, dphytim1);
+       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM2, dphytim2);
+       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM3, dphytim3);
+
+       ret = reset_control_deassert(dsi->rstc);
+       if (ret < 0)
+               return ret;
+
+       udelay(1);
+
+       return 0;
+}
+
+static void rzg2l_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi)
+{
+       u32 dphyctrl0;
+
+       dphyctrl0 = rzg2l_mipi_dsi_phy_read(dsi, DSIDPHYCTRL0);
+
+       dphyctrl0 &= ~(DSIDPHYCTRL0_EN_LDO1200 | DSIDPHYCTRL0_EN_BGR);
+       rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0);
+
+       reset_control_assert(dsi->rstc);
+}
+
+static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi,
+                                 const struct drm_display_mode *mode)
+{
+       unsigned long hsfreq;
+       unsigned int bpp;
+       u32 txsetr;
+       u32 clstptsetr;
+       u32 lptrnstsetr;
+       u32 clkkpt;
+       u32 clkbfht;
+       u32 clkstpt;
+       u32 golpbkt;
+       int ret;
+
+       /*
+        * Relationship between hsclk and vclk must follow
+        * vclk * bpp = hsclk * 8 * lanes
+        * where vclk: video clock (Hz)
+        *       bpp: video pixel bit depth
+        *       hsclk: DSI HS Byte clock frequency (Hz)
+        *       lanes: number of data lanes
+        *
+        * hsclk(bit) = hsclk(byte) * 8
+        */
+       bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+       hsfreq = (mode->clock * bpp * 8) / (8 * dsi->lanes);
+
+       ret = pm_runtime_resume_and_get(dsi->dev);
+       if (ret < 0)
+               return ret;
+
+       clk_set_rate(dsi->vclk, mode->clock * 1000);
+
+       ret = rzg2l_mipi_dsi_dphy_init(dsi, hsfreq);
+       if (ret < 0)
+               goto err_phy;
+
+       /* Enable Data lanes and Clock lanes */
+       txsetr = TXSETR_DLEN | TXSETR_NUMLANEUSE(dsi->lanes - 1) | TXSETR_CLEN;
+       rzg2l_mipi_dsi_link_write(dsi, TXSETR, txsetr);
+
+       /*
+        * Global timings characteristic depends on high speed Clock Frequency
+        * Currently MIPI DSI-IF just supports maximum FHD@60 with:
+        * - videoclock = 148.5 (MHz)
+        * - bpp: maximum 24bpp
+        * - data lanes: maximum 4 lanes
+        * Therefore maximum hsclk will be 891 Mbps.
+        */
+       if (hsfreq > 445500) {
+               clkkpt = 12;
+               clkbfht = 15;
+               clkstpt = 48;
+               golpbkt = 75;
+       } else if (hsfreq > 250000) {
+               clkkpt = 7;
+               clkbfht = 8;
+               clkstpt = 27;
+               golpbkt = 40;
+       } else {
+               clkkpt = 8;
+               clkbfht = 6;
+               clkstpt = 24;
+               golpbkt = 29;
+       }
+
+       clstptsetr = CLSTPTSETR_CLKKPT(clkkpt) | CLSTPTSETR_CLKBFHT(clkbfht) |
+                    CLSTPTSETR_CLKSTPT(clkstpt);
+       rzg2l_mipi_dsi_link_write(dsi, CLSTPTSETR, clstptsetr);
+
+       lptrnstsetr = LPTRNSTSETR_GOLPBKT(golpbkt);
+       rzg2l_mipi_dsi_link_write(dsi, LPTRNSTSETR, lptrnstsetr);
+
+       return 0;
+
+err_phy:
+       rzg2l_mipi_dsi_dphy_exit(dsi);
+       pm_runtime_put(dsi->dev);
+
+       return ret;
+}
+
+static void rzg2l_mipi_dsi_stop(struct rzg2l_mipi_dsi *dsi)
+{
+       rzg2l_mipi_dsi_dphy_exit(dsi);
+       pm_runtime_put(dsi->dev);
+}
+
+static void rzg2l_mipi_dsi_set_display_timing(struct rzg2l_mipi_dsi *dsi,
+                                             const struct drm_display_mode *mode)
+{
+       u32 vich1ppsetr;
+       u32 vich1vssetr;
+       u32 vich1vpsetr;
+       u32 vich1hssetr;
+       u32 vich1hpsetr;
+       int dsi_format;
+       u32 delay[2];
+       u8 index;
+
+       /* Configuration for Pixel Packet */
+       dsi_format = mipi_dsi_pixel_format_to_bpp(dsi->format);
+       switch (dsi_format) {
+       case 24:
+               vich1ppsetr = VICH1PPSETR_DT_RGB24;
+               break;
+       case 18:
+               vich1ppsetr = VICH1PPSETR_DT_RGB18;
+               break;
+       }
+
+       if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) &&
+           !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST))
+               vich1ppsetr |= VICH1PPSETR_TXESYNC_PULSE;
+
+       rzg2l_mipi_dsi_link_write(dsi, VICH1PPSETR, vich1ppsetr);
+
+       /* Configuration for Video Parameters */
+       vich1vssetr = VICH1VSSETR_VACTIVE(mode->vdisplay) |
+                     VICH1VSSETR_VSA(mode->vsync_end - mode->vsync_start);
+       vich1vssetr |= (mode->flags & DRM_MODE_FLAG_PVSYNC) ?
+                       VICH1VSSETR_VSPOL_HIGH : VICH1VSSETR_VSPOL_LOW;
+
+       vich1vpsetr = VICH1VPSETR_VFP(mode->vsync_start - mode->vdisplay) |
+                     VICH1VPSETR_VBP(mode->vtotal - mode->vsync_end);
+
+       vich1hssetr = VICH1HSSETR_HACTIVE(mode->hdisplay) |
+                     VICH1HSSETR_HSA(mode->hsync_end - mode->hsync_start);
+       vich1hssetr |= (mode->flags & DRM_MODE_FLAG_PHSYNC) ?
+                       VICH1HSSETR_HSPOL_HIGH : VICH1HSSETR_HSPOL_LOW;
+
+       vich1hpsetr = VICH1HPSETR_HFP(mode->hsync_start - mode->hdisplay) |
+                     VICH1HPSETR_HBP(mode->htotal - mode->hsync_end);
+
+       rzg2l_mipi_dsi_link_write(dsi, VICH1VSSETR, vich1vssetr);
+       rzg2l_mipi_dsi_link_write(dsi, VICH1VPSETR, vich1vpsetr);
+       rzg2l_mipi_dsi_link_write(dsi, VICH1HSSETR, vich1hssetr);
+       rzg2l_mipi_dsi_link_write(dsi, VICH1HPSETR, vich1hpsetr);
+
+       /*
+        * Configuration for Delay Value
+        * Delay value based on 2 ranges of video clock.
+        * 74.25MHz is videoclock of HD@60p or FHD@30p
+        */
+       if (mode->clock > 74250) {
+               delay[0] = 231;
+               delay[1] = 216;
+       } else {
+               delay[0] = 220;
+               delay[1] = 212;
+       }
+
+       if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+               index = 0;
+       else
+               index = 1;
+
+       rzg2l_mipi_dsi_link_write(dsi, VICH1SET1R,
+                                 VICH1SET1R_DLY(delay[index]));
+}
+
+static int rzg2l_mipi_dsi_start_hs_clock(struct rzg2l_mipi_dsi *dsi)
+{
+       bool is_clk_cont;
+       u32 hsclksetr;
+       u32 status;
+       int ret;
+
+       is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS);
+
+       /* Start HS clock */
+       hsclksetr = HSCLKSETR_HSCLKRUN_HS | (is_clk_cont ?
+                                            HSCLKSETR_HSCLKMODE_CONT :
+                                            HSCLKSETR_HSCLKMODE_NON_CONT);
+       rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR, hsclksetr);
+
+       if (is_clk_cont) {
+               ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
+                                       status & PLSR_CLLP2HS,
+                                       2000, 20000, false, dsi, PLSR);
+               if (ret < 0) {
+                       dev_err(dsi->dev, "failed to start HS clock\n");
+                       return ret;
+               }
+       }
+
+       dev_dbg(dsi->dev, "Start High Speed Clock with %s clock mode",
+               is_clk_cont ? "continuous" : "non-continuous");
+
+       return 0;
+}
+
+static int rzg2l_mipi_dsi_stop_hs_clock(struct rzg2l_mipi_dsi *dsi)
+{
+       bool is_clk_cont;
+       u32 status;
+       int ret;
+
+       is_clk_cont = !(dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS);
+
+       /* Stop HS clock */
+       rzg2l_mipi_dsi_link_write(dsi, HSCLKSETR,
+                                 is_clk_cont ? HSCLKSETR_HSCLKMODE_CONT :
+                                 HSCLKSETR_HSCLKMODE_NON_CONT);
+
+       if (is_clk_cont) {
+               ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
+                                       status & PLSR_CLHS2LP,
+                                       2000, 20000, false, dsi, PLSR);
+               if (ret < 0) {
+                       dev_err(dsi->dev, "failed to stop HS clock\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int rzg2l_mipi_dsi_start_video(struct rzg2l_mipi_dsi *dsi)
+{
+       u32 vich1set0r;
+       u32 status;
+       int ret;
+
+       /* Configuration for Blanking sequence and start video input*/
+       vich1set0r = VICH1SET0R_HFPNOLP | VICH1SET0R_HBPNOLP |
+                    VICH1SET0R_HSANOLP | VICH1SET0R_VSTART;
+       rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, vich1set0r);
+
+       ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
+                               status & VICH1SR_VIRDY,
+                               2000, 20000, false, dsi, VICH1SR);
+       if (ret < 0)
+               dev_err(dsi->dev, "Failed to start video signal input\n");
+
+       return ret;
+}
+
+static int rzg2l_mipi_dsi_stop_video(struct rzg2l_mipi_dsi *dsi)
+{
+       u32 status;
+       int ret;
+
+       rzg2l_mipi_dsi_link_write(dsi, VICH1SET0R, VICH1SET0R_VSTPAFT);
+       ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
+                               (status & VICH1SR_STOP) && (!(status & VICH1SR_RUNNING)),
+                               2000, 20000, false, dsi, VICH1SR);
+       if (ret < 0)
+               goto err;
+
+       ret = read_poll_timeout(rzg2l_mipi_dsi_link_read, status,
+                               !(status & LINKSR_HSBUSY),
+                               2000, 20000, false, dsi, LINKSR);
+       if (ret < 0)
+               goto err;
+
+       return 0;
+
+err:
+       dev_err(dsi->dev, "Failed to stop video signal input\n");
+       return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Bridge
+ */
+
+static int rzg2l_mipi_dsi_attach(struct drm_bridge *bridge,
+                                enum drm_bridge_attach_flags flags)
+{
+       struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
+
+       return drm_bridge_attach(bridge->encoder, dsi->next_bridge, bridge,
+                                flags);
+}
+
+static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
+                                        struct drm_bridge_state *old_bridge_state)
+{
+       struct drm_atomic_state *state = old_bridge_state->base.state;
+       struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
+       const struct drm_display_mode *mode;
+       struct drm_connector *connector;
+       struct drm_crtc *crtc;
+       int ret;
+
+       connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+       crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+       mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;
+
+       ret = rzg2l_mipi_dsi_startup(dsi, mode);
+       if (ret < 0)
+               return;
+
+       rzg2l_mipi_dsi_set_display_timing(dsi, mode);
+
+       ret = rzg2l_mipi_dsi_start_hs_clock(dsi);
+       if (ret < 0)
+               goto err_stop;
+
+       ret = rzg2l_mipi_dsi_start_video(dsi);
+       if (ret < 0)
+               goto err_stop_clock;
+
+       return;
+
+err_stop_clock:
+       rzg2l_mipi_dsi_stop_hs_clock(dsi);
+err_stop:
+       rzg2l_mipi_dsi_stop(dsi);
+}
+
+static void rzg2l_mipi_dsi_atomic_disable(struct drm_bridge *bridge,
+                                         struct drm_bridge_state *old_bridge_state)
+{
+       struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
+
+       rzg2l_mipi_dsi_stop_video(dsi);
+       rzg2l_mipi_dsi_stop_hs_clock(dsi);
+       rzg2l_mipi_dsi_stop(dsi);
+}
+
+static enum drm_mode_status
+rzg2l_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+                                const struct drm_display_info *info,
+                                const struct drm_display_mode *mode)
+{
+       if (mode->clock > 148500)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = {
+       .attach = rzg2l_mipi_dsi_attach,
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset = drm_atomic_helper_bridge_reset,
+       .atomic_enable = rzg2l_mipi_dsi_atomic_enable,
+       .atomic_disable = rzg2l_mipi_dsi_atomic_disable,
+       .mode_valid = rzg2l_mipi_dsi_bridge_mode_valid,
+};
+
+/* -----------------------------------------------------------------------------
+ * Host setting
+ */
+
+static int rzg2l_mipi_dsi_host_attach(struct mipi_dsi_host *host,
+                                     struct mipi_dsi_device *device)
+{
+       struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host);
+       int ret;
+
+       if (device->lanes > dsi->num_data_lanes) {
+               dev_err(dsi->dev,
+                       "Number of lines of device (%u) exceeds host (%u)\n",
+                       device->lanes, dsi->num_data_lanes);
+               return -EINVAL;
+       }
+
+       switch (mipi_dsi_pixel_format_to_bpp(device->format)) {
+       case 24:
+       case 18:
+               break;
+       default:
+               dev_err(dsi->dev, "Unsupported format 0x%04x\n", device->format);
+               return -EINVAL;
+       }
+
+       dsi->lanes = device->lanes;
+       dsi->format = device->format;
+       dsi->mode_flags = device->mode_flags;
+
+       dsi->next_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node,
+                                                 1, 0);
+       if (IS_ERR(dsi->next_bridge)) {
+               ret = PTR_ERR(dsi->next_bridge);
+               dev_err(dsi->dev, "failed to get next bridge: %d\n", ret);
+               return ret;
+       }
+
+       drm_bridge_add(&dsi->bridge);
+
+       return 0;
+}
+
+static int rzg2l_mipi_dsi_host_detach(struct mipi_dsi_host *host,
+                                     struct mipi_dsi_device *device)
+{
+       struct rzg2l_mipi_dsi *dsi = host_to_rzg2l_mipi_dsi(host);
+
+       drm_bridge_remove(&dsi->bridge);
+
+       return 0;
+}
+
+static const struct mipi_dsi_host_ops rzg2l_mipi_dsi_host_ops = {
+       .attach = rzg2l_mipi_dsi_host_attach,
+       .detach = rzg2l_mipi_dsi_host_detach,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int __maybe_unused rzg2l_mipi_pm_runtime_suspend(struct device *dev)
+{
+       struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+       reset_control_assert(dsi->prstc);
+       reset_control_assert(dsi->arstc);
+
+       return 0;
+}
+
+static int __maybe_unused rzg2l_mipi_pm_runtime_resume(struct device *dev)
+{
+       struct rzg2l_mipi_dsi *dsi = dev_get_drvdata(dev);
+       int ret;
+
+       ret = reset_control_deassert(dsi->arstc);
+       if (ret < 0)
+               return ret;
+
+       ret = reset_control_deassert(dsi->prstc);
+       if (ret < 0)
+               reset_control_assert(dsi->arstc);
+
+       return ret;
+}
+
+static const struct dev_pm_ops rzg2l_mipi_pm_ops = {
+       SET_RUNTIME_PM_OPS(rzg2l_mipi_pm_runtime_suspend, rzg2l_mipi_pm_runtime_resume, NULL)
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int rzg2l_mipi_dsi_probe(struct platform_device *pdev)
+{
+       unsigned int num_data_lanes;
+       struct rzg2l_mipi_dsi *dsi;
+       u32 txsetr;
+       int ret;
+
+       dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+       if (!dsi)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, dsi);
+       dsi->dev = &pdev->dev;
+
+       ret = drm_of_get_data_lanes_count_ep(dsi->dev->of_node, 1, 0, 1, 4);
+       if (ret < 0)
+               return dev_err_probe(dsi->dev, ret,
+                                    "missing or invalid data-lanes property\n");
+
+       num_data_lanes = ret;
+
+       dsi->mmio = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(dsi->mmio))
+               return PTR_ERR(dsi->mmio);
+
+       dsi->vclk = devm_clk_get(dsi->dev, "vclk");
+       if (IS_ERR(dsi->vclk))
+               return PTR_ERR(dsi->vclk);
+
+       dsi->rstc = devm_reset_control_get_exclusive(dsi->dev, "rst");
+       if (IS_ERR(dsi->rstc))
+               return dev_err_probe(dsi->dev, PTR_ERR(dsi->rstc),
+                                    "failed to get rst\n");
+
+       dsi->arstc = devm_reset_control_get_exclusive(dsi->dev, "arst");
+       if (IS_ERR(dsi->arstc))
+               return dev_err_probe(&pdev->dev, PTR_ERR(dsi->arstc),
+                                    "failed to get arst\n");
+
+       dsi->prstc = devm_reset_control_get_exclusive(dsi->dev, "prst");
+       if (IS_ERR(dsi->prstc))
+               return dev_err_probe(dsi->dev, PTR_ERR(dsi->prstc),
+                                    "failed to get prst\n");
+
+       platform_set_drvdata(pdev, dsi);
+
+       pm_runtime_enable(dsi->dev);
+
+       ret = pm_runtime_resume_and_get(dsi->dev);
+       if (ret < 0)
+               goto err_pm_disable;
+
+       /*
+        * TXSETR register can be read only after DPHY init. But during probe
+        * mode->clock and format are not available. So initialize DPHY with
+        * timing parameters for 80Mbps.
+        */
+       ret = rzg2l_mipi_dsi_dphy_init(dsi, 80000);
+       if (ret < 0)
+               goto err_phy;
+
+       txsetr = rzg2l_mipi_dsi_link_read(dsi, TXSETR);
+       dsi->num_data_lanes = min(((txsetr >> 16) & 3) + 1, num_data_lanes);
+       rzg2l_mipi_dsi_dphy_exit(dsi);
+       pm_runtime_put(dsi->dev);
+
+       /* Initialize the DRM bridge. */
+       dsi->bridge.funcs = &rzg2l_mipi_dsi_bridge_ops;
+       dsi->bridge.of_node = dsi->dev->of_node;
+
+       /* Init host device */
+       dsi->host.dev = dsi->dev;
+       dsi->host.ops = &rzg2l_mipi_dsi_host_ops;
+       ret = mipi_dsi_host_register(&dsi->host);
+       if (ret < 0)
+               goto err_pm_disable;
+
+       return 0;
+
+err_phy:
+       rzg2l_mipi_dsi_dphy_exit(dsi);
+       pm_runtime_put(dsi->dev);
+err_pm_disable:
+       pm_runtime_disable(dsi->dev);
+       return ret;
+}
+
+static int rzg2l_mipi_dsi_remove(struct platform_device *pdev)
+{
+       struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev);
+
+       mipi_dsi_host_unregister(&dsi->host);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id rzg2l_mipi_dsi_of_table[] = {
+       { .compatible = "renesas,rzg2l-mipi-dsi" },
+       { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, rzg2l_mipi_dsi_of_table);
+
+static struct platform_driver rzg2l_mipi_dsi_platform_driver = {
+       .probe  = rzg2l_mipi_dsi_probe,
+       .remove = rzg2l_mipi_dsi_remove,
+       .driver = {
+               .name = "rzg2l-mipi-dsi",
+               .pm = &rzg2l_mipi_pm_ops,
+               .of_match_table = rzg2l_mipi_dsi_of_table,
+       },
+};
+
+module_platform_driver(rzg2l_mipi_dsi_platform_driver);
+
+MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
+MODULE_DESCRIPTION("Renesas RZ/G2L MIPI DSI Encoder Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h
new file mode 100644 (file)
index 0000000..1dbc16e
--- /dev/null
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RZ/G2L MIPI DSI Interface Registers Definitions
+ *
+ * Copyright (C) 2022 Renesas Electronics Corporation
+ */
+
+#ifndef __RZG2L_MIPI_DSI_REGS_H__
+#define __RZG2L_MIPI_DSI_REGS_H__
+
+#include <linux/bits.h>
+
+/* DPHY Registers */
+#define DSIDPHYCTRL0                   0x00
+#define DSIDPHYCTRL0_CAL_EN_HSRX_OFS   BIT(16)
+#define DSIDPHYCTRL0_CMN_MASTER_EN     BIT(8)
+#define DSIDPHYCTRL0_RE_VDD_DETVCCQLV18        BIT(2)
+#define DSIDPHYCTRL0_EN_LDO1200                BIT(1)
+#define DSIDPHYCTRL0_EN_BGR            BIT(0)
+
+#define DSIDPHYTIM0                    0x04
+#define DSIDPHYTIM0_TCLK_MISS(x)       ((x) << 24)
+#define DSIDPHYTIM0_T_INIT(x)          ((x) << 0)
+
+#define DSIDPHYTIM1                    0x08
+#define DSIDPHYTIM1_THS_PREPARE(x)     ((x) << 24)
+#define DSIDPHYTIM1_TCLK_PREPARE(x)    ((x) << 16)
+#define DSIDPHYTIM1_THS_SETTLE(x)      ((x) << 8)
+#define DSIDPHYTIM1_TCLK_SETTLE(x)     ((x) << 0)
+
+#define DSIDPHYTIM2                    0x0c
+#define DSIDPHYTIM2_TCLK_TRAIL(x)      ((x) << 24)
+#define DSIDPHYTIM2_TCLK_POST(x)       ((x) << 16)
+#define DSIDPHYTIM2_TCLK_PRE(x)                ((x) << 8)
+#define DSIDPHYTIM2_TCLK_ZERO(x)       ((x) << 0)
+
+#define DSIDPHYTIM3                    0x10
+#define DSIDPHYTIM3_TLPX(x)            ((x) << 24)
+#define DSIDPHYTIM3_THS_EXIT(x)                ((x) << 16)
+#define DSIDPHYTIM3_THS_TRAIL(x)       ((x) << 8)
+#define DSIDPHYTIM3_THS_ZERO(x)                ((x) << 0)
+
+/* --------------------------------------------------------*/
+/* Link Registers */
+#define LINK_REG_OFFSET                        0x10000
+
+/* Link Status Register */
+#define LINKSR                         0x10
+#define LINKSR_LPBUSY                  BIT(13)
+#define LINKSR_HSBUSY                  BIT(12)
+#define LINKSR_VICHRUN1                        BIT(8)
+#define LINKSR_SQCHRUN1                        BIT(4)
+#define LINKSR_SQCHRUN0                        BIT(0)
+
+/* Tx Set Register */
+#define TXSETR                         0x100
+#define TXSETR_NUMLANECAP              (0x3 << 16)
+#define TXSETR_DLEN                    (1 << 9)
+#define TXSETR_CLEN                    (1 << 8)
+#define TXSETR_NUMLANEUSE(x)           (((x) & 0x3) << 0)
+
+/* HS Clock Set Register */
+#define HSCLKSETR                      0x104
+#define HSCLKSETR_HSCLKMODE_CONT       (1 << 1)
+#define HSCLKSETR_HSCLKMODE_NON_CONT   (0 << 1)
+#define HSCLKSETR_HSCLKRUN_HS          (1 << 0)
+#define HSCLKSETR_HSCLKRUN_LP          (0 << 0)
+
+/* Reset Control Register */
+#define RSTCR                          0x110
+#define RSTCR_SWRST                    BIT(0)
+#define RSTCR_FCETXSTP                 BIT(16)
+
+/* Reset Status Register */
+#define RSTSR                          0x114
+#define RSTSR_DL0DIR                   (1 << 15)
+#define RSTSR_DLSTPST                  (0xf << 8)
+#define RSTSR_SWRSTV1                  (1 << 4)
+#define RSTSR_SWRSTIB                  (1 << 3)
+#define RSTSR_SWRSTAPB                 (1 << 2)
+#define RSTSR_SWRSTLP                  (1 << 1)
+#define RSTSR_SWRSTHS                  (1 << 0)
+
+/* Clock Lane Stop Time Set Register */
+#define CLSTPTSETR                     0x314
+#define CLSTPTSETR_CLKKPT(x)           ((x) << 24)
+#define CLSTPTSETR_CLKBFHT(x)          ((x) << 16)
+#define CLSTPTSETR_CLKSTPT(x)          ((x) << 2)
+
+/* LP Transition Time Set Register */
+#define LPTRNSTSETR                    0x318
+#define LPTRNSTSETR_GOLPBKT(x)         ((x) << 0)
+
+/* Physical Lane Status Register */
+#define PLSR                           0x320
+#define PLSR_CLHS2LP                   BIT(27)
+#define PLSR_CLLP2HS                   BIT(26)
+
+/* Video-Input Channel 1 Set 0 Register */
+#define VICH1SET0R                     0x400
+#define VICH1SET0R_VSEN                        BIT(12)
+#define VICH1SET0R_HFPNOLP             BIT(10)
+#define VICH1SET0R_HBPNOLP             BIT(9)
+#define VICH1SET0R_HSANOLP             BIT(8)
+#define VICH1SET0R_VSTPAFT             BIT(1)
+#define VICH1SET0R_VSTART              BIT(0)
+
+/* Video-Input Channel 1 Set 1 Register */
+#define VICH1SET1R                     0x404
+#define VICH1SET1R_DLY(x)              (((x) & 0xfff) << 2)
+
+/* Video-Input Channel 1 Status Register */
+#define VICH1SR                                0x410
+#define VICH1SR_VIRDY                  BIT(3)
+#define VICH1SR_RUNNING                        BIT(2)
+#define VICH1SR_STOP                   BIT(1)
+#define VICH1SR_START                  BIT(0)
+
+/* Video-Input Channel 1 Pixel Packet Set Register */
+#define VICH1PPSETR                    0x420
+#define VICH1PPSETR_DT_RGB18           (0x1e << 16)
+#define VICH1PPSETR_DT_RGB18_LS                (0x2e << 16)
+#define VICH1PPSETR_DT_RGB24           (0x3e << 16)
+#define VICH1PPSETR_TXESYNC_PULSE      (1 << 15)
+#define VICH1PPSETR_VC(x)              ((x) << 22)
+
+/* Video-Input Channel 1 Vertical Size Set Register */
+#define VICH1VSSETR                    0x428
+#define VICH1VSSETR_VACTIVE(x)         (((x) & 0x7fff) << 16)
+#define VICH1VSSETR_VSPOL_LOW          (1 << 15)
+#define VICH1VSSETR_VSPOL_HIGH         (0 << 15)
+#define VICH1VSSETR_VSA(x)             (((x) & 0xfff) << 0)
+
+/* Video-Input Channel 1 Vertical Porch Set Register */
+#define VICH1VPSETR                    0x42c
+#define VICH1VPSETR_VFP(x)             (((x) & 0x1fff) << 16)
+#define VICH1VPSETR_VBP(x)             (((x) & 0x1fff) << 0)
+
+/* Video-Input Channel 1 Horizontal Size Set Register */
+#define VICH1HSSETR                    0x430
+#define VICH1HSSETR_HACTIVE(x)         (((x) & 0x7fff) << 16)
+#define VICH1HSSETR_HSPOL_LOW          (1 << 15)
+#define VICH1HSSETR_HSPOL_HIGH         (0 << 15)
+#define VICH1HSSETR_HSA(x)             (((x) & 0xfff) << 0)
+
+/* Video-Input Channel 1 Horizontal Porch Set Register */
+#define VICH1HPSETR                    0x434
+#define VICH1HPSETR_HFP(x)             (((x) & 0x1fff) << 16)
+#define VICH1HPSETR_HBP(x)             (((x) & 0x1fff) << 0)
+
+#endif /* __RZG2L_MIPI_DSI_REGS_H__ */
diff --git a/drivers/gpu/drm/renesas/shmobile/Kconfig b/drivers/gpu/drm/renesas/shmobile/Kconfig
new file mode 100644 (file)
index 0000000..ad14112
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_SHMOBILE
+       tristate "DRM Support for SH Mobile"
+       depends on DRM
+       depends on ARCH_RENESAS || ARCH_SHMOBILE || COMPILE_TEST
+       select BACKLIGHT_CLASS_DEVICE
+       select DRM_KMS_HELPER
+       select DRM_GEM_DMA_HELPER
+       help
+         Choose this option if you have an SH Mobile chipset.
+         If M is selected the module will be called shmob-drm.
+
diff --git a/drivers/gpu/drm/renesas/shmobile/Makefile b/drivers/gpu/drm/renesas/shmobile/Makefile
new file mode 100644 (file)
index 0000000..861edaf
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+shmob-drm-y := shmob_drm_backlight.o \
+              shmob_drm_crtc.o \
+              shmob_drm_drv.o \
+              shmob_drm_kms.o \
+              shmob_drm_plane.o
+
+obj-$(CONFIG_DRM_SHMOBILE)     += shmob-drm.o
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.c
new file mode 100644 (file)
index 0000000..794573b
--- /dev/null
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * shmob_drm_backlight.c  --  SH Mobile DRM Backlight
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/backlight.h>
+
+#include "shmob_drm_backlight.h"
+#include "shmob_drm_crtc.h"
+#include "shmob_drm_drv.h"
+
+static int shmob_drm_backlight_update(struct backlight_device *bdev)
+{
+       struct shmob_drm_connector *scon = bl_get_data(bdev);
+       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
+       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
+       int brightness = backlight_get_brightness(bdev);
+
+       return bdata->set_brightness(brightness);
+}
+
+static int shmob_drm_backlight_get_brightness(struct backlight_device *bdev)
+{
+       struct shmob_drm_connector *scon = bl_get_data(bdev);
+       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
+       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
+
+       return bdata->get_brightness();
+}
+
+static const struct backlight_ops shmob_drm_backlight_ops = {
+       .options        = BL_CORE_SUSPENDRESUME,
+       .update_status  = shmob_drm_backlight_update,
+       .get_brightness = shmob_drm_backlight_get_brightness,
+};
+
+void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode)
+{
+       if (scon->backlight == NULL)
+               return;
+
+       scon->backlight->props.power = mode == DRM_MODE_DPMS_ON
+                                    ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+       backlight_update_status(scon->backlight);
+}
+
+int shmob_drm_backlight_init(struct shmob_drm_connector *scon)
+{
+       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
+       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
+       struct drm_connector *connector = &scon->connector;
+       struct drm_device *dev = connector->dev;
+       struct backlight_device *backlight;
+
+       if (!bdata->max_brightness)
+               return 0;
+
+       backlight = backlight_device_register(bdata->name, dev->dev, scon,
+                                             &shmob_drm_backlight_ops, NULL);
+       if (IS_ERR(backlight)) {
+               dev_err(dev->dev, "unable to register backlight device: %ld\n",
+                       PTR_ERR(backlight));
+               return PTR_ERR(backlight);
+       }
+
+       backlight->props.max_brightness = bdata->max_brightness;
+       backlight->props.brightness = bdata->max_brightness;
+       backlight->props.power = FB_BLANK_POWERDOWN;
+       backlight_update_status(backlight);
+
+       scon->backlight = backlight;
+       return 0;
+}
+
+void shmob_drm_backlight_exit(struct shmob_drm_connector *scon)
+{
+       backlight_device_unregister(scon->backlight);
+}
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.h b/drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.h
new file mode 100644 (file)
index 0000000..d9abb7a
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * shmob_drm_backlight.h  --  SH Mobile DRM Backlight
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __SHMOB_DRM_BACKLIGHT_H__
+#define __SHMOB_DRM_BACKLIGHT_H__
+
+struct shmob_drm_connector;
+
+void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode);
+int shmob_drm_backlight_init(struct shmob_drm_connector *scon);
+void shmob_drm_backlight_exit(struct shmob_drm_connector *scon);
+
+#endif /* __SHMOB_DRM_BACKLIGHT_H__ */
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c
new file mode 100644 (file)
index 0000000..11dd2bc
--- /dev/null
@@ -0,0 +1,712 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * shmob_drm_crtc.c  --  SH Mobile DRM CRTCs
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/backlight.h>
+#include <linux/clk.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "shmob_drm_backlight.h"
+#include "shmob_drm_crtc.h"
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_plane.h"
+#include "shmob_drm_regs.h"
+
+/*
+ * TODO: panel support
+ */
+
+/* -----------------------------------------------------------------------------
+ * Clock management
+ */
+
+static int shmob_drm_clk_on(struct shmob_drm_device *sdev)
+{
+       int ret;
+
+       if (sdev->clock) {
+               ret = clk_prepare_enable(sdev->clock);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
+{
+       if (sdev->clock)
+               clk_disable_unprepare(sdev->clock);
+}
+
+/* -----------------------------------------------------------------------------
+ * CRTC
+ */
+
+static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+       const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
+       const struct drm_display_mode *mode = &crtc->mode;
+       u32 value;
+
+       value = sdev->ldmt1r
+             | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL)
+             | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0)
+             | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0);
+       lcdc_write(sdev, LDMT1R, value);
+
+       if (idata->interface >= SHMOB_DRM_IFACE_SYS8A &&
+           idata->interface <= SHMOB_DRM_IFACE_SYS24) {
+               /* Setup SYS bus. */
+               value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT)
+                     | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0)
+                     | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0)
+                     | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT)
+                     | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT)
+                     | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT);
+               lcdc_write(sdev, LDMT2R, value);
+
+               value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT)
+                     | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT)
+                     | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT)
+                     | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT);
+               lcdc_write(sdev, LDMT3R, value);
+       }
+
+       value = ((mode->hdisplay / 8) << 16)                    /* HDCN */
+             | (mode->htotal / 8);                             /* HTCN */
+       lcdc_write(sdev, LDHCNR, value);
+
+       value = (((mode->hsync_end - mode->hsync_start) / 8) << 16) /* HSYNW */
+             | (mode->hsync_start / 8);                        /* HSYNP */
+       lcdc_write(sdev, LDHSYNR, value);
+
+       value = ((mode->hdisplay & 7) << 24) | ((mode->htotal & 7) << 16)
+             | (((mode->hsync_end - mode->hsync_start) & 7) << 8)
+             | (mode->hsync_start & 7);
+       lcdc_write(sdev, LDHAJR, value);
+
+       value = ((mode->vdisplay) << 16)                        /* VDLN */
+             | mode->vtotal;                                   /* VTLN */
+       lcdc_write(sdev, LDVLNR, value);
+
+       value = ((mode->vsync_end - mode->vsync_start) << 16)   /* VSYNW */
+             | mode->vsync_start;                              /* VSYNP */
+       lcdc_write(sdev, LDVSYNR, value);
+}
+
+static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start)
+{
+       struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private;
+       u32 value;
+
+       value = lcdc_read(sdev, LDCNT2R);
+       if (start)
+               lcdc_write(sdev, LDCNT2R, value | LDCNT2R_DO);
+       else
+               lcdc_write(sdev, LDCNT2R, value & ~LDCNT2R_DO);
+
+       /* Wait until power is applied/stopped. */
+       while (1) {
+               value = lcdc_read(sdev, LDPMR) & LDPMR_LPS;
+               if ((start && value) || (!start && !value))
+                       break;
+
+               cpu_relax();
+       }
+
+       if (!start) {
+               /* Stop the dot clock. */
+               lcdc_write(sdev, LDDCKSTPR, LDDCKSTPR_DCKSTP);
+       }
+}
+
+/*
+ * shmob_drm_crtc_start - Configure and start the LCDC
+ * @scrtc: the SH Mobile CRTC
+ *
+ * Configure and start the LCDC device. External devices (clocks, MERAM, panels,
+ * ...) are not touched by this function.
+ */
+static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+       const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
+       const struct shmob_drm_format_info *format;
+       struct drm_device *dev = sdev->ddev;
+       struct drm_plane *plane;
+       u32 value;
+       int ret;
+
+       if (scrtc->started)
+               return;
+
+       format = shmob_drm_format_info(crtc->primary->fb->format->format);
+       if (WARN_ON(format == NULL))
+               return;
+
+       /* Enable clocks before accessing the hardware. */
+       ret = shmob_drm_clk_on(sdev);
+       if (ret < 0)
+               return;
+
+       /* Reset and enable the LCDC. */
+       lcdc_write(sdev, LDCNT2R, lcdc_read(sdev, LDCNT2R) | LDCNT2R_BR);
+       lcdc_wait_bit(sdev, LDCNT2R, LDCNT2R_BR, 0);
+       lcdc_write(sdev, LDCNT2R, LDCNT2R_ME);
+
+       /* Stop the LCDC first and disable all interrupts. */
+       shmob_drm_crtc_start_stop(scrtc, false);
+       lcdc_write(sdev, LDINTR, 0);
+
+       /* Configure power supply, dot clocks and start them. */
+       lcdc_write(sdev, LDPMR, 0);
+
+       value = sdev->lddckr;
+       if (idata->clk_div) {
+               /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
+                * denominator.
+                */
+               lcdc_write(sdev, LDDCKPAT1R, 0);
+               lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1);
+
+               if (idata->clk_div == 1)
+                       value |= LDDCKR_MOSEL;
+               else
+                       value |= idata->clk_div;
+       }
+
+       lcdc_write(sdev, LDDCKR, value);
+       lcdc_write(sdev, LDDCKSTPR, 0);
+       lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0);
+
+       /* TODO: Setup SYS panel */
+
+       /* Setup geometry, format, frame buffer memory and operation mode. */
+       shmob_drm_crtc_setup_geometry(scrtc);
+
+       /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
+       lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1);
+       lcdc_write(sdev, LDMLSR, scrtc->line_size);
+       lcdc_write(sdev, LDSA1R, scrtc->dma[0]);
+       if (format->yuv)
+               lcdc_write(sdev, LDSA2R, scrtc->dma[1]);
+       lcdc_write(sdev, LDSM1R, 0);
+
+       /* Word and long word swap. */
+       switch (format->fourcc) {
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV42:
+               value = LDDDSR_LS | LDDDSR_WS;
+               break;
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV24:
+               value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
+               break;
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_XRGB8888:
+       default:
+               value = LDDDSR_LS;
+               break;
+       }
+       lcdc_write(sdev, LDDDSR, value);
+
+       /* Setup planes. */
+       drm_for_each_legacy_plane(plane, dev) {
+               if (plane->crtc == crtc)
+                       shmob_drm_plane_setup(plane);
+       }
+
+       /* Enable the display output. */
+       lcdc_write(sdev, LDCNT1R, LDCNT1R_DE);
+
+       shmob_drm_crtc_start_stop(scrtc, true);
+
+       scrtc->started = true;
+}
+
+static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+
+       if (!scrtc->started)
+               return;
+
+       /* Stop the LCDC. */
+       shmob_drm_crtc_start_stop(scrtc, false);
+
+       /* Disable the display output. */
+       lcdc_write(sdev, LDCNT1R, 0);
+
+       /* Stop clocks. */
+       shmob_drm_clk_off(sdev);
+
+       scrtc->started = false;
+}
+
+void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc)
+{
+       shmob_drm_crtc_stop(scrtc);
+}
+
+void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc)
+{
+       if (scrtc->dpms != DRM_MODE_DPMS_ON)
+               return;
+
+       shmob_drm_crtc_start(scrtc);
+}
+
+static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc,
+                                       int x, int y)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct drm_framebuffer *fb = crtc->primary->fb;
+       struct drm_gem_dma_object *gem;
+       unsigned int bpp;
+
+       bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp;
+       gem = drm_fb_dma_get_gem_obj(fb, 0);
+       scrtc->dma[0] = gem->dma_addr + fb->offsets[0]
+                     + y * fb->pitches[0] + x * bpp / 8;
+
+       if (scrtc->format->yuv) {
+               bpp = scrtc->format->bpp - 8;
+               gem = drm_fb_dma_get_gem_obj(fb, 1);
+               scrtc->dma[1] = gem->dma_addr + fb->offsets[1]
+                             + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
+                             + x * (bpp == 16 ? 2 : 1);
+       }
+}
+
+static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_crtc *crtc = &scrtc->crtc;
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+
+       shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y);
+
+       lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]);
+       if (scrtc->format->yuv)
+               lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]);
+
+       lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
+}
+
+#define to_shmob_crtc(c)       container_of(c, struct shmob_drm_crtc, crtc)
+
+static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
+
+       if (scrtc->dpms == mode)
+               return;
+
+       if (mode == DRM_MODE_DPMS_ON)
+               shmob_drm_crtc_start(scrtc);
+       else
+               shmob_drm_crtc_stop(scrtc);
+
+       scrtc->dpms = mode;
+}
+
+static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc)
+{
+       shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc,
+                                  struct drm_display_mode *mode,
+                                  struct drm_display_mode *adjusted_mode,
+                                  int x, int y,
+                                  struct drm_framebuffer *old_fb)
+{
+       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+       const struct shmob_drm_format_info *format;
+
+       format = shmob_drm_format_info(crtc->primary->fb->format->format);
+       if (format == NULL) {
+               dev_dbg(sdev->dev, "mode_set: unsupported format %p4cc\n",
+                       &crtc->primary->fb->format->format);
+               return -EINVAL;
+       }
+
+       scrtc->format = format;
+       scrtc->line_size = crtc->primary->fb->pitches[0];
+
+       shmob_drm_crtc_compute_base(scrtc, x, y);
+
+       return 0;
+}
+
+static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc)
+{
+       shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                       struct drm_framebuffer *old_fb)
+{
+       shmob_drm_crtc_update_base(to_shmob_crtc(crtc));
+
+       return 0;
+}
+
+static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+       .dpms = shmob_drm_crtc_dpms,
+       .prepare = shmob_drm_crtc_mode_prepare,
+       .commit = shmob_drm_crtc_mode_commit,
+       .mode_set = shmob_drm_crtc_mode_set,
+       .mode_set_base = shmob_drm_crtc_mode_set_base,
+};
+
+void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = scrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = scrtc->event;
+       scrtc->event = NULL;
+       if (event) {
+               drm_crtc_send_vblank_event(&scrtc->crtc, event);
+               drm_crtc_vblank_put(&scrtc->crtc);
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
+                                   struct drm_framebuffer *fb,
+                                   struct drm_pending_vblank_event *event,
+                                   uint32_t page_flip_flags,
+                                   struct drm_modeset_acquire_ctx *ctx)
+{
+       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
+       struct drm_device *dev = scrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (scrtc->event != NULL) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       crtc->primary->fb = fb;
+       shmob_drm_crtc_update_base(scrtc);
+
+       if (event) {
+               event->pipe = 0;
+               drm_crtc_vblank_get(&scrtc->crtc);
+               spin_lock_irqsave(&dev->event_lock, flags);
+               scrtc->event = event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+
+       return 0;
+}
+
+static void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev,
+                                        bool enable)
+{
+       unsigned long flags;
+       u32 ldintr;
+
+       /* Be careful not to acknowledge any pending interrupt. */
+       spin_lock_irqsave(&sdev->irq_lock, flags);
+       ldintr = lcdc_read(sdev, LDINTR) | LDINTR_STATUS_MASK;
+       if (enable)
+               ldintr |= LDINTR_VEE;
+       else
+               ldintr &= ~LDINTR_VEE;
+       lcdc_write(sdev, LDINTR, ldintr);
+       spin_unlock_irqrestore(&sdev->irq_lock, flags);
+}
+
+static int shmob_drm_enable_vblank(struct drm_crtc *crtc)
+{
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+
+       shmob_drm_crtc_enable_vblank(sdev, true);
+
+       return 0;
+}
+
+static void shmob_drm_disable_vblank(struct drm_crtc *crtc)
+{
+       struct shmob_drm_device *sdev = crtc->dev->dev_private;
+
+       shmob_drm_crtc_enable_vblank(sdev, false);
+}
+
+static const struct drm_crtc_funcs crtc_funcs = {
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_crtc_helper_set_config,
+       .page_flip = shmob_drm_crtc_page_flip,
+       .enable_vblank = shmob_drm_enable_vblank,
+       .disable_vblank = shmob_drm_disable_vblank,
+};
+
+static const uint32_t modeset_formats[] = {
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+};
+
+static const struct drm_plane_funcs primary_plane_funcs = {
+       DRM_PLANE_NON_ATOMIC_FUNCS,
+};
+
+int shmob_drm_crtc_create(struct shmob_drm_device *sdev)
+{
+       struct drm_crtc *crtc = &sdev->crtc.crtc;
+       struct drm_plane *primary;
+       int ret;
+
+       sdev->crtc.dpms = DRM_MODE_DPMS_OFF;
+
+       primary = __drm_universal_plane_alloc(sdev->ddev, sizeof(*primary), 0,
+                                             0, &primary_plane_funcs,
+                                             modeset_formats,
+                                             ARRAY_SIZE(modeset_formats),
+                                             NULL, DRM_PLANE_TYPE_PRIMARY,
+                                             NULL);
+       if (IS_ERR(primary))
+               return PTR_ERR(primary);
+
+       ret = drm_crtc_init_with_planes(sdev->ddev, crtc, primary, NULL,
+                                       &crtc_funcs, NULL);
+       if (ret < 0) {
+               drm_plane_cleanup(primary);
+               kfree(primary);
+               return ret;
+       }
+
+       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+#define to_shmob_encoder(e) \
+       container_of(e, struct shmob_drm_encoder, encoder)
+
+static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct shmob_drm_encoder *senc = to_shmob_encoder(encoder);
+       struct shmob_drm_device *sdev = encoder->dev->dev_private;
+       struct shmob_drm_connector *scon = &sdev->connector;
+
+       if (senc->dpms == mode)
+               return;
+
+       shmob_drm_backlight_dpms(scon, mode);
+
+       senc->dpms = mode;
+}
+
+static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+                                        const struct drm_display_mode *mode,
+                                        struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = encoder->dev;
+       struct shmob_drm_device *sdev = dev->dev_private;
+       struct drm_connector *connector = &sdev->connector.connector;
+       const struct drm_display_mode *panel_mode;
+
+       if (list_empty(&connector->modes)) {
+               dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
+               return false;
+       }
+
+       /* The flat panel mode is fixed, just copy it to the adjusted mode. */
+       panel_mode = list_first_entry(&connector->modes,
+                                     struct drm_display_mode, head);
+       drm_mode_copy(adjusted_mode, panel_mode);
+
+       return true;
+}
+
+static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder)
+{
+       /* No-op, everything is handled in the CRTC code. */
+}
+
+static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder,
+                                      struct drm_display_mode *mode,
+                                      struct drm_display_mode *adjusted_mode)
+{
+       /* No-op, everything is handled in the CRTC code. */
+}
+
+static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder)
+{
+       /* No-op, everything is handled in the CRTC code. */
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+       .dpms = shmob_drm_encoder_dpms,
+       .mode_fixup = shmob_drm_encoder_mode_fixup,
+       .prepare = shmob_drm_encoder_mode_prepare,
+       .commit = shmob_drm_encoder_mode_commit,
+       .mode_set = shmob_drm_encoder_mode_set,
+};
+
+int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
+{
+       struct drm_encoder *encoder = &sdev->encoder.encoder;
+       int ret;
+
+       sdev->encoder.dpms = DRM_MODE_DPMS_OFF;
+
+       encoder->possible_crtcs = 1;
+
+       ret = drm_simple_encoder_init(sdev->ddev, encoder,
+                                     DRM_MODE_ENCODER_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_encoder_helper_add(encoder, &encoder_helper_funcs);
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+#define to_shmob_connector(c) \
+       container_of(c, struct shmob_drm_connector, connector)
+
+static int shmob_drm_connector_get_modes(struct drm_connector *connector)
+{
+       struct shmob_drm_device *sdev = connector->dev->dev_private;
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_create(connector->dev);
+       if (mode == NULL)
+               return 0;
+
+       mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+       mode->clock = sdev->pdata->panel.mode.clock;
+       mode->hdisplay = sdev->pdata->panel.mode.hdisplay;
+       mode->hsync_start = sdev->pdata->panel.mode.hsync_start;
+       mode->hsync_end = sdev->pdata->panel.mode.hsync_end;
+       mode->htotal = sdev->pdata->panel.mode.htotal;
+       mode->vdisplay = sdev->pdata->panel.mode.vdisplay;
+       mode->vsync_start = sdev->pdata->panel.mode.vsync_start;
+       mode->vsync_end = sdev->pdata->panel.mode.vsync_end;
+       mode->vtotal = sdev->pdata->panel.mode.vtotal;
+       mode->flags = sdev->pdata->panel.mode.flags;
+
+       drm_mode_set_name(mode);
+       drm_mode_probed_add(connector, mode);
+
+       connector->display_info.width_mm = sdev->pdata->panel.width_mm;
+       connector->display_info.height_mm = sdev->pdata->panel.height_mm;
+
+       return 1;
+}
+
+static struct drm_encoder *
+shmob_drm_connector_best_encoder(struct drm_connector *connector)
+{
+       struct shmob_drm_connector *scon = to_shmob_connector(connector);
+
+       return scon->encoder;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = shmob_drm_connector_get_modes,
+       .best_encoder = shmob_drm_connector_best_encoder,
+};
+
+static void shmob_drm_connector_destroy(struct drm_connector *connector)
+{
+       struct shmob_drm_connector *scon = to_shmob_connector(connector);
+
+       shmob_drm_backlight_exit(scon);
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = shmob_drm_connector_destroy,
+};
+
+int shmob_drm_connector_create(struct shmob_drm_device *sdev,
+                              struct drm_encoder *encoder)
+{
+       struct drm_connector *connector = &sdev->connector.connector;
+       int ret;
+
+       sdev->connector.encoder = encoder;
+
+       connector->display_info.width_mm = sdev->pdata->panel.width_mm;
+       connector->display_info.height_mm = sdev->pdata->panel.height_mm;
+
+       ret = drm_connector_init(sdev->ddev, connector, &connector_funcs,
+                                DRM_MODE_CONNECTOR_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+
+       ret = shmob_drm_backlight_init(&sdev->connector);
+       if (ret < 0)
+               goto err_cleanup;
+
+       ret = drm_connector_attach_encoder(connector, encoder);
+       if (ret < 0)
+               goto err_backlight;
+
+       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+       drm_object_property_set_value(&connector->base,
+               sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+       return 0;
+
+err_backlight:
+       shmob_drm_backlight_exit(&sdev->connector);
+err_cleanup:
+       drm_connector_cleanup(connector);
+       return ret;
+}
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.h b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.h
new file mode 100644 (file)
index 0000000..2171884
--- /dev/null
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * shmob_drm_crtc.h  --  SH Mobile DRM CRTCs
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __SHMOB_DRM_CRTC_H__
+#define __SHMOB_DRM_CRTC_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+
+struct backlight_device;
+struct drm_pending_vblank_event;
+struct shmob_drm_device;
+struct shmob_drm_format_info;
+
+struct shmob_drm_crtc {
+       struct drm_crtc crtc;
+
+       struct drm_pending_vblank_event *event;
+       int dpms;
+
+       const struct shmob_drm_format_info *format;
+       unsigned long dma[2];
+       unsigned int line_size;
+       bool started;
+};
+
+struct shmob_drm_encoder {
+       struct drm_encoder encoder;
+       int dpms;
+};
+
+struct shmob_drm_connector {
+       struct drm_connector connector;
+       struct drm_encoder *encoder;
+
+       struct backlight_device *backlight;
+};
+
+int shmob_drm_crtc_create(struct shmob_drm_device *sdev);
+void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc);
+void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc);
+void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc);
+
+int shmob_drm_encoder_create(struct shmob_drm_device *sdev);
+int shmob_drm_connector_create(struct shmob_drm_device *sdev,
+                              struct drm_encoder *encoder);
+
+#endif /* __SHMOB_DRM_CRTC_H__ */
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c
new file mode 100644 (file)
index 0000000..30493ce
--- /dev/null
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * shmob_drm_drv.c  --  SH Mobile DRM driver
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_module.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_plane.h"
+#include "shmob_drm_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Hardware initialization
+ */
+
+static int shmob_drm_init_interface(struct shmob_drm_device *sdev)
+{
+       static const u32 ldmt1r[] = {
+               [SHMOB_DRM_IFACE_RGB8] = LDMT1R_MIFTYP_RGB8,
+               [SHMOB_DRM_IFACE_RGB9] = LDMT1R_MIFTYP_RGB9,
+               [SHMOB_DRM_IFACE_RGB12A] = LDMT1R_MIFTYP_RGB12A,
+               [SHMOB_DRM_IFACE_RGB12B] = LDMT1R_MIFTYP_RGB12B,
+               [SHMOB_DRM_IFACE_RGB16] = LDMT1R_MIFTYP_RGB16,
+               [SHMOB_DRM_IFACE_RGB18] = LDMT1R_MIFTYP_RGB18,
+               [SHMOB_DRM_IFACE_RGB24] = LDMT1R_MIFTYP_RGB24,
+               [SHMOB_DRM_IFACE_YUV422] = LDMT1R_MIFTYP_YCBCR,
+               [SHMOB_DRM_IFACE_SYS8A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A,
+               [SHMOB_DRM_IFACE_SYS8B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B,
+               [SHMOB_DRM_IFACE_SYS8C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C,
+               [SHMOB_DRM_IFACE_SYS8D] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D,
+               [SHMOB_DRM_IFACE_SYS9] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9,
+               [SHMOB_DRM_IFACE_SYS12] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12,
+               [SHMOB_DRM_IFACE_SYS16A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A,
+               [SHMOB_DRM_IFACE_SYS16B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B,
+               [SHMOB_DRM_IFACE_SYS16C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C,
+               [SHMOB_DRM_IFACE_SYS18] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18,
+               [SHMOB_DRM_IFACE_SYS24] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24,
+       };
+
+       if (sdev->pdata->iface.interface >= ARRAY_SIZE(ldmt1r)) {
+               dev_err(sdev->dev, "invalid interface type %u\n",
+                       sdev->pdata->iface.interface);
+               return -EINVAL;
+       }
+
+       sdev->ldmt1r = ldmt1r[sdev->pdata->iface.interface];
+       return 0;
+}
+
+static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
+                                           enum shmob_drm_clk_source clksrc)
+{
+       struct clk *clk;
+       char *clkname;
+
+       switch (clksrc) {
+       case SHMOB_DRM_CLK_BUS:
+               clkname = "bus_clk";
+               sdev->lddckr = LDDCKR_ICKSEL_BUS;
+               break;
+       case SHMOB_DRM_CLK_PERIPHERAL:
+               clkname = "peripheral_clk";
+               sdev->lddckr = LDDCKR_ICKSEL_MIPI;
+               break;
+       case SHMOB_DRM_CLK_EXTERNAL:
+               clkname = NULL;
+               sdev->lddckr = LDDCKR_ICKSEL_HDMI;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       clk = devm_clk_get(sdev->dev, clkname);
+       if (IS_ERR(clk)) {
+               dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);
+               return PTR_ERR(clk);
+       }
+
+       sdev->clock = clk;
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM operations
+ */
+
+static irqreturn_t shmob_drm_irq(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct shmob_drm_device *sdev = dev->dev_private;
+       unsigned long flags;
+       u32 status;
+
+       /* Acknowledge interrupts. Putting interrupt enable and interrupt flag
+        * bits in the same register is really brain-dead design and requires
+        * taking a spinlock.
+        */
+       spin_lock_irqsave(&sdev->irq_lock, flags);
+       status = lcdc_read(sdev, LDINTR);
+       lcdc_write(sdev, LDINTR, status ^ LDINTR_STATUS_MASK);
+       spin_unlock_irqrestore(&sdev->irq_lock, flags);
+
+       if (status & LDINTR_VES) {
+               drm_handle_vblank(dev, 0);
+               shmob_drm_crtc_finish_page_flip(&sdev->crtc);
+       }
+
+       return IRQ_HANDLED;
+}
+
+DEFINE_DRM_GEM_DMA_FOPS(shmob_drm_fops);
+
+static const struct drm_driver shmob_drm_driver = {
+       .driver_features        = DRIVER_GEM | DRIVER_MODESET,
+       DRM_GEM_DMA_DRIVER_OPS,
+       .fops                   = &shmob_drm_fops,
+       .name                   = "shmob-drm",
+       .desc                   = "Renesas SH Mobile DRM",
+       .date                   = "20120424",
+       .major                  = 1,
+       .minor                  = 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int shmob_drm_pm_suspend(struct device *dev)
+{
+       struct shmob_drm_device *sdev = dev_get_drvdata(dev);
+
+       drm_kms_helper_poll_disable(sdev->ddev);
+       shmob_drm_crtc_suspend(&sdev->crtc);
+
+       return 0;
+}
+
+static int shmob_drm_pm_resume(struct device *dev)
+{
+       struct shmob_drm_device *sdev = dev_get_drvdata(dev);
+
+       drm_modeset_lock_all(sdev->ddev);
+       shmob_drm_crtc_resume(&sdev->crtc);
+       drm_modeset_unlock_all(sdev->ddev);
+
+       drm_kms_helper_poll_enable(sdev->ddev);
+       return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops,
+                               shmob_drm_pm_suspend, shmob_drm_pm_resume);
+
+/* -----------------------------------------------------------------------------
+ * Platform driver
+ */
+
+static int shmob_drm_remove(struct platform_device *pdev)
+{
+       struct shmob_drm_device *sdev = platform_get_drvdata(pdev);
+       struct drm_device *ddev = sdev->ddev;
+
+       drm_dev_unregister(ddev);
+       drm_kms_helper_poll_fini(ddev);
+       free_irq(sdev->irq, ddev);
+       drm_dev_put(ddev);
+
+       return 0;
+}
+
+static int shmob_drm_probe(struct platform_device *pdev)
+{
+       struct shmob_drm_platform_data *pdata = pdev->dev.platform_data;
+       struct shmob_drm_device *sdev;
+       struct drm_device *ddev;
+       unsigned int i;
+       int ret;
+
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Allocate and initialize the driver private data, I/O resources and
+        * clocks.
+        */
+       sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
+       if (sdev == NULL)
+               return -ENOMEM;
+
+       sdev->dev = &pdev->dev;
+       sdev->pdata = pdata;
+       spin_lock_init(&sdev->irq_lock);
+
+       platform_set_drvdata(pdev, sdev);
+
+       sdev->mmio = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(sdev->mmio))
+               return PTR_ERR(sdev->mmio);
+
+       ret = shmob_drm_setup_clocks(sdev, pdata->clk_source);
+       if (ret < 0)
+               return ret;
+
+       ret = shmob_drm_init_interface(sdev);
+       if (ret < 0)
+               return ret;
+
+       /* Allocate and initialize the DRM device. */
+       ddev = drm_dev_alloc(&shmob_drm_driver, &pdev->dev);
+       if (IS_ERR(ddev))
+               return PTR_ERR(ddev);
+
+       sdev->ddev = ddev;
+       ddev->dev_private = sdev;
+
+       ret = shmob_drm_modeset_init(sdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize mode setting\n");
+               goto err_free_drm_dev;
+       }
+
+       for (i = 0; i < 4; ++i) {
+               ret = shmob_drm_plane_create(sdev, i);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to create plane %u\n", i);
+                       goto err_modeset_cleanup;
+               }
+       }
+
+       ret = drm_vblank_init(ddev, 1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize vblank\n");
+               goto err_modeset_cleanup;
+       }
+
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0)
+               goto err_modeset_cleanup;
+       sdev->irq = ret;
+
+       ret = request_irq(sdev->irq, shmob_drm_irq, 0, ddev->driver->name,
+                         ddev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to install IRQ handler\n");
+               goto err_modeset_cleanup;
+       }
+
+       /*
+        * Register the DRM device with the core and the connectors with
+        * sysfs.
+        */
+       ret = drm_dev_register(ddev, 0);
+       if (ret < 0)
+               goto err_irq_uninstall;
+
+       drm_fbdev_generic_setup(ddev, 16);
+
+       return 0;
+
+err_irq_uninstall:
+       free_irq(sdev->irq, ddev);
+err_modeset_cleanup:
+       drm_kms_helper_poll_fini(ddev);
+err_free_drm_dev:
+       drm_dev_put(ddev);
+
+       return ret;
+}
+
+static struct platform_driver shmob_drm_platform_driver = {
+       .probe          = shmob_drm_probe,
+       .remove         = shmob_drm_remove,
+       .driver         = {
+               .name   = "shmob-drm",
+               .pm     = pm_sleep_ptr(&shmob_drm_pm_ops),
+       },
+};
+
+drm_module_platform_driver(shmob_drm_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas SH Mobile DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.h b/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.h
new file mode 100644 (file)
index 0000000..4964ddd
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * shmob_drm.h  --  SH Mobile DRM driver
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __SHMOB_DRM_DRV_H__
+#define __SHMOB_DRM_DRV_H__
+
+#include <linux/kernel.h>
+#include <linux/platform_data/shmob_drm.h>
+#include <linux/spinlock.h>
+
+#include "shmob_drm_crtc.h"
+
+struct clk;
+struct device;
+struct drm_device;
+
+struct shmob_drm_device {
+       struct device *dev;
+       const struct shmob_drm_platform_data *pdata;
+
+       void __iomem *mmio;
+       struct clk *clock;
+       u32 lddckr;
+       u32 ldmt1r;
+
+       unsigned int irq;
+       spinlock_t irq_lock;            /* Protects hardware LDINTR register */
+
+       struct drm_device *ddev;
+
+       struct shmob_drm_crtc crtc;
+       struct shmob_drm_encoder encoder;
+       struct shmob_drm_connector connector;
+};
+
+#endif /* __SHMOB_DRM_DRV_H__ */
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.c
new file mode 100644 (file)
index 0000000..99381cc
--- /dev/null
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * shmob_drm_kms.c  --  SH Mobile DRM Mode Setting
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_probe_helper.h>
+
+#include "shmob_drm_crtc.h"
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct shmob_drm_format_info shmob_drm_format_infos[] = {
+       {
+               .fourcc = DRM_FORMAT_RGB565,
+               .bpp = 16,
+               .yuv = false,
+               .lddfr = LDDFR_PKF_RGB16,
+       }, {
+               .fourcc = DRM_FORMAT_RGB888,
+               .bpp = 24,
+               .yuv = false,
+               .lddfr = LDDFR_PKF_RGB24,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB8888,
+               .bpp = 32,
+               .yuv = false,
+               .lddfr = LDDFR_PKF_ARGB32,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB8888,
+               .bpp = 32,
+               .yuv = false,
+               .lddfr = LDDFR_PKF_ARGB32,
+       }, {
+               .fourcc = DRM_FORMAT_NV12,
+               .bpp = 12,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_420,
+       }, {
+               .fourcc = DRM_FORMAT_NV21,
+               .bpp = 12,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_420,
+       }, {
+               .fourcc = DRM_FORMAT_NV16,
+               .bpp = 16,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_422,
+       }, {
+               .fourcc = DRM_FORMAT_NV61,
+               .bpp = 16,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_422,
+       }, {
+               .fourcc = DRM_FORMAT_NV24,
+               .bpp = 24,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_444,
+       }, {
+               .fourcc = DRM_FORMAT_NV42,
+               .bpp = 24,
+               .yuv = true,
+               .lddfr = LDDFR_CC | LDDFR_YF_444,
+       },
+};
+
+const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(shmob_drm_format_infos); ++i) {
+               if (shmob_drm_format_infos[i].fourcc == fourcc)
+                       return &shmob_drm_format_infos[i];
+       }
+
+       return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer
+ */
+
+static struct drm_framebuffer *
+shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+                   const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       const struct shmob_drm_format_info *format;
+
+       format = shmob_drm_format_info(mode_cmd->pixel_format);
+       if (format == NULL) {
+               dev_dbg(dev->dev, "unsupported pixel format %p4cc\n",
+                       &mode_cmd->pixel_format);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (mode_cmd->pitches[0] & 7 || mode_cmd->pitches[0] >= 65536) {
+               dev_dbg(dev->dev, "invalid pitch value %u\n",
+                       mode_cmd->pitches[0]);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (format->yuv) {
+               unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1;
+
+               if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) {
+                       dev_dbg(dev->dev,
+                               "luma and chroma pitches do not match\n");
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       return drm_gem_fb_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = {
+       .fb_create = shmob_drm_fb_create,
+};
+
+int shmob_drm_modeset_init(struct shmob_drm_device *sdev)
+{
+       int ret;
+
+       ret = drmm_mode_config_init(sdev->ddev);
+       if (ret)
+               return ret;
+
+       shmob_drm_crtc_create(sdev);
+       shmob_drm_encoder_create(sdev);
+       shmob_drm_connector_create(sdev, &sdev->encoder.encoder);
+
+       drm_kms_helper_poll_init(sdev->ddev);
+
+       sdev->ddev->mode_config.min_width = 0;
+       sdev->ddev->mode_config.min_height = 0;
+       sdev->ddev->mode_config.max_width = 4095;
+       sdev->ddev->mode_config.max_height = 4095;
+       sdev->ddev->mode_config.funcs = &shmob_drm_mode_config_funcs;
+
+       drm_helper_disable_unused_functions(sdev->ddev);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.h b/drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.h
new file mode 100644 (file)
index 0000000..0347b1f
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * shmob_drm_kms.h  --  SH Mobile DRM Mode Setting
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __SHMOB_DRM_KMS_H__
+#define __SHMOB_DRM_KMS_H__
+
+#include <linux/types.h>
+
+struct drm_gem_dma_object;
+struct shmob_drm_device;
+
+struct shmob_drm_format_info {
+       u32 fourcc;
+       unsigned int bpp;
+       bool yuv;
+       u32 lddfr;
+};
+
+const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc);
+
+int shmob_drm_modeset_init(struct shmob_drm_device *sdev);
+
+#endif /* __SHMOB_DRM_KMS_H__ */
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c
new file mode 100644 (file)
index 0000000..850986c
--- /dev/null
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * shmob_drm_plane.c  --  SH Mobile DRM Planes
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_dma_helper.h>
+
+#include "shmob_drm_drv.h"
+#include "shmob_drm_kms.h"
+#include "shmob_drm_plane.h"
+#include "shmob_drm_regs.h"
+
+struct shmob_drm_plane {
+       struct drm_plane plane;
+       unsigned int index;
+       unsigned int alpha;
+
+       const struct shmob_drm_format_info *format;
+       unsigned long dma[2];
+
+       unsigned int src_x;
+       unsigned int src_y;
+       unsigned int crtc_x;
+       unsigned int crtc_y;
+       unsigned int crtc_w;
+       unsigned int crtc_h;
+};
+
+#define to_shmob_plane(p)      container_of(p, struct shmob_drm_plane, plane)
+
+static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane,
+                                        struct drm_framebuffer *fb,
+                                        int x, int y)
+{
+       struct drm_gem_dma_object *gem;
+       unsigned int bpp;
+
+       bpp = splane->format->yuv ? 8 : splane->format->bpp;
+       gem = drm_fb_dma_get_gem_obj(fb, 0);
+       splane->dma[0] = gem->dma_addr + fb->offsets[0]
+                      + y * fb->pitches[0] + x * bpp / 8;
+
+       if (splane->format->yuv) {
+               bpp = splane->format->bpp - 8;
+               gem = drm_fb_dma_get_gem_obj(fb, 1);
+               splane->dma[1] = gem->dma_addr + fb->offsets[1]
+                              + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
+                              + x * (bpp == 16 ? 2 : 1);
+       }
+}
+
+static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane,
+                                   struct drm_framebuffer *fb)
+{
+       struct shmob_drm_device *sdev = splane->plane.dev->dev_private;
+       u32 format;
+
+       /* TODO: Support ROP3 mode */
+       format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT);
+
+       switch (splane->format->fourcc) {
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV42:
+               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
+               break;
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV24:
+               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
+               break;
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_XRGB8888:
+       default:
+               format |= LDBBSIFR_SWPL;
+               break;
+       }
+
+       switch (splane->format->fourcc) {
+       case DRM_FORMAT_RGB565:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
+               break;
+       case DRM_FORMAT_RGB888:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
+               break;
+       case DRM_FORMAT_ARGB8888:
+               format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+               break;
+       case DRM_FORMAT_XRGB8888:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+               break;
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
+               break;
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV61:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
+               break;
+       case DRM_FORMAT_NV24:
+       case DRM_FORMAT_NV42:
+               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
+               break;
+       }
+
+#define plane_reg_dump(sdev, splane, reg) \
+       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \
+               splane->index, #reg, \
+               lcdc_read(sdev, reg(splane->index)), \
+               lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET))
+
+       plane_reg_dump(sdev, splane, LDBnBSIFR);
+       plane_reg_dump(sdev, splane, LDBnBSSZR);
+       plane_reg_dump(sdev, splane, LDBnBLOCR);
+       plane_reg_dump(sdev, splane, LDBnBSMWR);
+       plane_reg_dump(sdev, splane, LDBnBSAYR);
+       plane_reg_dump(sdev, splane, LDBnBSACR);
+
+       lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));
+       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
+               "LDBCR", lcdc_read(sdev, LDBCR));
+
+       lcdc_write(sdev, LDBnBSIFR(splane->index), format);
+
+       lcdc_write(sdev, LDBnBSSZR(splane->index),
+                  (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) |
+                  (splane->crtc_w << LDBBSSZR_BHSS_SHIFT));
+       lcdc_write(sdev, LDBnBLOCR(splane->index),
+                  (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) |
+                  (splane->crtc_x << LDBBLOCR_CHLC_SHIFT));
+       lcdc_write(sdev, LDBnBSMWR(splane->index),
+                  fb->pitches[0] << LDBBSMWR_BSMW_SHIFT);
+
+       shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y);
+
+       lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]);
+       if (splane->format->yuv)
+               lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]);
+
+       lcdc_write(sdev, LDBCR,
+                  LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));
+       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
+               "LDBCR", lcdc_read(sdev, LDBCR));
+
+       plane_reg_dump(sdev, splane, LDBnBSIFR);
+       plane_reg_dump(sdev, splane, LDBnBSSZR);
+       plane_reg_dump(sdev, splane, LDBnBLOCR);
+       plane_reg_dump(sdev, splane, LDBnBSMWR);
+       plane_reg_dump(sdev, splane, LDBnBSAYR);
+       plane_reg_dump(sdev, splane, LDBnBSACR);
+}
+
+void shmob_drm_plane_setup(struct drm_plane *plane)
+{
+       struct shmob_drm_plane *splane = to_shmob_plane(plane);
+
+       if (plane->fb == NULL)
+               return;
+
+       __shmob_drm_plane_setup(splane, plane->fb);
+}
+
+static int
+shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+                      struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+                      unsigned int crtc_w, unsigned int crtc_h,
+                      uint32_t src_x, uint32_t src_y,
+                      uint32_t src_w, uint32_t src_h,
+                      struct drm_modeset_acquire_ctx *ctx)
+{
+       struct shmob_drm_plane *splane = to_shmob_plane(plane);
+       struct shmob_drm_device *sdev = plane->dev->dev_private;
+       const struct shmob_drm_format_info *format;
+
+       format = shmob_drm_format_info(fb->format->format);
+       if (format == NULL) {
+               dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n",
+                       fb->format->format);
+               return -EINVAL;
+       }
+
+       if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
+               dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__);
+               return -EINVAL;
+       }
+
+       splane->format = format;
+
+       splane->src_x = src_x >> 16;
+       splane->src_y = src_y >> 16;
+       splane->crtc_x = crtc_x;
+       splane->crtc_y = crtc_y;
+       splane->crtc_w = crtc_w;
+       splane->crtc_h = crtc_h;
+
+       __shmob_drm_plane_setup(splane, fb);
+       return 0;
+}
+
+static int shmob_drm_plane_disable(struct drm_plane *plane,
+                                  struct drm_modeset_acquire_ctx *ctx)
+{
+       struct shmob_drm_plane *splane = to_shmob_plane(plane);
+       struct shmob_drm_device *sdev = plane->dev->dev_private;
+
+       splane->format = NULL;
+
+       lcdc_write(sdev, LDBnBSIFR(splane->index), 0);
+       return 0;
+}
+
+static void shmob_drm_plane_destroy(struct drm_plane *plane)
+{
+       drm_plane_force_disable(plane);
+       drm_plane_cleanup(plane);
+}
+
+static const struct drm_plane_funcs shmob_drm_plane_funcs = {
+       .update_plane = shmob_drm_plane_update,
+       .disable_plane = shmob_drm_plane_disable,
+       .destroy = shmob_drm_plane_destroy,
+};
+
+static const uint32_t formats[] = {
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+       DRM_FORMAT_NV61,
+       DRM_FORMAT_NV24,
+       DRM_FORMAT_NV42,
+};
+
+int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index)
+{
+       struct shmob_drm_plane *splane;
+       int ret;
+
+       splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL);
+       if (splane == NULL)
+               return -ENOMEM;
+
+       splane->index = index;
+       splane->alpha = 255;
+
+       ret = drm_universal_plane_init(sdev->ddev, &splane->plane, 1,
+                                      &shmob_drm_plane_funcs,
+                                      formats, ARRAY_SIZE(formats), NULL,
+                                      DRM_PLANE_TYPE_OVERLAY, NULL);
+
+       return ret;
+}
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.h b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.h
new file mode 100644 (file)
index 0000000..e72b21a
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * shmob_drm_plane.h  --  SH Mobile DRM Planes
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __SHMOB_DRM_PLANE_H__
+#define __SHMOB_DRM_PLANE_H__
+
+struct drm_plane;
+struct shmob_drm_device;
+
+int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index);
+void shmob_drm_plane_setup(struct drm_plane *plane);
+
+#endif /* __SHMOB_DRM_PLANE_H__ */
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_regs.h b/drivers/gpu/drm/renesas/shmobile/shmob_drm_regs.h
new file mode 100644 (file)
index 0000000..0585336
--- /dev/null
@@ -0,0 +1,310 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * shmob_drm_regs.h  --  SH Mobile DRM registers
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ *
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#ifndef __SHMOB_DRM_REGS_H__
+#define __SHMOB_DRM_REGS_H__
+
+#include <linux/io.h>
+#include <linux/jiffies.h>
+
+#include "shmob_drm_drv.h"
+
+/* Register definitions */
+#define LDDCKPAT1R             0x400
+#define LDDCKPAT2R             0x404
+#define LDDCKR                 0x410
+#define LDDCKR_ICKSEL_BUS      (0 << 16)
+#define LDDCKR_ICKSEL_MIPI     (1 << 16)
+#define LDDCKR_ICKSEL_HDMI     (2 << 16)
+#define LDDCKR_ICKSEL_EXT      (3 << 16)
+#define LDDCKR_ICKSEL_MASK     (7 << 16)
+#define LDDCKR_MOSEL           (1 << 6)
+#define LDDCKSTPR              0x414
+#define LDDCKSTPR_DCKSTS       (1 << 16)
+#define LDDCKSTPR_DCKSTP       (1 << 0)
+#define LDMT1R                 0x418
+#define LDMT1R_VPOL            (1 << 28)
+#define LDMT1R_HPOL            (1 << 27)
+#define LDMT1R_DWPOL           (1 << 26)
+#define LDMT1R_DIPOL           (1 << 25)
+#define LDMT1R_DAPOL           (1 << 24)
+#define LDMT1R_HSCNT           (1 << 17)
+#define LDMT1R_DWCNT           (1 << 16)
+#define LDMT1R_IFM             (1 << 12)
+#define LDMT1R_MIFTYP_RGB8     (0x0 << 0)
+#define LDMT1R_MIFTYP_RGB9     (0x4 << 0)
+#define LDMT1R_MIFTYP_RGB12A   (0x5 << 0)
+#define LDMT1R_MIFTYP_RGB12B   (0x6 << 0)
+#define LDMT1R_MIFTYP_RGB16    (0x7 << 0)
+#define LDMT1R_MIFTYP_RGB18    (0xa << 0)
+#define LDMT1R_MIFTYP_RGB24    (0xb << 0)
+#define LDMT1R_MIFTYP_YCBCR    (0xf << 0)
+#define LDMT1R_MIFTYP_SYS8A    (0x0 << 0)
+#define LDMT1R_MIFTYP_SYS8B    (0x1 << 0)
+#define LDMT1R_MIFTYP_SYS8C    (0x2 << 0)
+#define LDMT1R_MIFTYP_SYS8D    (0x3 << 0)
+#define LDMT1R_MIFTYP_SYS9     (0x4 << 0)
+#define LDMT1R_MIFTYP_SYS12    (0x5 << 0)
+#define LDMT1R_MIFTYP_SYS16A   (0x7 << 0)
+#define LDMT1R_MIFTYP_SYS16B   (0x8 << 0)
+#define LDMT1R_MIFTYP_SYS16C   (0x9 << 0)
+#define LDMT1R_MIFTYP_SYS18    (0xa << 0)
+#define LDMT1R_MIFTYP_SYS24    (0xb << 0)
+#define LDMT1R_MIFTYP_MASK     (0xf << 0)
+#define LDMT2R                 0x41c
+#define LDMT2R_CSUP_MASK       (7 << 26)
+#define LDMT2R_CSUP_SHIFT      26
+#define LDMT2R_RSV             (1 << 25)
+#define LDMT2R_VSEL            (1 << 24)
+#define LDMT2R_WCSC_MASK       (0xff << 16)
+#define LDMT2R_WCSC_SHIFT      16
+#define LDMT2R_WCEC_MASK       (0xff << 8)
+#define LDMT2R_WCEC_SHIFT      8
+#define LDMT2R_WCLW_MASK       (0xff << 0)
+#define LDMT2R_WCLW_SHIFT      0
+#define LDMT3R                 0x420
+#define LDMT3R_RDLC_MASK       (0x3f << 24)
+#define LDMT3R_RDLC_SHIFT      24
+#define LDMT3R_RCSC_MASK       (0xff << 16)
+#define LDMT3R_RCSC_SHIFT      16
+#define LDMT3R_RCEC_MASK       (0xff << 8)
+#define LDMT3R_RCEC_SHIFT      8
+#define LDMT3R_RCLW_MASK       (0xff << 0)
+#define LDMT3R_RCLW_SHIFT      0
+#define LDDFR                  0x424
+#define LDDFR_CF1              (1 << 18)
+#define LDDFR_CF0              (1 << 17)
+#define LDDFR_CC               (1 << 16)
+#define LDDFR_YF_420           (0 << 8)
+#define LDDFR_YF_422           (1 << 8)
+#define LDDFR_YF_444           (2 << 8)
+#define LDDFR_YF_MASK          (3 << 8)
+#define LDDFR_PKF_ARGB32       (0x00 << 0)
+#define LDDFR_PKF_RGB16                (0x03 << 0)
+#define LDDFR_PKF_RGB24                (0x0b << 0)
+#define LDDFR_PKF_MASK         (0x1f << 0)
+#define LDSM1R                 0x428
+#define LDSM1R_OS              (1 << 0)
+#define LDSM2R                 0x42c
+#define LDSM2R_OSTRG           (1 << 0)
+#define LDSA1R                 0x430
+#define LDSA2R                 0x434
+#define LDMLSR                 0x438
+#define LDWBFR                 0x43c
+#define LDWBCNTR               0x440
+#define LDWBAR                 0x444
+#define LDHCNR                 0x448
+#define LDHSYNR                        0x44c
+#define LDVLNR                 0x450
+#define LDVSYNR                        0x454
+#define LDHPDR                 0x458
+#define LDVPDR                 0x45c
+#define LDPMR                  0x460
+#define LDPMR_LPS              (3 << 0)
+#define LDINTR                 0x468
+#define LDINTR_FE              (1 << 10)
+#define LDINTR_VSE             (1 << 9)
+#define LDINTR_VEE             (1 << 8)
+#define LDINTR_FS              (1 << 2)
+#define LDINTR_VSS             (1 << 1)
+#define LDINTR_VES             (1 << 0)
+#define LDINTR_STATUS_MASK     (0xff << 0)
+#define LDSR                   0x46c
+#define LDSR_MSS               (1 << 10)
+#define LDSR_MRS               (1 << 8)
+#define LDSR_AS                        (1 << 1)
+#define LDCNT1R                        0x470
+#define LDCNT1R_DE             (1 << 0)
+#define LDCNT2R                        0x474
+#define LDCNT2R_BR             (1 << 8)
+#define LDCNT2R_MD             (1 << 3)
+#define LDCNT2R_SE             (1 << 2)
+#define LDCNT2R_ME             (1 << 1)
+#define LDCNT2R_DO             (1 << 0)
+#define LDRCNTR                        0x478
+#define LDRCNTR_SRS            (1 << 17)
+#define LDRCNTR_SRC            (1 << 16)
+#define LDRCNTR_MRS            (1 << 1)
+#define LDRCNTR_MRC            (1 << 0)
+#define LDDDSR                 0x47c
+#define LDDDSR_LS              (1 << 2)
+#define LDDDSR_WS              (1 << 1)
+#define LDDDSR_BS              (1 << 0)
+#define LDHAJR                 0x4a0
+
+#define LDDWD0R                        0x800
+#define LDDWDxR_WDACT          (1 << 28)
+#define LDDWDxR_RSW            (1 << 24)
+#define LDDRDR                 0x840
+#define LDDRDR_RSR             (1 << 24)
+#define LDDRDR_DRD_MASK                (0x3ffff << 0)
+#define LDDWAR                 0x900
+#define LDDWAR_WA              (1 << 0)
+#define LDDRAR                 0x904
+#define LDDRAR_RA              (1 << 0)
+
+#define LDBCR                  0xb00
+#define LDBCR_UPC(n)           (1 << ((n) + 16))
+#define LDBCR_UPF(n)           (1 << ((n) + 8))
+#define LDBCR_UPD(n)           (1 << ((n) + 0))
+#define LDBnBSIFR(n)           (0xb20 + (n) * 0x20 + 0x00)
+#define LDBBSIFR_EN            (1 << 31)
+#define LDBBSIFR_VS            (1 << 29)
+#define LDBBSIFR_BRSEL         (1 << 28)
+#define LDBBSIFR_MX            (1 << 27)
+#define LDBBSIFR_MY            (1 << 26)
+#define LDBBSIFR_CV3           (3 << 24)
+#define LDBBSIFR_CV2           (2 << 24)
+#define LDBBSIFR_CV1           (1 << 24)
+#define LDBBSIFR_CV0           (0 << 24)
+#define LDBBSIFR_CV_MASK       (3 << 24)
+#define LDBBSIFR_LAY_MASK      (0xff << 16)
+#define LDBBSIFR_LAY_SHIFT     16
+#define LDBBSIFR_ROP3_MASK     (0xff << 16)
+#define LDBBSIFR_ROP3_SHIFT    16
+#define LDBBSIFR_AL_PL8                (3 << 14)
+#define LDBBSIFR_AL_PL1                (2 << 14)
+#define LDBBSIFR_AL_PK         (1 << 14)
+#define LDBBSIFR_AL_1          (0 << 14)
+#define LDBBSIFR_AL_MASK       (3 << 14)
+#define LDBBSIFR_SWPL          (1 << 10)
+#define LDBBSIFR_SWPW          (1 << 9)
+#define LDBBSIFR_SWPB          (1 << 8)
+#define LDBBSIFR_RY            (1 << 7)
+#define LDBBSIFR_CHRR_420      (2 << 0)
+#define LDBBSIFR_CHRR_422      (1 << 0)
+#define LDBBSIFR_CHRR_444      (0 << 0)
+#define LDBBSIFR_RPKF_ARGB32   (0x00 << 0)
+#define LDBBSIFR_RPKF_RGB16    (0x03 << 0)
+#define LDBBSIFR_RPKF_RGB24    (0x0b << 0)
+#define LDBBSIFR_RPKF_MASK     (0x1f << 0)
+#define LDBnBSSZR(n)           (0xb20 + (n) * 0x20 + 0x04)
+#define LDBBSSZR_BVSS_MASK     (0xfff << 16)
+#define LDBBSSZR_BVSS_SHIFT    16
+#define LDBBSSZR_BHSS_MASK     (0xfff << 0)
+#define LDBBSSZR_BHSS_SHIFT    0
+#define LDBnBLOCR(n)           (0xb20 + (n) * 0x20 + 0x08)
+#define LDBBLOCR_CVLC_MASK     (0xfff << 16)
+#define LDBBLOCR_CVLC_SHIFT    16
+#define LDBBLOCR_CHLC_MASK     (0xfff << 0)
+#define LDBBLOCR_CHLC_SHIFT    0
+#define LDBnBSMWR(n)           (0xb20 + (n) * 0x20 + 0x0c)
+#define LDBBSMWR_BSMWA_MASK    (0xffff << 16)
+#define LDBBSMWR_BSMWA_SHIFT   16
+#define LDBBSMWR_BSMW_MASK     (0xffff << 0)
+#define LDBBSMWR_BSMW_SHIFT    0
+#define LDBnBSAYR(n)           (0xb20 + (n) * 0x20 + 0x10)
+#define LDBBSAYR_FG1A_MASK     (0xff << 24)
+#define LDBBSAYR_FG1A_SHIFT    24
+#define LDBBSAYR_FG1R_MASK     (0xff << 16)
+#define LDBBSAYR_FG1R_SHIFT    16
+#define LDBBSAYR_FG1G_MASK     (0xff << 8)
+#define LDBBSAYR_FG1G_SHIFT    8
+#define LDBBSAYR_FG1B_MASK     (0xff << 0)
+#define LDBBSAYR_FG1B_SHIFT    0
+#define LDBnBSACR(n)           (0xb20 + (n) * 0x20 + 0x14)
+#define LDBBSACR_FG2A_MASK     (0xff << 24)
+#define LDBBSACR_FG2A_SHIFT    24
+#define LDBBSACR_FG2R_MASK     (0xff << 16)
+#define LDBBSACR_FG2R_SHIFT    16
+#define LDBBSACR_FG2G_MASK     (0xff << 8)
+#define LDBBSACR_FG2G_SHIFT    8
+#define LDBBSACR_FG2B_MASK     (0xff << 0)
+#define LDBBSACR_FG2B_SHIFT    0
+#define LDBnBSAAR(n)           (0xb20 + (n) * 0x20 + 0x18)
+#define LDBBSAAR_AP_MASK       (0xff << 24)
+#define LDBBSAAR_AP_SHIFT      24
+#define LDBBSAAR_R_MASK                (0xff << 16)
+#define LDBBSAAR_R_SHIFT       16
+#define LDBBSAAR_GY_MASK       (0xff << 8)
+#define LDBBSAAR_GY_SHIFT      8
+#define LDBBSAAR_B_MASK                (0xff << 0)
+#define LDBBSAAR_B_SHIFT       0
+#define LDBnBPPCR(n)           (0xb20 + (n) * 0x20 + 0x1c)
+#define LDBBPPCR_AP_MASK       (0xff << 24)
+#define LDBBPPCR_AP_SHIFT      24
+#define LDBBPPCR_R_MASK                (0xff << 16)
+#define LDBBPPCR_R_SHIFT       16
+#define LDBBPPCR_GY_MASK       (0xff << 8)
+#define LDBBPPCR_GY_SHIFT      8
+#define LDBBPPCR_B_MASK                (0xff << 0)
+#define LDBBPPCR_B_SHIFT       0
+#define LDBnBBGCL(n)           (0xb10 + (n) * 0x04)
+#define LDBBBGCL_BGA_MASK      (0xff << 24)
+#define LDBBBGCL_BGA_SHIFT     24
+#define LDBBBGCL_BGR_MASK      (0xff << 16)
+#define LDBBBGCL_BGR_SHIFT     16
+#define LDBBBGCL_BGG_MASK      (0xff << 8)
+#define LDBBBGCL_BGG_SHIFT     8
+#define LDBBBGCL_BGB_MASK      (0xff << 0)
+#define LDBBBGCL_BGB_SHIFT     0
+
+#define LCDC_SIDE_B_OFFSET     0x1000
+#define LCDC_MIRROR_OFFSET     0x2000
+
+static inline bool lcdc_is_banked(u32 reg)
+{
+       switch (reg) {
+       case LDMT1R:
+       case LDMT2R:
+       case LDMT3R:
+       case LDDFR:
+       case LDSM1R:
+       case LDSA1R:
+       case LDSA2R:
+       case LDMLSR:
+       case LDWBFR:
+       case LDWBCNTR:
+       case LDWBAR:
+       case LDHCNR:
+       case LDHSYNR:
+       case LDVLNR:
+       case LDVSYNR:
+       case LDHPDR:
+       case LDVPDR:
+       case LDHAJR:
+               return true;
+       default:
+               return reg >= LDBnBBGCL(0) && reg <= LDBnBPPCR(3);
+       }
+}
+
+static inline void lcdc_write_mirror(struct shmob_drm_device *sdev, u32 reg,
+                                    u32 data)
+{
+       iowrite32(data, sdev->mmio + reg + LCDC_MIRROR_OFFSET);
+}
+
+static inline void lcdc_write(struct shmob_drm_device *sdev, u32 reg, u32 data)
+{
+       iowrite32(data, sdev->mmio + reg);
+       if (lcdc_is_banked(reg))
+               iowrite32(data, sdev->mmio + reg + LCDC_SIDE_B_OFFSET);
+}
+
+static inline u32 lcdc_read(struct shmob_drm_device *sdev, u32 reg)
+{
+       return ioread32(sdev->mmio + reg);
+}
+
+static inline int lcdc_wait_bit(struct shmob_drm_device *sdev, u32 reg,
+                               u32 mask, u32 until)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(5);
+
+       while ((lcdc_read(sdev, reg) & mask) != until) {
+               if (time_after(jiffies, timeout))
+                       return -ETIMEDOUT;
+               cpu_relax();
+       }
+
+       return 0;
+}
+
+#endif /* __SHMOB_DRM_REGS_H__ */
diff --git a/drivers/gpu/drm/shmobile/Kconfig b/drivers/gpu/drm/shmobile/Kconfig
deleted file mode 100644 (file)
index ad14112..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-config DRM_SHMOBILE
-       tristate "DRM Support for SH Mobile"
-       depends on DRM
-       depends on ARCH_RENESAS || ARCH_SHMOBILE || COMPILE_TEST
-       select BACKLIGHT_CLASS_DEVICE
-       select DRM_KMS_HELPER
-       select DRM_GEM_DMA_HELPER
-       help
-         Choose this option if you have an SH Mobile chipset.
-         If M is selected the module will be called shmob-drm.
-
diff --git a/drivers/gpu/drm/shmobile/Makefile b/drivers/gpu/drm/shmobile/Makefile
deleted file mode 100644 (file)
index 861edaf..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-shmob-drm-y := shmob_drm_backlight.o \
-              shmob_drm_crtc.o \
-              shmob_drm_drv.o \
-              shmob_drm_kms.o \
-              shmob_drm_plane.o
-
-obj-$(CONFIG_DRM_SHMOBILE)     += shmob-drm.o
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.c b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c
deleted file mode 100644 (file)
index 794573b..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * shmob_drm_backlight.c  --  SH Mobile DRM Backlight
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/backlight.h>
-
-#include "shmob_drm_backlight.h"
-#include "shmob_drm_crtc.h"
-#include "shmob_drm_drv.h"
-
-static int shmob_drm_backlight_update(struct backlight_device *bdev)
-{
-       struct shmob_drm_connector *scon = bl_get_data(bdev);
-       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
-       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
-       int brightness = backlight_get_brightness(bdev);
-
-       return bdata->set_brightness(brightness);
-}
-
-static int shmob_drm_backlight_get_brightness(struct backlight_device *bdev)
-{
-       struct shmob_drm_connector *scon = bl_get_data(bdev);
-       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
-       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
-
-       return bdata->get_brightness();
-}
-
-static const struct backlight_ops shmob_drm_backlight_ops = {
-       .options        = BL_CORE_SUSPENDRESUME,
-       .update_status  = shmob_drm_backlight_update,
-       .get_brightness = shmob_drm_backlight_get_brightness,
-};
-
-void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode)
-{
-       if (scon->backlight == NULL)
-               return;
-
-       scon->backlight->props.power = mode == DRM_MODE_DPMS_ON
-                                    ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
-       backlight_update_status(scon->backlight);
-}
-
-int shmob_drm_backlight_init(struct shmob_drm_connector *scon)
-{
-       struct shmob_drm_device *sdev = scon->connector.dev->dev_private;
-       const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight;
-       struct drm_connector *connector = &scon->connector;
-       struct drm_device *dev = connector->dev;
-       struct backlight_device *backlight;
-
-       if (!bdata->max_brightness)
-               return 0;
-
-       backlight = backlight_device_register(bdata->name, dev->dev, scon,
-                                             &shmob_drm_backlight_ops, NULL);
-       if (IS_ERR(backlight)) {
-               dev_err(dev->dev, "unable to register backlight device: %ld\n",
-                       PTR_ERR(backlight));
-               return PTR_ERR(backlight);
-       }
-
-       backlight->props.max_brightness = bdata->max_brightness;
-       backlight->props.brightness = bdata->max_brightness;
-       backlight->props.power = FB_BLANK_POWERDOWN;
-       backlight_update_status(backlight);
-
-       scon->backlight = backlight;
-       return 0;
-}
-
-void shmob_drm_backlight_exit(struct shmob_drm_connector *scon)
-{
-       backlight_device_unregister(scon->backlight);
-}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.h b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h
deleted file mode 100644 (file)
index d9abb7a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * shmob_drm_backlight.h  --  SH Mobile DRM Backlight
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __SHMOB_DRM_BACKLIGHT_H__
-#define __SHMOB_DRM_BACKLIGHT_H__
-
-struct shmob_drm_connector;
-
-void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode);
-int shmob_drm_backlight_init(struct shmob_drm_connector *scon);
-void shmob_drm_backlight_exit(struct shmob_drm_connector *scon);
-
-#endif /* __SHMOB_DRM_BACKLIGHT_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
deleted file mode 100644 (file)
index 11dd2bc..0000000
+++ /dev/null
@@ -1,712 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * shmob_drm_crtc.c  --  SH Mobile DRM CRTCs
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/backlight.h>
-#include <linux/clk.h>
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_fb_dma_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_modeset_helper.h>
-#include <drm/drm_modeset_helper_vtables.h>
-#include <drm/drm_plane_helper.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
-#include <drm/drm_vblank.h>
-
-#include "shmob_drm_backlight.h"
-#include "shmob_drm_crtc.h"
-#include "shmob_drm_drv.h"
-#include "shmob_drm_kms.h"
-#include "shmob_drm_plane.h"
-#include "shmob_drm_regs.h"
-
-/*
- * TODO: panel support
- */
-
-/* -----------------------------------------------------------------------------
- * Clock management
- */
-
-static int shmob_drm_clk_on(struct shmob_drm_device *sdev)
-{
-       int ret;
-
-       if (sdev->clock) {
-               ret = clk_prepare_enable(sdev->clock);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static void shmob_drm_clk_off(struct shmob_drm_device *sdev)
-{
-       if (sdev->clock)
-               clk_disable_unprepare(sdev->clock);
-}
-
-/* -----------------------------------------------------------------------------
- * CRTC
- */
-
-static void shmob_drm_crtc_setup_geometry(struct shmob_drm_crtc *scrtc)
-{
-       struct drm_crtc *crtc = &scrtc->crtc;
-       struct shmob_drm_device *sdev = crtc->dev->dev_private;
-       const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
-       const struct drm_display_mode *mode = &crtc->mode;
-       u32 value;
-
-       value = sdev->ldmt1r
-             | ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : LDMT1R_VPOL)
-             | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : LDMT1R_HPOL)
-             | ((idata->flags & SHMOB_DRM_IFACE_FL_DWPOL) ? LDMT1R_DWPOL : 0)
-             | ((idata->flags & SHMOB_DRM_IFACE_FL_DIPOL) ? LDMT1R_DIPOL : 0)
-             | ((idata->flags & SHMOB_DRM_IFACE_FL_DAPOL) ? LDMT1R_DAPOL : 0)
-             | ((idata->flags & SHMOB_DRM_IFACE_FL_HSCNT) ? LDMT1R_HSCNT : 0)
-             | ((idata->flags & SHMOB_DRM_IFACE_FL_DWCNT) ? LDMT1R_DWCNT : 0);
-       lcdc_write(sdev, LDMT1R, value);
-
-       if (idata->interface >= SHMOB_DRM_IFACE_SYS8A &&
-           idata->interface <= SHMOB_DRM_IFACE_SYS24) {
-               /* Setup SYS bus. */
-               value = (idata->sys.cs_setup << LDMT2R_CSUP_SHIFT)
-                     | (idata->sys.vsync_active_high ? LDMT2R_RSV : 0)
-                     | (idata->sys.vsync_dir_input ? LDMT2R_VSEL : 0)
-                     | (idata->sys.write_setup << LDMT2R_WCSC_SHIFT)
-                     | (idata->sys.write_cycle << LDMT2R_WCEC_SHIFT)
-                     | (idata->sys.write_strobe << LDMT2R_WCLW_SHIFT);
-               lcdc_write(sdev, LDMT2R, value);
-
-               value = (idata->sys.read_latch << LDMT3R_RDLC_SHIFT)
-                     | (idata->sys.read_setup << LDMT3R_RCSC_SHIFT)
-                     | (idata->sys.read_cycle << LDMT3R_RCEC_SHIFT)
-                     | (idata->sys.read_strobe << LDMT3R_RCLW_SHIFT);
-               lcdc_write(sdev, LDMT3R, value);
-       }
-
-       value = ((mode->hdisplay / 8) << 16)                    /* HDCN */
-             | (mode->htotal / 8);                             /* HTCN */
-       lcdc_write(sdev, LDHCNR, value);
-
-       value = (((mode->hsync_end - mode->hsync_start) / 8) << 16) /* HSYNW */
-             | (mode->hsync_start / 8);                        /* HSYNP */
-       lcdc_write(sdev, LDHSYNR, value);
-
-       value = ((mode->hdisplay & 7) << 24) | ((mode->htotal & 7) << 16)
-             | (((mode->hsync_end - mode->hsync_start) & 7) << 8)
-             | (mode->hsync_start & 7);
-       lcdc_write(sdev, LDHAJR, value);
-
-       value = ((mode->vdisplay) << 16)                        /* VDLN */
-             | mode->vtotal;                                   /* VTLN */
-       lcdc_write(sdev, LDVLNR, value);
-
-       value = ((mode->vsync_end - mode->vsync_start) << 16)   /* VSYNW */
-             | mode->vsync_start;                              /* VSYNP */
-       lcdc_write(sdev, LDVSYNR, value);
-}
-
-static void shmob_drm_crtc_start_stop(struct shmob_drm_crtc *scrtc, bool start)
-{
-       struct shmob_drm_device *sdev = scrtc->crtc.dev->dev_private;
-       u32 value;
-
-       value = lcdc_read(sdev, LDCNT2R);
-       if (start)
-               lcdc_write(sdev, LDCNT2R, value | LDCNT2R_DO);
-       else
-               lcdc_write(sdev, LDCNT2R, value & ~LDCNT2R_DO);
-
-       /* Wait until power is applied/stopped. */
-       while (1) {
-               value = lcdc_read(sdev, LDPMR) & LDPMR_LPS;
-               if ((start && value) || (!start && !value))
-                       break;
-
-               cpu_relax();
-       }
-
-       if (!start) {
-               /* Stop the dot clock. */
-               lcdc_write(sdev, LDDCKSTPR, LDDCKSTPR_DCKSTP);
-       }
-}
-
-/*
- * shmob_drm_crtc_start - Configure and start the LCDC
- * @scrtc: the SH Mobile CRTC
- *
- * Configure and start the LCDC device. External devices (clocks, MERAM, panels,
- * ...) are not touched by this function.
- */
-static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
-{
-       struct drm_crtc *crtc = &scrtc->crtc;
-       struct shmob_drm_device *sdev = crtc->dev->dev_private;
-       const struct shmob_drm_interface_data *idata = &sdev->pdata->iface;
-       const struct shmob_drm_format_info *format;
-       struct drm_device *dev = sdev->ddev;
-       struct drm_plane *plane;
-       u32 value;
-       int ret;
-
-       if (scrtc->started)
-               return;
-
-       format = shmob_drm_format_info(crtc->primary->fb->format->format);
-       if (WARN_ON(format == NULL))
-               return;
-
-       /* Enable clocks before accessing the hardware. */
-       ret = shmob_drm_clk_on(sdev);
-       if (ret < 0)
-               return;
-
-       /* Reset and enable the LCDC. */
-       lcdc_write(sdev, LDCNT2R, lcdc_read(sdev, LDCNT2R) | LDCNT2R_BR);
-       lcdc_wait_bit(sdev, LDCNT2R, LDCNT2R_BR, 0);
-       lcdc_write(sdev, LDCNT2R, LDCNT2R_ME);
-
-       /* Stop the LCDC first and disable all interrupts. */
-       shmob_drm_crtc_start_stop(scrtc, false);
-       lcdc_write(sdev, LDINTR, 0);
-
-       /* Configure power supply, dot clocks and start them. */
-       lcdc_write(sdev, LDPMR, 0);
-
-       value = sdev->lddckr;
-       if (idata->clk_div) {
-               /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
-                * denominator.
-                */
-               lcdc_write(sdev, LDDCKPAT1R, 0);
-               lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1);
-
-               if (idata->clk_div == 1)
-                       value |= LDDCKR_MOSEL;
-               else
-                       value |= idata->clk_div;
-       }
-
-       lcdc_write(sdev, LDDCKR, value);
-       lcdc_write(sdev, LDDCKSTPR, 0);
-       lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0);
-
-       /* TODO: Setup SYS panel */
-
-       /* Setup geometry, format, frame buffer memory and operation mode. */
-       shmob_drm_crtc_setup_geometry(scrtc);
-
-       /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
-       lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1);
-       lcdc_write(sdev, LDMLSR, scrtc->line_size);
-       lcdc_write(sdev, LDSA1R, scrtc->dma[0]);
-       if (format->yuv)
-               lcdc_write(sdev, LDSA2R, scrtc->dma[1]);
-       lcdc_write(sdev, LDSM1R, 0);
-
-       /* Word and long word swap. */
-       switch (format->fourcc) {
-       case DRM_FORMAT_RGB565:
-       case DRM_FORMAT_NV21:
-       case DRM_FORMAT_NV61:
-       case DRM_FORMAT_NV42:
-               value = LDDDSR_LS | LDDDSR_WS;
-               break;
-       case DRM_FORMAT_RGB888:
-       case DRM_FORMAT_NV12:
-       case DRM_FORMAT_NV16:
-       case DRM_FORMAT_NV24:
-               value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
-               break;
-       case DRM_FORMAT_ARGB8888:
-       case DRM_FORMAT_XRGB8888:
-       default:
-               value = LDDDSR_LS;
-               break;
-       }
-       lcdc_write(sdev, LDDDSR, value);
-
-       /* Setup planes. */
-       drm_for_each_legacy_plane(plane, dev) {
-               if (plane->crtc == crtc)
-                       shmob_drm_plane_setup(plane);
-       }
-
-       /* Enable the display output. */
-       lcdc_write(sdev, LDCNT1R, LDCNT1R_DE);
-
-       shmob_drm_crtc_start_stop(scrtc, true);
-
-       scrtc->started = true;
-}
-
-static void shmob_drm_crtc_stop(struct shmob_drm_crtc *scrtc)
-{
-       struct drm_crtc *crtc = &scrtc->crtc;
-       struct shmob_drm_device *sdev = crtc->dev->dev_private;
-
-       if (!scrtc->started)
-               return;
-
-       /* Stop the LCDC. */
-       shmob_drm_crtc_start_stop(scrtc, false);
-
-       /* Disable the display output. */
-       lcdc_write(sdev, LDCNT1R, 0);
-
-       /* Stop clocks. */
-       shmob_drm_clk_off(sdev);
-
-       scrtc->started = false;
-}
-
-void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc)
-{
-       shmob_drm_crtc_stop(scrtc);
-}
-
-void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc)
-{
-       if (scrtc->dpms != DRM_MODE_DPMS_ON)
-               return;
-
-       shmob_drm_crtc_start(scrtc);
-}
-
-static void shmob_drm_crtc_compute_base(struct shmob_drm_crtc *scrtc,
-                                       int x, int y)
-{
-       struct drm_crtc *crtc = &scrtc->crtc;
-       struct drm_framebuffer *fb = crtc->primary->fb;
-       struct drm_gem_dma_object *gem;
-       unsigned int bpp;
-
-       bpp = scrtc->format->yuv ? 8 : scrtc->format->bpp;
-       gem = drm_fb_dma_get_gem_obj(fb, 0);
-       scrtc->dma[0] = gem->dma_addr + fb->offsets[0]
-                     + y * fb->pitches[0] + x * bpp / 8;
-
-       if (scrtc->format->yuv) {
-               bpp = scrtc->format->bpp - 8;
-               gem = drm_fb_dma_get_gem_obj(fb, 1);
-               scrtc->dma[1] = gem->dma_addr + fb->offsets[1]
-                             + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
-                             + x * (bpp == 16 ? 2 : 1);
-       }
-}
-
-static void shmob_drm_crtc_update_base(struct shmob_drm_crtc *scrtc)
-{
-       struct drm_crtc *crtc = &scrtc->crtc;
-       struct shmob_drm_device *sdev = crtc->dev->dev_private;
-
-       shmob_drm_crtc_compute_base(scrtc, crtc->x, crtc->y);
-
-       lcdc_write_mirror(sdev, LDSA1R, scrtc->dma[0]);
-       if (scrtc->format->yuv)
-               lcdc_write_mirror(sdev, LDSA2R, scrtc->dma[1]);
-
-       lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
-}
-
-#define to_shmob_crtc(c)       container_of(c, struct shmob_drm_crtc, crtc)
-
-static void shmob_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
-
-       if (scrtc->dpms == mode)
-               return;
-
-       if (mode == DRM_MODE_DPMS_ON)
-               shmob_drm_crtc_start(scrtc);
-       else
-               shmob_drm_crtc_stop(scrtc);
-
-       scrtc->dpms = mode;
-}
-
-static void shmob_drm_crtc_mode_prepare(struct drm_crtc *crtc)
-{
-       shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-}
-
-static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc,
-                                  struct drm_display_mode *mode,
-                                  struct drm_display_mode *adjusted_mode,
-                                  int x, int y,
-                                  struct drm_framebuffer *old_fb)
-{
-       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
-       struct shmob_drm_device *sdev = crtc->dev->dev_private;
-       const struct shmob_drm_format_info *format;
-
-       format = shmob_drm_format_info(crtc->primary->fb->format->format);
-       if (format == NULL) {
-               dev_dbg(sdev->dev, "mode_set: unsupported format %p4cc\n",
-                       &crtc->primary->fb->format->format);
-               return -EINVAL;
-       }
-
-       scrtc->format = format;
-       scrtc->line_size = crtc->primary->fb->pitches[0];
-
-       shmob_drm_crtc_compute_base(scrtc, x, y);
-
-       return 0;
-}
-
-static void shmob_drm_crtc_mode_commit(struct drm_crtc *crtc)
-{
-       shmob_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-}
-
-static int shmob_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-                                       struct drm_framebuffer *old_fb)
-{
-       shmob_drm_crtc_update_base(to_shmob_crtc(crtc));
-
-       return 0;
-}
-
-static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
-       .dpms = shmob_drm_crtc_dpms,
-       .prepare = shmob_drm_crtc_mode_prepare,
-       .commit = shmob_drm_crtc_mode_commit,
-       .mode_set = shmob_drm_crtc_mode_set,
-       .mode_set_base = shmob_drm_crtc_mode_set_base,
-};
-
-void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc)
-{
-       struct drm_pending_vblank_event *event;
-       struct drm_device *dev = scrtc->crtc.dev;
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->event_lock, flags);
-       event = scrtc->event;
-       scrtc->event = NULL;
-       if (event) {
-               drm_crtc_send_vblank_event(&scrtc->crtc, event);
-               drm_crtc_vblank_put(&scrtc->crtc);
-       }
-       spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
-static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
-                                   struct drm_framebuffer *fb,
-                                   struct drm_pending_vblank_event *event,
-                                   uint32_t page_flip_flags,
-                                   struct drm_modeset_acquire_ctx *ctx)
-{
-       struct shmob_drm_crtc *scrtc = to_shmob_crtc(crtc);
-       struct drm_device *dev = scrtc->crtc.dev;
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->event_lock, flags);
-       if (scrtc->event != NULL) {
-               spin_unlock_irqrestore(&dev->event_lock, flags);
-               return -EBUSY;
-       }
-       spin_unlock_irqrestore(&dev->event_lock, flags);
-
-       crtc->primary->fb = fb;
-       shmob_drm_crtc_update_base(scrtc);
-
-       if (event) {
-               event->pipe = 0;
-               drm_crtc_vblank_get(&scrtc->crtc);
-               spin_lock_irqsave(&dev->event_lock, flags);
-               scrtc->event = event;
-               spin_unlock_irqrestore(&dev->event_lock, flags);
-       }
-
-       return 0;
-}
-
-static void shmob_drm_crtc_enable_vblank(struct shmob_drm_device *sdev,
-                                        bool enable)
-{
-       unsigned long flags;
-       u32 ldintr;
-
-       /* Be careful not to acknowledge any pending interrupt. */
-       spin_lock_irqsave(&sdev->irq_lock, flags);
-       ldintr = lcdc_read(sdev, LDINTR) | LDINTR_STATUS_MASK;
-       if (enable)
-               ldintr |= LDINTR_VEE;
-       else
-               ldintr &= ~LDINTR_VEE;
-       lcdc_write(sdev, LDINTR, ldintr);
-       spin_unlock_irqrestore(&sdev->irq_lock, flags);
-}
-
-static int shmob_drm_enable_vblank(struct drm_crtc *crtc)
-{
-       struct shmob_drm_device *sdev = crtc->dev->dev_private;
-
-       shmob_drm_crtc_enable_vblank(sdev, true);
-
-       return 0;
-}
-
-static void shmob_drm_disable_vblank(struct drm_crtc *crtc)
-{
-       struct shmob_drm_device *sdev = crtc->dev->dev_private;
-
-       shmob_drm_crtc_enable_vblank(sdev, false);
-}
-
-static const struct drm_crtc_funcs crtc_funcs = {
-       .destroy = drm_crtc_cleanup,
-       .set_config = drm_crtc_helper_set_config,
-       .page_flip = shmob_drm_crtc_page_flip,
-       .enable_vblank = shmob_drm_enable_vblank,
-       .disable_vblank = shmob_drm_disable_vblank,
-};
-
-static const uint32_t modeset_formats[] = {
-       DRM_FORMAT_RGB565,
-       DRM_FORMAT_RGB888,
-       DRM_FORMAT_ARGB8888,
-       DRM_FORMAT_XRGB8888,
-};
-
-static const struct drm_plane_funcs primary_plane_funcs = {
-       DRM_PLANE_NON_ATOMIC_FUNCS,
-};
-
-int shmob_drm_crtc_create(struct shmob_drm_device *sdev)
-{
-       struct drm_crtc *crtc = &sdev->crtc.crtc;
-       struct drm_plane *primary;
-       int ret;
-
-       sdev->crtc.dpms = DRM_MODE_DPMS_OFF;
-
-       primary = __drm_universal_plane_alloc(sdev->ddev, sizeof(*primary), 0,
-                                             0, &primary_plane_funcs,
-                                             modeset_formats,
-                                             ARRAY_SIZE(modeset_formats),
-                                             NULL, DRM_PLANE_TYPE_PRIMARY,
-                                             NULL);
-       if (IS_ERR(primary))
-               return PTR_ERR(primary);
-
-       ret = drm_crtc_init_with_planes(sdev->ddev, crtc, primary, NULL,
-                                       &crtc_funcs, NULL);
-       if (ret < 0) {
-               drm_plane_cleanup(primary);
-               kfree(primary);
-               return ret;
-       }
-
-       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
-
-       return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Encoder
- */
-
-#define to_shmob_encoder(e) \
-       container_of(e, struct shmob_drm_encoder, encoder)
-
-static void shmob_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
-{
-       struct shmob_drm_encoder *senc = to_shmob_encoder(encoder);
-       struct shmob_drm_device *sdev = encoder->dev->dev_private;
-       struct shmob_drm_connector *scon = &sdev->connector;
-
-       if (senc->dpms == mode)
-               return;
-
-       shmob_drm_backlight_dpms(scon, mode);
-
-       senc->dpms = mode;
-}
-
-static bool shmob_drm_encoder_mode_fixup(struct drm_encoder *encoder,
-                                        const struct drm_display_mode *mode,
-                                        struct drm_display_mode *adjusted_mode)
-{
-       struct drm_device *dev = encoder->dev;
-       struct shmob_drm_device *sdev = dev->dev_private;
-       struct drm_connector *connector = &sdev->connector.connector;
-       const struct drm_display_mode *panel_mode;
-
-       if (list_empty(&connector->modes)) {
-               dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
-               return false;
-       }
-
-       /* The flat panel mode is fixed, just copy it to the adjusted mode. */
-       panel_mode = list_first_entry(&connector->modes,
-                                     struct drm_display_mode, head);
-       drm_mode_copy(adjusted_mode, panel_mode);
-
-       return true;
-}
-
-static void shmob_drm_encoder_mode_prepare(struct drm_encoder *encoder)
-{
-       /* No-op, everything is handled in the CRTC code. */
-}
-
-static void shmob_drm_encoder_mode_set(struct drm_encoder *encoder,
-                                      struct drm_display_mode *mode,
-                                      struct drm_display_mode *adjusted_mode)
-{
-       /* No-op, everything is handled in the CRTC code. */
-}
-
-static void shmob_drm_encoder_mode_commit(struct drm_encoder *encoder)
-{
-       /* No-op, everything is handled in the CRTC code. */
-}
-
-static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
-       .dpms = shmob_drm_encoder_dpms,
-       .mode_fixup = shmob_drm_encoder_mode_fixup,
-       .prepare = shmob_drm_encoder_mode_prepare,
-       .commit = shmob_drm_encoder_mode_commit,
-       .mode_set = shmob_drm_encoder_mode_set,
-};
-
-int shmob_drm_encoder_create(struct shmob_drm_device *sdev)
-{
-       struct drm_encoder *encoder = &sdev->encoder.encoder;
-       int ret;
-
-       sdev->encoder.dpms = DRM_MODE_DPMS_OFF;
-
-       encoder->possible_crtcs = 1;
-
-       ret = drm_simple_encoder_init(sdev->ddev, encoder,
-                                     DRM_MODE_ENCODER_LVDS);
-       if (ret < 0)
-               return ret;
-
-       drm_encoder_helper_add(encoder, &encoder_helper_funcs);
-
-       return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Connector
- */
-
-#define to_shmob_connector(c) \
-       container_of(c, struct shmob_drm_connector, connector)
-
-static int shmob_drm_connector_get_modes(struct drm_connector *connector)
-{
-       struct shmob_drm_device *sdev = connector->dev->dev_private;
-       struct drm_display_mode *mode;
-
-       mode = drm_mode_create(connector->dev);
-       if (mode == NULL)
-               return 0;
-
-       mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
-       mode->clock = sdev->pdata->panel.mode.clock;
-       mode->hdisplay = sdev->pdata->panel.mode.hdisplay;
-       mode->hsync_start = sdev->pdata->panel.mode.hsync_start;
-       mode->hsync_end = sdev->pdata->panel.mode.hsync_end;
-       mode->htotal = sdev->pdata->panel.mode.htotal;
-       mode->vdisplay = sdev->pdata->panel.mode.vdisplay;
-       mode->vsync_start = sdev->pdata->panel.mode.vsync_start;
-       mode->vsync_end = sdev->pdata->panel.mode.vsync_end;
-       mode->vtotal = sdev->pdata->panel.mode.vtotal;
-       mode->flags = sdev->pdata->panel.mode.flags;
-
-       drm_mode_set_name(mode);
-       drm_mode_probed_add(connector, mode);
-
-       connector->display_info.width_mm = sdev->pdata->panel.width_mm;
-       connector->display_info.height_mm = sdev->pdata->panel.height_mm;
-
-       return 1;
-}
-
-static struct drm_encoder *
-shmob_drm_connector_best_encoder(struct drm_connector *connector)
-{
-       struct shmob_drm_connector *scon = to_shmob_connector(connector);
-
-       return scon->encoder;
-}
-
-static const struct drm_connector_helper_funcs connector_helper_funcs = {
-       .get_modes = shmob_drm_connector_get_modes,
-       .best_encoder = shmob_drm_connector_best_encoder,
-};
-
-static void shmob_drm_connector_destroy(struct drm_connector *connector)
-{
-       struct shmob_drm_connector *scon = to_shmob_connector(connector);
-
-       shmob_drm_backlight_exit(scon);
-       drm_connector_unregister(connector);
-       drm_connector_cleanup(connector);
-}
-
-static const struct drm_connector_funcs connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
-       .fill_modes = drm_helper_probe_single_connector_modes,
-       .destroy = shmob_drm_connector_destroy,
-};
-
-int shmob_drm_connector_create(struct shmob_drm_device *sdev,
-                              struct drm_encoder *encoder)
-{
-       struct drm_connector *connector = &sdev->connector.connector;
-       int ret;
-
-       sdev->connector.encoder = encoder;
-
-       connector->display_info.width_mm = sdev->pdata->panel.width_mm;
-       connector->display_info.height_mm = sdev->pdata->panel.height_mm;
-
-       ret = drm_connector_init(sdev->ddev, connector, &connector_funcs,
-                                DRM_MODE_CONNECTOR_LVDS);
-       if (ret < 0)
-               return ret;
-
-       drm_connector_helper_add(connector, &connector_helper_funcs);
-
-       ret = shmob_drm_backlight_init(&sdev->connector);
-       if (ret < 0)
-               goto err_cleanup;
-
-       ret = drm_connector_attach_encoder(connector, encoder);
-       if (ret < 0)
-               goto err_backlight;
-
-       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-       drm_object_property_set_value(&connector->base,
-               sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
-
-       return 0;
-
-err_backlight:
-       shmob_drm_backlight_exit(&sdev->connector);
-err_cleanup:
-       drm_connector_cleanup(connector);
-       return ret;
-}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h
deleted file mode 100644 (file)
index 2171884..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * shmob_drm_crtc.h  --  SH Mobile DRM CRTCs
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __SHMOB_DRM_CRTC_H__
-#define __SHMOB_DRM_CRTC_H__
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_connector.h>
-#include <drm/drm_encoder.h>
-
-struct backlight_device;
-struct drm_pending_vblank_event;
-struct shmob_drm_device;
-struct shmob_drm_format_info;
-
-struct shmob_drm_crtc {
-       struct drm_crtc crtc;
-
-       struct drm_pending_vblank_event *event;
-       int dpms;
-
-       const struct shmob_drm_format_info *format;
-       unsigned long dma[2];
-       unsigned int line_size;
-       bool started;
-};
-
-struct shmob_drm_encoder {
-       struct drm_encoder encoder;
-       int dpms;
-};
-
-struct shmob_drm_connector {
-       struct drm_connector connector;
-       struct drm_encoder *encoder;
-
-       struct backlight_device *backlight;
-};
-
-int shmob_drm_crtc_create(struct shmob_drm_device *sdev);
-void shmob_drm_crtc_finish_page_flip(struct shmob_drm_crtc *scrtc);
-void shmob_drm_crtc_suspend(struct shmob_drm_crtc *scrtc);
-void shmob_drm_crtc_resume(struct shmob_drm_crtc *scrtc);
-
-int shmob_drm_encoder_create(struct shmob_drm_device *sdev);
-int shmob_drm_connector_create(struct shmob_drm_device *sdev,
-                              struct drm_encoder *encoder);
-
-#endif /* __SHMOB_DRM_CRTC_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
deleted file mode 100644 (file)
index 30493ce..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * shmob_drm_drv.c  --  SH Mobile DRM driver
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-
-#include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_module.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_vblank.h>
-
-#include "shmob_drm_drv.h"
-#include "shmob_drm_kms.h"
-#include "shmob_drm_plane.h"
-#include "shmob_drm_regs.h"
-
-/* -----------------------------------------------------------------------------
- * Hardware initialization
- */
-
-static int shmob_drm_init_interface(struct shmob_drm_device *sdev)
-{
-       static const u32 ldmt1r[] = {
-               [SHMOB_DRM_IFACE_RGB8] = LDMT1R_MIFTYP_RGB8,
-               [SHMOB_DRM_IFACE_RGB9] = LDMT1R_MIFTYP_RGB9,
-               [SHMOB_DRM_IFACE_RGB12A] = LDMT1R_MIFTYP_RGB12A,
-               [SHMOB_DRM_IFACE_RGB12B] = LDMT1R_MIFTYP_RGB12B,
-               [SHMOB_DRM_IFACE_RGB16] = LDMT1R_MIFTYP_RGB16,
-               [SHMOB_DRM_IFACE_RGB18] = LDMT1R_MIFTYP_RGB18,
-               [SHMOB_DRM_IFACE_RGB24] = LDMT1R_MIFTYP_RGB24,
-               [SHMOB_DRM_IFACE_YUV422] = LDMT1R_MIFTYP_YCBCR,
-               [SHMOB_DRM_IFACE_SYS8A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8A,
-               [SHMOB_DRM_IFACE_SYS8B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8B,
-               [SHMOB_DRM_IFACE_SYS8C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8C,
-               [SHMOB_DRM_IFACE_SYS8D] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS8D,
-               [SHMOB_DRM_IFACE_SYS9] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS9,
-               [SHMOB_DRM_IFACE_SYS12] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS12,
-               [SHMOB_DRM_IFACE_SYS16A] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16A,
-               [SHMOB_DRM_IFACE_SYS16B] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16B,
-               [SHMOB_DRM_IFACE_SYS16C] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS16C,
-               [SHMOB_DRM_IFACE_SYS18] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS18,
-               [SHMOB_DRM_IFACE_SYS24] = LDMT1R_IFM | LDMT1R_MIFTYP_SYS24,
-       };
-
-       if (sdev->pdata->iface.interface >= ARRAY_SIZE(ldmt1r)) {
-               dev_err(sdev->dev, "invalid interface type %u\n",
-                       sdev->pdata->iface.interface);
-               return -EINVAL;
-       }
-
-       sdev->ldmt1r = ldmt1r[sdev->pdata->iface.interface];
-       return 0;
-}
-
-static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
-                                           enum shmob_drm_clk_source clksrc)
-{
-       struct clk *clk;
-       char *clkname;
-
-       switch (clksrc) {
-       case SHMOB_DRM_CLK_BUS:
-               clkname = "bus_clk";
-               sdev->lddckr = LDDCKR_ICKSEL_BUS;
-               break;
-       case SHMOB_DRM_CLK_PERIPHERAL:
-               clkname = "peripheral_clk";
-               sdev->lddckr = LDDCKR_ICKSEL_MIPI;
-               break;
-       case SHMOB_DRM_CLK_EXTERNAL:
-               clkname = NULL;
-               sdev->lddckr = LDDCKR_ICKSEL_HDMI;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       clk = devm_clk_get(sdev->dev, clkname);
-       if (IS_ERR(clk)) {
-               dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);
-               return PTR_ERR(clk);
-       }
-
-       sdev->clock = clk;
-       return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * DRM operations
- */
-
-static irqreturn_t shmob_drm_irq(int irq, void *arg)
-{
-       struct drm_device *dev = arg;
-       struct shmob_drm_device *sdev = dev->dev_private;
-       unsigned long flags;
-       u32 status;
-
-       /* Acknowledge interrupts. Putting interrupt enable and interrupt flag
-        * bits in the same register is really brain-dead design and requires
-        * taking a spinlock.
-        */
-       spin_lock_irqsave(&sdev->irq_lock, flags);
-       status = lcdc_read(sdev, LDINTR);
-       lcdc_write(sdev, LDINTR, status ^ LDINTR_STATUS_MASK);
-       spin_unlock_irqrestore(&sdev->irq_lock, flags);
-
-       if (status & LDINTR_VES) {
-               drm_handle_vblank(dev, 0);
-               shmob_drm_crtc_finish_page_flip(&sdev->crtc);
-       }
-
-       return IRQ_HANDLED;
-}
-
-DEFINE_DRM_GEM_DMA_FOPS(shmob_drm_fops);
-
-static const struct drm_driver shmob_drm_driver = {
-       .driver_features        = DRIVER_GEM | DRIVER_MODESET,
-       DRM_GEM_DMA_DRIVER_OPS,
-       .fops                   = &shmob_drm_fops,
-       .name                   = "shmob-drm",
-       .desc                   = "Renesas SH Mobile DRM",
-       .date                   = "20120424",
-       .major                  = 1,
-       .minor                  = 0,
-};
-
-/* -----------------------------------------------------------------------------
- * Power management
- */
-
-static int shmob_drm_pm_suspend(struct device *dev)
-{
-       struct shmob_drm_device *sdev = dev_get_drvdata(dev);
-
-       drm_kms_helper_poll_disable(sdev->ddev);
-       shmob_drm_crtc_suspend(&sdev->crtc);
-
-       return 0;
-}
-
-static int shmob_drm_pm_resume(struct device *dev)
-{
-       struct shmob_drm_device *sdev = dev_get_drvdata(dev);
-
-       drm_modeset_lock_all(sdev->ddev);
-       shmob_drm_crtc_resume(&sdev->crtc);
-       drm_modeset_unlock_all(sdev->ddev);
-
-       drm_kms_helper_poll_enable(sdev->ddev);
-       return 0;
-}
-
-static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops,
-                               shmob_drm_pm_suspend, shmob_drm_pm_resume);
-
-/* -----------------------------------------------------------------------------
- * Platform driver
- */
-
-static int shmob_drm_remove(struct platform_device *pdev)
-{
-       struct shmob_drm_device *sdev = platform_get_drvdata(pdev);
-       struct drm_device *ddev = sdev->ddev;
-
-       drm_dev_unregister(ddev);
-       drm_kms_helper_poll_fini(ddev);
-       free_irq(sdev->irq, ddev);
-       drm_dev_put(ddev);
-
-       return 0;
-}
-
-static int shmob_drm_probe(struct platform_device *pdev)
-{
-       struct shmob_drm_platform_data *pdata = pdev->dev.platform_data;
-       struct shmob_drm_device *sdev;
-       struct drm_device *ddev;
-       unsigned int i;
-       int ret;
-
-       if (pdata == NULL) {
-               dev_err(&pdev->dev, "no platform data\n");
-               return -EINVAL;
-       }
-
-       /*
-        * Allocate and initialize the driver private data, I/O resources and
-        * clocks.
-        */
-       sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
-       if (sdev == NULL)
-               return -ENOMEM;
-
-       sdev->dev = &pdev->dev;
-       sdev->pdata = pdata;
-       spin_lock_init(&sdev->irq_lock);
-
-       platform_set_drvdata(pdev, sdev);
-
-       sdev->mmio = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(sdev->mmio))
-               return PTR_ERR(sdev->mmio);
-
-       ret = shmob_drm_setup_clocks(sdev, pdata->clk_source);
-       if (ret < 0)
-               return ret;
-
-       ret = shmob_drm_init_interface(sdev);
-       if (ret < 0)
-               return ret;
-
-       /* Allocate and initialize the DRM device. */
-       ddev = drm_dev_alloc(&shmob_drm_driver, &pdev->dev);
-       if (IS_ERR(ddev))
-               return PTR_ERR(ddev);
-
-       sdev->ddev = ddev;
-       ddev->dev_private = sdev;
-
-       ret = shmob_drm_modeset_init(sdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to initialize mode setting\n");
-               goto err_free_drm_dev;
-       }
-
-       for (i = 0; i < 4; ++i) {
-               ret = shmob_drm_plane_create(sdev, i);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to create plane %u\n", i);
-                       goto err_modeset_cleanup;
-               }
-       }
-
-       ret = drm_vblank_init(ddev, 1);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to initialize vblank\n");
-               goto err_modeset_cleanup;
-       }
-
-       ret = platform_get_irq(pdev, 0);
-       if (ret < 0)
-               goto err_modeset_cleanup;
-       sdev->irq = ret;
-
-       ret = request_irq(sdev->irq, shmob_drm_irq, 0, ddev->driver->name,
-                         ddev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to install IRQ handler\n");
-               goto err_modeset_cleanup;
-       }
-
-       /*
-        * Register the DRM device with the core and the connectors with
-        * sysfs.
-        */
-       ret = drm_dev_register(ddev, 0);
-       if (ret < 0)
-               goto err_irq_uninstall;
-
-       drm_fbdev_generic_setup(ddev, 16);
-
-       return 0;
-
-err_irq_uninstall:
-       free_irq(sdev->irq, ddev);
-err_modeset_cleanup:
-       drm_kms_helper_poll_fini(ddev);
-err_free_drm_dev:
-       drm_dev_put(ddev);
-
-       return ret;
-}
-
-static struct platform_driver shmob_drm_platform_driver = {
-       .probe          = shmob_drm_probe,
-       .remove         = shmob_drm_remove,
-       .driver         = {
-               .name   = "shmob-drm",
-               .pm     = pm_sleep_ptr(&shmob_drm_pm_ops),
-       },
-};
-
-drm_module_platform_driver(shmob_drm_platform_driver);
-
-MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
-MODULE_DESCRIPTION("Renesas SH Mobile DRM Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.h b/drivers/gpu/drm/shmobile/shmob_drm_drv.h
deleted file mode 100644 (file)
index 4964ddd..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * shmob_drm.h  --  SH Mobile DRM driver
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __SHMOB_DRM_DRV_H__
-#define __SHMOB_DRM_DRV_H__
-
-#include <linux/kernel.h>
-#include <linux/platform_data/shmob_drm.h>
-#include <linux/spinlock.h>
-
-#include "shmob_drm_crtc.h"
-
-struct clk;
-struct device;
-struct drm_device;
-
-struct shmob_drm_device {
-       struct device *dev;
-       const struct shmob_drm_platform_data *pdata;
-
-       void __iomem *mmio;
-       struct clk *clock;
-       u32 lddckr;
-       u32 ldmt1r;
-
-       unsigned int irq;
-       spinlock_t irq_lock;            /* Protects hardware LDINTR register */
-
-       struct drm_device *ddev;
-
-       struct shmob_drm_crtc crtc;
-       struct shmob_drm_encoder encoder;
-       struct shmob_drm_connector connector;
-};
-
-#endif /* __SHMOB_DRM_DRV_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.c b/drivers/gpu/drm/shmobile/shmob_drm_kms.c
deleted file mode 100644 (file)
index 99381cc..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * shmob_drm_kms.c  --  SH Mobile DRM Mode Setting
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_probe_helper.h>
-
-#include "shmob_drm_crtc.h"
-#include "shmob_drm_drv.h"
-#include "shmob_drm_kms.h"
-#include "shmob_drm_regs.h"
-
-/* -----------------------------------------------------------------------------
- * Format helpers
- */
-
-static const struct shmob_drm_format_info shmob_drm_format_infos[] = {
-       {
-               .fourcc = DRM_FORMAT_RGB565,
-               .bpp = 16,
-               .yuv = false,
-               .lddfr = LDDFR_PKF_RGB16,
-       }, {
-               .fourcc = DRM_FORMAT_RGB888,
-               .bpp = 24,
-               .yuv = false,
-               .lddfr = LDDFR_PKF_RGB24,
-       }, {
-               .fourcc = DRM_FORMAT_ARGB8888,
-               .bpp = 32,
-               .yuv = false,
-               .lddfr = LDDFR_PKF_ARGB32,
-       }, {
-               .fourcc = DRM_FORMAT_XRGB8888,
-               .bpp = 32,
-               .yuv = false,
-               .lddfr = LDDFR_PKF_ARGB32,
-       }, {
-               .fourcc = DRM_FORMAT_NV12,
-               .bpp = 12,
-               .yuv = true,
-               .lddfr = LDDFR_CC | LDDFR_YF_420,
-       }, {
-               .fourcc = DRM_FORMAT_NV21,
-               .bpp = 12,
-               .yuv = true,
-               .lddfr = LDDFR_CC | LDDFR_YF_420,
-       }, {
-               .fourcc = DRM_FORMAT_NV16,
-               .bpp = 16,
-               .yuv = true,
-               .lddfr = LDDFR_CC | LDDFR_YF_422,
-       }, {
-               .fourcc = DRM_FORMAT_NV61,
-               .bpp = 16,
-               .yuv = true,
-               .lddfr = LDDFR_CC | LDDFR_YF_422,
-       }, {
-               .fourcc = DRM_FORMAT_NV24,
-               .bpp = 24,
-               .yuv = true,
-               .lddfr = LDDFR_CC | LDDFR_YF_444,
-       }, {
-               .fourcc = DRM_FORMAT_NV42,
-               .bpp = 24,
-               .yuv = true,
-               .lddfr = LDDFR_CC | LDDFR_YF_444,
-       },
-};
-
-const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc)
-{
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(shmob_drm_format_infos); ++i) {
-               if (shmob_drm_format_infos[i].fourcc == fourcc)
-                       return &shmob_drm_format_infos[i];
-       }
-
-       return NULL;
-}
-
-/* -----------------------------------------------------------------------------
- * Frame buffer
- */
-
-static struct drm_framebuffer *
-shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv,
-                   const struct drm_mode_fb_cmd2 *mode_cmd)
-{
-       const struct shmob_drm_format_info *format;
-
-       format = shmob_drm_format_info(mode_cmd->pixel_format);
-       if (format == NULL) {
-               dev_dbg(dev->dev, "unsupported pixel format %p4cc\n",
-                       &mode_cmd->pixel_format);
-               return ERR_PTR(-EINVAL);
-       }
-
-       if (mode_cmd->pitches[0] & 7 || mode_cmd->pitches[0] >= 65536) {
-               dev_dbg(dev->dev, "invalid pitch value %u\n",
-                       mode_cmd->pitches[0]);
-               return ERR_PTR(-EINVAL);
-       }
-
-       if (format->yuv) {
-               unsigned int chroma_cpp = format->bpp == 24 ? 2 : 1;
-
-               if (mode_cmd->pitches[1] != mode_cmd->pitches[0] * chroma_cpp) {
-                       dev_dbg(dev->dev,
-                               "luma and chroma pitches do not match\n");
-                       return ERR_PTR(-EINVAL);
-               }
-       }
-
-       return drm_gem_fb_create(dev, file_priv, mode_cmd);
-}
-
-static const struct drm_mode_config_funcs shmob_drm_mode_config_funcs = {
-       .fb_create = shmob_drm_fb_create,
-};
-
-int shmob_drm_modeset_init(struct shmob_drm_device *sdev)
-{
-       int ret;
-
-       ret = drmm_mode_config_init(sdev->ddev);
-       if (ret)
-               return ret;
-
-       shmob_drm_crtc_create(sdev);
-       shmob_drm_encoder_create(sdev);
-       shmob_drm_connector_create(sdev, &sdev->encoder.encoder);
-
-       drm_kms_helper_poll_init(sdev->ddev);
-
-       sdev->ddev->mode_config.min_width = 0;
-       sdev->ddev->mode_config.min_height = 0;
-       sdev->ddev->mode_config.max_width = 4095;
-       sdev->ddev->mode_config.max_height = 4095;
-       sdev->ddev->mode_config.funcs = &shmob_drm_mode_config_funcs;
-
-       drm_helper_disable_unused_functions(sdev->ddev);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.h b/drivers/gpu/drm/shmobile/shmob_drm_kms.h
deleted file mode 100644 (file)
index 0347b1f..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * shmob_drm_kms.h  --  SH Mobile DRM Mode Setting
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __SHMOB_DRM_KMS_H__
-#define __SHMOB_DRM_KMS_H__
-
-#include <linux/types.h>
-
-struct drm_gem_dma_object;
-struct shmob_drm_device;
-
-struct shmob_drm_format_info {
-       u32 fourcc;
-       unsigned int bpp;
-       bool yuv;
-       u32 lddfr;
-};
-
-const struct shmob_drm_format_info *shmob_drm_format_info(u32 fourcc);
-
-int shmob_drm_modeset_init(struct shmob_drm_device *sdev);
-
-#endif /* __SHMOB_DRM_KMS_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
deleted file mode 100644 (file)
index 850986c..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * shmob_drm_plane.c  --  SH Mobile DRM Planes
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_fb_dma_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_dma_helper.h>
-
-#include "shmob_drm_drv.h"
-#include "shmob_drm_kms.h"
-#include "shmob_drm_plane.h"
-#include "shmob_drm_regs.h"
-
-struct shmob_drm_plane {
-       struct drm_plane plane;
-       unsigned int index;
-       unsigned int alpha;
-
-       const struct shmob_drm_format_info *format;
-       unsigned long dma[2];
-
-       unsigned int src_x;
-       unsigned int src_y;
-       unsigned int crtc_x;
-       unsigned int crtc_y;
-       unsigned int crtc_w;
-       unsigned int crtc_h;
-};
-
-#define to_shmob_plane(p)      container_of(p, struct shmob_drm_plane, plane)
-
-static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane,
-                                        struct drm_framebuffer *fb,
-                                        int x, int y)
-{
-       struct drm_gem_dma_object *gem;
-       unsigned int bpp;
-
-       bpp = splane->format->yuv ? 8 : splane->format->bpp;
-       gem = drm_fb_dma_get_gem_obj(fb, 0);
-       splane->dma[0] = gem->dma_addr + fb->offsets[0]
-                      + y * fb->pitches[0] + x * bpp / 8;
-
-       if (splane->format->yuv) {
-               bpp = splane->format->bpp - 8;
-               gem = drm_fb_dma_get_gem_obj(fb, 1);
-               splane->dma[1] = gem->dma_addr + fb->offsets[1]
-                              + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
-                              + x * (bpp == 16 ? 2 : 1);
-       }
-}
-
-static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane,
-                                   struct drm_framebuffer *fb)
-{
-       struct shmob_drm_device *sdev = splane->plane.dev->dev_private;
-       u32 format;
-
-       /* TODO: Support ROP3 mode */
-       format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT);
-
-       switch (splane->format->fourcc) {
-       case DRM_FORMAT_RGB565:
-       case DRM_FORMAT_NV21:
-       case DRM_FORMAT_NV61:
-       case DRM_FORMAT_NV42:
-               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
-               break;
-       case DRM_FORMAT_RGB888:
-       case DRM_FORMAT_NV12:
-       case DRM_FORMAT_NV16:
-       case DRM_FORMAT_NV24:
-               format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
-               break;
-       case DRM_FORMAT_ARGB8888:
-       case DRM_FORMAT_XRGB8888:
-       default:
-               format |= LDBBSIFR_SWPL;
-               break;
-       }
-
-       switch (splane->format->fourcc) {
-       case DRM_FORMAT_RGB565:
-               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
-               break;
-       case DRM_FORMAT_RGB888:
-               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
-               break;
-       case DRM_FORMAT_ARGB8888:
-               format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
-               break;
-       case DRM_FORMAT_XRGB8888:
-               format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
-               break;
-       case DRM_FORMAT_NV12:
-       case DRM_FORMAT_NV21:
-               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
-               break;
-       case DRM_FORMAT_NV16:
-       case DRM_FORMAT_NV61:
-               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
-               break;
-       case DRM_FORMAT_NV24:
-       case DRM_FORMAT_NV42:
-               format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
-               break;
-       }
-
-#define plane_reg_dump(sdev, splane, reg) \
-       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \
-               splane->index, #reg, \
-               lcdc_read(sdev, reg(splane->index)), \
-               lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET))
-
-       plane_reg_dump(sdev, splane, LDBnBSIFR);
-       plane_reg_dump(sdev, splane, LDBnBSSZR);
-       plane_reg_dump(sdev, splane, LDBnBLOCR);
-       plane_reg_dump(sdev, splane, LDBnBSMWR);
-       plane_reg_dump(sdev, splane, LDBnBSAYR);
-       plane_reg_dump(sdev, splane, LDBnBSACR);
-
-       lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));
-       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
-               "LDBCR", lcdc_read(sdev, LDBCR));
-
-       lcdc_write(sdev, LDBnBSIFR(splane->index), format);
-
-       lcdc_write(sdev, LDBnBSSZR(splane->index),
-                  (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) |
-                  (splane->crtc_w << LDBBSSZR_BHSS_SHIFT));
-       lcdc_write(sdev, LDBnBLOCR(splane->index),
-                  (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) |
-                  (splane->crtc_x << LDBBLOCR_CHLC_SHIFT));
-       lcdc_write(sdev, LDBnBSMWR(splane->index),
-                  fb->pitches[0] << LDBBSMWR_BSMW_SHIFT);
-
-       shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y);
-
-       lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]);
-       if (splane->format->yuv)
-               lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]);
-
-       lcdc_write(sdev, LDBCR,
-                  LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));
-       dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
-               "LDBCR", lcdc_read(sdev, LDBCR));
-
-       plane_reg_dump(sdev, splane, LDBnBSIFR);
-       plane_reg_dump(sdev, splane, LDBnBSSZR);
-       plane_reg_dump(sdev, splane, LDBnBLOCR);
-       plane_reg_dump(sdev, splane, LDBnBSMWR);
-       plane_reg_dump(sdev, splane, LDBnBSAYR);
-       plane_reg_dump(sdev, splane, LDBnBSACR);
-}
-
-void shmob_drm_plane_setup(struct drm_plane *plane)
-{
-       struct shmob_drm_plane *splane = to_shmob_plane(plane);
-
-       if (plane->fb == NULL)
-               return;
-
-       __shmob_drm_plane_setup(splane, plane->fb);
-}
-
-static int
-shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
-                      struct drm_framebuffer *fb, int crtc_x, int crtc_y,
-                      unsigned int crtc_w, unsigned int crtc_h,
-                      uint32_t src_x, uint32_t src_y,
-                      uint32_t src_w, uint32_t src_h,
-                      struct drm_modeset_acquire_ctx *ctx)
-{
-       struct shmob_drm_plane *splane = to_shmob_plane(plane);
-       struct shmob_drm_device *sdev = plane->dev->dev_private;
-       const struct shmob_drm_format_info *format;
-
-       format = shmob_drm_format_info(fb->format->format);
-       if (format == NULL) {
-               dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n",
-                       fb->format->format);
-               return -EINVAL;
-       }
-
-       if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
-               dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__);
-               return -EINVAL;
-       }
-
-       splane->format = format;
-
-       splane->src_x = src_x >> 16;
-       splane->src_y = src_y >> 16;
-       splane->crtc_x = crtc_x;
-       splane->crtc_y = crtc_y;
-       splane->crtc_w = crtc_w;
-       splane->crtc_h = crtc_h;
-
-       __shmob_drm_plane_setup(splane, fb);
-       return 0;
-}
-
-static int shmob_drm_plane_disable(struct drm_plane *plane,
-                                  struct drm_modeset_acquire_ctx *ctx)
-{
-       struct shmob_drm_plane *splane = to_shmob_plane(plane);
-       struct shmob_drm_device *sdev = plane->dev->dev_private;
-
-       splane->format = NULL;
-
-       lcdc_write(sdev, LDBnBSIFR(splane->index), 0);
-       return 0;
-}
-
-static void shmob_drm_plane_destroy(struct drm_plane *plane)
-{
-       drm_plane_force_disable(plane);
-       drm_plane_cleanup(plane);
-}
-
-static const struct drm_plane_funcs shmob_drm_plane_funcs = {
-       .update_plane = shmob_drm_plane_update,
-       .disable_plane = shmob_drm_plane_disable,
-       .destroy = shmob_drm_plane_destroy,
-};
-
-static const uint32_t formats[] = {
-       DRM_FORMAT_RGB565,
-       DRM_FORMAT_RGB888,
-       DRM_FORMAT_ARGB8888,
-       DRM_FORMAT_XRGB8888,
-       DRM_FORMAT_NV12,
-       DRM_FORMAT_NV21,
-       DRM_FORMAT_NV16,
-       DRM_FORMAT_NV61,
-       DRM_FORMAT_NV24,
-       DRM_FORMAT_NV42,
-};
-
-int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index)
-{
-       struct shmob_drm_plane *splane;
-       int ret;
-
-       splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL);
-       if (splane == NULL)
-               return -ENOMEM;
-
-       splane->index = index;
-       splane->alpha = 255;
-
-       ret = drm_universal_plane_init(sdev->ddev, &splane->plane, 1,
-                                      &shmob_drm_plane_funcs,
-                                      formats, ARRAY_SIZE(formats), NULL,
-                                      DRM_PLANE_TYPE_OVERLAY, NULL);
-
-       return ret;
-}
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.h b/drivers/gpu/drm/shmobile/shmob_drm_plane.h
deleted file mode 100644 (file)
index e72b21a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * shmob_drm_plane.h  --  SH Mobile DRM Planes
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __SHMOB_DRM_PLANE_H__
-#define __SHMOB_DRM_PLANE_H__
-
-struct drm_plane;
-struct shmob_drm_device;
-
-int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index);
-void shmob_drm_plane_setup(struct drm_plane *plane);
-
-#endif /* __SHMOB_DRM_PLANE_H__ */
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_regs.h b/drivers/gpu/drm/shmobile/shmob_drm_regs.h
deleted file mode 100644 (file)
index 0585336..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * shmob_drm_regs.h  --  SH Mobile DRM registers
- *
- * Copyright (C) 2012 Renesas Electronics Corporation
- *
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- */
-
-#ifndef __SHMOB_DRM_REGS_H__
-#define __SHMOB_DRM_REGS_H__
-
-#include <linux/io.h>
-#include <linux/jiffies.h>
-
-#include "shmob_drm_drv.h"
-
-/* Register definitions */
-#define LDDCKPAT1R             0x400
-#define LDDCKPAT2R             0x404
-#define LDDCKR                 0x410
-#define LDDCKR_ICKSEL_BUS      (0 << 16)
-#define LDDCKR_ICKSEL_MIPI     (1 << 16)
-#define LDDCKR_ICKSEL_HDMI     (2 << 16)
-#define LDDCKR_ICKSEL_EXT      (3 << 16)
-#define LDDCKR_ICKSEL_MASK     (7 << 16)
-#define LDDCKR_MOSEL           (1 << 6)
-#define LDDCKSTPR              0x414
-#define LDDCKSTPR_DCKSTS       (1 << 16)
-#define LDDCKSTPR_DCKSTP       (1 << 0)
-#define LDMT1R                 0x418
-#define LDMT1R_VPOL            (1 << 28)
-#define LDMT1R_HPOL            (1 << 27)
-#define LDMT1R_DWPOL           (1 << 26)
-#define LDMT1R_DIPOL           (1 << 25)
-#define LDMT1R_DAPOL           (1 << 24)
-#define LDMT1R_HSCNT           (1 << 17)
-#define LDMT1R_DWCNT           (1 << 16)
-#define LDMT1R_IFM             (1 << 12)
-#define LDMT1R_MIFTYP_RGB8     (0x0 << 0)
-#define LDMT1R_MIFTYP_RGB9     (0x4 << 0)
-#define LDMT1R_MIFTYP_RGB12A   (0x5 << 0)
-#define LDMT1R_MIFTYP_RGB12B   (0x6 << 0)
-#define LDMT1R_MIFTYP_RGB16    (0x7 << 0)
-#define LDMT1R_MIFTYP_RGB18    (0xa << 0)
-#define LDMT1R_MIFTYP_RGB24    (0xb << 0)
-#define LDMT1R_MIFTYP_YCBCR    (0xf << 0)
-#define LDMT1R_MIFTYP_SYS8A    (0x0 << 0)
-#define LDMT1R_MIFTYP_SYS8B    (0x1 << 0)
-#define LDMT1R_MIFTYP_SYS8C    (0x2 << 0)
-#define LDMT1R_MIFTYP_SYS8D    (0x3 << 0)
-#define LDMT1R_MIFTYP_SYS9     (0x4 << 0)
-#define LDMT1R_MIFTYP_SYS12    (0x5 << 0)
-#define LDMT1R_MIFTYP_SYS16A   (0x7 << 0)
-#define LDMT1R_MIFTYP_SYS16B   (0x8 << 0)
-#define LDMT1R_MIFTYP_SYS16C   (0x9 << 0)
-#define LDMT1R_MIFTYP_SYS18    (0xa << 0)
-#define LDMT1R_MIFTYP_SYS24    (0xb << 0)
-#define LDMT1R_MIFTYP_MASK     (0xf << 0)
-#define LDMT2R                 0x41c
-#define LDMT2R_CSUP_MASK       (7 << 26)
-#define LDMT2R_CSUP_SHIFT      26
-#define LDMT2R_RSV             (1 << 25)
-#define LDMT2R_VSEL            (1 << 24)
-#define LDMT2R_WCSC_MASK       (0xff << 16)
-#define LDMT2R_WCSC_SHIFT      16
-#define LDMT2R_WCEC_MASK       (0xff << 8)
-#define LDMT2R_WCEC_SHIFT      8
-#define LDMT2R_WCLW_MASK       (0xff << 0)
-#define LDMT2R_WCLW_SHIFT      0
-#define LDMT3R                 0x420
-#define LDMT3R_RDLC_MASK       (0x3f << 24)
-#define LDMT3R_RDLC_SHIFT      24
-#define LDMT3R_RCSC_MASK       (0xff << 16)
-#define LDMT3R_RCSC_SHIFT      16
-#define LDMT3R_RCEC_MASK       (0xff << 8)
-#define LDMT3R_RCEC_SHIFT      8
-#define LDMT3R_RCLW_MASK       (0xff << 0)
-#define LDMT3R_RCLW_SHIFT      0
-#define LDDFR                  0x424
-#define LDDFR_CF1              (1 << 18)
-#define LDDFR_CF0              (1 << 17)
-#define LDDFR_CC               (1 << 16)
-#define LDDFR_YF_420           (0 << 8)
-#define LDDFR_YF_422           (1 << 8)
-#define LDDFR_YF_444           (2 << 8)
-#define LDDFR_YF_MASK          (3 << 8)
-#define LDDFR_PKF_ARGB32       (0x00 << 0)
-#define LDDFR_PKF_RGB16                (0x03 << 0)
-#define LDDFR_PKF_RGB24                (0x0b << 0)
-#define LDDFR_PKF_MASK         (0x1f << 0)
-#define LDSM1R                 0x428
-#define LDSM1R_OS              (1 << 0)
-#define LDSM2R                 0x42c
-#define LDSM2R_OSTRG           (1 << 0)
-#define LDSA1R                 0x430
-#define LDSA2R                 0x434
-#define LDMLSR                 0x438
-#define LDWBFR                 0x43c
-#define LDWBCNTR               0x440
-#define LDWBAR                 0x444
-#define LDHCNR                 0x448
-#define LDHSYNR                        0x44c
-#define LDVLNR                 0x450
-#define LDVSYNR                        0x454
-#define LDHPDR                 0x458
-#define LDVPDR                 0x45c
-#define LDPMR                  0x460
-#define LDPMR_LPS              (3 << 0)
-#define LDINTR                 0x468
-#define LDINTR_FE              (1 << 10)
-#define LDINTR_VSE             (1 << 9)
-#define LDINTR_VEE             (1 << 8)
-#define LDINTR_FS              (1 << 2)
-#define LDINTR_VSS             (1 << 1)
-#define LDINTR_VES             (1 << 0)
-#define LDINTR_STATUS_MASK     (0xff << 0)
-#define LDSR                   0x46c
-#define LDSR_MSS               (1 << 10)
-#define LDSR_MRS               (1 << 8)
-#define LDSR_AS                        (1 << 1)
-#define LDCNT1R                        0x470
-#define LDCNT1R_DE             (1 << 0)
-#define LDCNT2R                        0x474
-#define LDCNT2R_BR             (1 << 8)
-#define LDCNT2R_MD             (1 << 3)
-#define LDCNT2R_SE             (1 << 2)
-#define LDCNT2R_ME             (1 << 1)
-#define LDCNT2R_DO             (1 << 0)
-#define LDRCNTR                        0x478
-#define LDRCNTR_SRS            (1 << 17)
-#define LDRCNTR_SRC            (1 << 16)
-#define LDRCNTR_MRS            (1 << 1)
-#define LDRCNTR_MRC            (1 << 0)
-#define LDDDSR                 0x47c
-#define LDDDSR_LS              (1 << 2)
-#define LDDDSR_WS              (1 << 1)
-#define LDDDSR_BS              (1 << 0)
-#define LDHAJR                 0x4a0
-
-#define LDDWD0R                        0x800
-#define LDDWDxR_WDACT          (1 << 28)
-#define LDDWDxR_RSW            (1 << 24)
-#define LDDRDR                 0x840
-#define LDDRDR_RSR             (1 << 24)
-#define LDDRDR_DRD_MASK                (0x3ffff << 0)
-#define LDDWAR                 0x900
-#define LDDWAR_WA              (1 << 0)
-#define LDDRAR                 0x904
-#define LDDRAR_RA              (1 << 0)
-
-#define LDBCR                  0xb00
-#define LDBCR_UPC(n)           (1 << ((n) + 16))
-#define LDBCR_UPF(n)           (1 << ((n) + 8))
-#define LDBCR_UPD(n)           (1 << ((n) + 0))
-#define LDBnBSIFR(n)           (0xb20 + (n) * 0x20 + 0x00)
-#define LDBBSIFR_EN            (1 << 31)
-#define LDBBSIFR_VS            (1 << 29)
-#define LDBBSIFR_BRSEL         (1 << 28)
-#define LDBBSIFR_MX            (1 << 27)
-#define LDBBSIFR_MY            (1 << 26)
-#define LDBBSIFR_CV3           (3 << 24)
-#define LDBBSIFR_CV2           (2 << 24)
-#define LDBBSIFR_CV1           (1 << 24)
-#define LDBBSIFR_CV0           (0 << 24)
-#define LDBBSIFR_CV_MASK       (3 << 24)
-#define LDBBSIFR_LAY_MASK      (0xff << 16)
-#define LDBBSIFR_LAY_SHIFT     16
-#define LDBBSIFR_ROP3_MASK     (0xff << 16)
-#define LDBBSIFR_ROP3_SHIFT    16
-#define LDBBSIFR_AL_PL8                (3 << 14)
-#define LDBBSIFR_AL_PL1                (2 << 14)
-#define LDBBSIFR_AL_PK         (1 << 14)
-#define LDBBSIFR_AL_1          (0 << 14)
-#define LDBBSIFR_AL_MASK       (3 << 14)
-#define LDBBSIFR_SWPL          (1 << 10)
-#define LDBBSIFR_SWPW          (1 << 9)
-#define LDBBSIFR_SWPB          (1 << 8)
-#define LDBBSIFR_RY            (1 << 7)
-#define LDBBSIFR_CHRR_420      (2 << 0)
-#define LDBBSIFR_CHRR_422      (1 << 0)
-#define LDBBSIFR_CHRR_444      (0 << 0)
-#define LDBBSIFR_RPKF_ARGB32   (0x00 << 0)
-#define LDBBSIFR_RPKF_RGB16    (0x03 << 0)
-#define LDBBSIFR_RPKF_RGB24    (0x0b << 0)
-#define LDBBSIFR_RPKF_MASK     (0x1f << 0)
-#define LDBnBSSZR(n)           (0xb20 + (n) * 0x20 + 0x04)
-#define LDBBSSZR_BVSS_MASK     (0xfff << 16)
-#define LDBBSSZR_BVSS_SHIFT    16
-#define LDBBSSZR_BHSS_MASK     (0xfff << 0)
-#define LDBBSSZR_BHSS_SHIFT    0
-#define LDBnBLOCR(n)           (0xb20 + (n) * 0x20 + 0x08)
-#define LDBBLOCR_CVLC_MASK     (0xfff << 16)
-#define LDBBLOCR_CVLC_SHIFT    16
-#define LDBBLOCR_CHLC_MASK     (0xfff << 0)
-#define LDBBLOCR_CHLC_SHIFT    0
-#define LDBnBSMWR(n)           (0xb20 + (n) * 0x20 + 0x0c)
-#define LDBBSMWR_BSMWA_MASK    (0xffff << 16)
-#define LDBBSMWR_BSMWA_SHIFT   16
-#define LDBBSMWR_BSMW_MASK     (0xffff << 0)
-#define LDBBSMWR_BSMW_SHIFT    0
-#define LDBnBSAYR(n)           (0xb20 + (n) * 0x20 + 0x10)
-#define LDBBSAYR_FG1A_MASK     (0xff << 24)
-#define LDBBSAYR_FG1A_SHIFT    24
-#define LDBBSAYR_FG1R_MASK     (0xff << 16)
-#define LDBBSAYR_FG1R_SHIFT    16
-#define LDBBSAYR_FG1G_MASK     (0xff << 8)
-#define LDBBSAYR_FG1G_SHIFT    8
-#define LDBBSAYR_FG1B_MASK     (0xff << 0)
-#define LDBBSAYR_FG1B_SHIFT    0
-#define LDBnBSACR(n)           (0xb20 + (n) * 0x20 + 0x14)
-#define LDBBSACR_FG2A_MASK     (0xff << 24)
-#define LDBBSACR_FG2A_SHIFT    24
-#define LDBBSACR_FG2R_MASK     (0xff << 16)
-#define LDBBSACR_FG2R_SHIFT    16
-#define LDBBSACR_FG2G_MASK     (0xff << 8)
-#define LDBBSACR_FG2G_SHIFT    8
-#define LDBBSACR_FG2B_MASK     (0xff << 0)
-#define LDBBSACR_FG2B_SHIFT    0
-#define LDBnBSAAR(n)           (0xb20 + (n) * 0x20 + 0x18)
-#define LDBBSAAR_AP_MASK       (0xff << 24)
-#define LDBBSAAR_AP_SHIFT      24
-#define LDBBSAAR_R_MASK                (0xff << 16)
-#define LDBBSAAR_R_SHIFT       16
-#define LDBBSAAR_GY_MASK       (0xff << 8)
-#define LDBBSAAR_GY_SHIFT      8
-#define LDBBSAAR_B_MASK                (0xff << 0)
-#define LDBBSAAR_B_SHIFT       0
-#define LDBnBPPCR(n)           (0xb20 + (n) * 0x20 + 0x1c)
-#define LDBBPPCR_AP_MASK       (0xff << 24)
-#define LDBBPPCR_AP_SHIFT      24
-#define LDBBPPCR_R_MASK                (0xff << 16)
-#define LDBBPPCR_R_SHIFT       16
-#define LDBBPPCR_GY_MASK       (0xff << 8)
-#define LDBBPPCR_GY_SHIFT      8
-#define LDBBPPCR_B_MASK                (0xff << 0)
-#define LDBBPPCR_B_SHIFT       0
-#define LDBnBBGCL(n)           (0xb10 + (n) * 0x04)
-#define LDBBBGCL_BGA_MASK      (0xff << 24)
-#define LDBBBGCL_BGA_SHIFT     24
-#define LDBBBGCL_BGR_MASK      (0xff << 16)
-#define LDBBBGCL_BGR_SHIFT     16
-#define LDBBBGCL_BGG_MASK      (0xff << 8)
-#define LDBBBGCL_BGG_SHIFT     8
-#define LDBBBGCL_BGB_MASK      (0xff << 0)
-#define LDBBBGCL_BGB_SHIFT     0
-
-#define LCDC_SIDE_B_OFFSET     0x1000
-#define LCDC_MIRROR_OFFSET     0x2000
-
-static inline bool lcdc_is_banked(u32 reg)
-{
-       switch (reg) {
-       case LDMT1R:
-       case LDMT2R:
-       case LDMT3R:
-       case LDDFR:
-       case LDSM1R:
-       case LDSA1R:
-       case LDSA2R:
-       case LDMLSR:
-       case LDWBFR:
-       case LDWBCNTR:
-       case LDWBAR:
-       case LDHCNR:
-       case LDHSYNR:
-       case LDVLNR:
-       case LDVSYNR:
-       case LDHPDR:
-       case LDVPDR:
-       case LDHAJR:
-               return true;
-       default:
-               return reg >= LDBnBBGCL(0) && reg <= LDBnBPPCR(3);
-       }
-}
-
-static inline void lcdc_write_mirror(struct shmob_drm_device *sdev, u32 reg,
-                                    u32 data)
-{
-       iowrite32(data, sdev->mmio + reg + LCDC_MIRROR_OFFSET);
-}
-
-static inline void lcdc_write(struct shmob_drm_device *sdev, u32 reg, u32 data)
-{
-       iowrite32(data, sdev->mmio + reg);
-       if (lcdc_is_banked(reg))
-               iowrite32(data, sdev->mmio + reg + LCDC_SIDE_B_OFFSET);
-}
-
-static inline u32 lcdc_read(struct shmob_drm_device *sdev, u32 reg)
-{
-       return ioread32(sdev->mmio + reg);
-}
-
-static inline int lcdc_wait_bit(struct shmob_drm_device *sdev, u32 reg,
-                               u32 mask, u32 until)
-{
-       unsigned long timeout = jiffies + msecs_to_jiffies(5);
-
-       while ((lcdc_read(sdev, reg) & mask) != until) {
-               if (time_after(jiffies, timeout))
-                       return -ETIMEDOUT;
-               cpu_relax();
-       }
-
-       return 0;
-}
-
-#endif /* __SHMOB_DRM_REGS_H__ */