From bfa8342c27c67e86a7b7022df06848709386e00d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 12 Apr 2023 10:09:20 +0200 Subject: MAINTAINERS: add drm_bridge for drm bridge maintainers Otherwise core changes don't get noticed by the right people. I noticed this because a patch set from Jagan Teki seems to have fallen through the cracks. Signed-off-by: Daniel Vetter Signed-off-by: Daniel Vetter Cc: Jagan Teki Cc: Andrzej Hajda Cc: Neil Armstrong Cc: Robert Foss Cc: Laurent Pinchart Cc: Jonas Karlman Cc: Jernej Skrabec Reviewed-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Acked-by: Neil Armstrong Reviewed-by: Jagan Teki [narmstrong: fixed ordering & Daniel's SoB] Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20230412080921.10171-1-daniel.vetter@ffwll.ch --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 904500cbdcf0..1558eebdcdea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6841,6 +6841,7 @@ S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/bridge/ F: drivers/gpu/drm/bridge/ +F: drivers/gpu/drm/drm_bridge.c F: include/drm/drm_bridge.h DRM DRIVERS FOR EXYNOS -- cgit v1.2.3 From 79c87edd18ec49f5b6fb40175bd1b1fea9398fdb Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Mon, 8 May 2023 11:10:39 -0300 Subject: MAINTAINERS: Add Maira to VKMS maintainers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've been contributing to VKMS with improvements, reviews, testing and debugging. Therefore, add myself as a co-maintainer of the VKMS driver. Acked-by: Melissa Wen Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Link: https://patchwork.freedesktop.org/patch/msgid/20230508141038.327160-1-mairacanal@riseup.net --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 276298cfc7ee..63e9b1e8c113 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6721,6 +6721,7 @@ F: drivers/gpu/drm/udl/ DRM DRIVER FOR VIRTUAL KERNEL MODESETTING (VKMS) M: Rodrigo Siqueira M: Melissa Wen +M: Maíra Canal R: Haneen Mohammed R: Daniel Vetter L: dri-devel@lists.freedesktop.org -- cgit v1.2.3 From 3bc7fb9ac9307583f11c991ae72369499a604621 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 12 Apr 2023 00:43:08 +0200 Subject: MAINTAINERS: Add Marijn Suijten as drm/msm reviewer As I get more and more active in the drm/msm space, yet sometimes miss out on patches (where I was involved in previous discussions), add myself as reviewer to make this involvement clear. Signed-off-by: Marijn Suijten Acked-by: Dmitry Baryshkov Acked-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/531770/ Link: https://lore.kernel.org/r/20230411224308.440550-1-marijn.suijten@somainline.org Signed-off-by: Dmitry Baryshkov --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index dd389b88c512..633bd47c4aba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6514,6 +6514,7 @@ M: Rob Clark M: Abhinav Kumar M: Dmitry Baryshkov R: Sean Paul +R: Marijn Suijten L: linux-arm-msm@vger.kernel.org L: dri-devel@lists.freedesktop.org L: freedreno@lists.freedesktop.org -- cgit v1.2.3 From 0dd53308f74fcb16aa4e5cb90739b831c4a558de Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Fri, 19 May 2023 19:03:54 +0200 Subject: MAINTAINERS: Add entry for Samsung S6D7AA0 LCD panel controller driver Add myself as maintainer of the Samsung S6D7AA0 panel driver. Signed-off-by: Artur Weber Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20230519170354.29610-4-aweber.kernel@gmail.com --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 402e26d0cdbc..7cc2bfa4af6f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6663,6 +6663,12 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/samsung,s6d27a1.yaml F: drivers/gpu/drm/panel/panel-samsung-s6d27a1.c +DRM DRIVER FOR SAMSUNG S6D7AA0 PANELS +M: Artur Weber +S: Maintained +F: Documentation/devicetree/bindings/display/panel/samsung,s6d7aa0.yaml +F: drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c + DRM DRIVER FOR SITRONIX ST7703 PANELS M: Guido Günther R: Purism Kernel Team -- cgit v1.2.3 From 11696c5e89245a1d360f75be3dfc4960b25a265a Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 2 May 2023 11:09:08 +0100 Subject: drm: Place Renesas drivers in a separate dir Create vendor specific renesas directory and move renesas drivers to that directory. Signed-off-by: Biju Das Acked-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- MAINTAINERS | 3 +- drivers/gpu/drm/Kconfig | 4 +- drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/rcar-du/Kconfig | 82 -- drivers/gpu/drm/rcar-du/Makefile | 18 - drivers/gpu/drm/rcar-du/rcar_cmm.c | 217 ---- drivers/gpu/drm/rcar-du/rcar_cmm.h | 58 - drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 1338 -------------------- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 103 -- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 744 ----------- drivers/gpu/drm/rcar-du/rcar_du_drv.h | 152 --- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 137 -- drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 29 - drivers/gpu/drm/rcar-du/rcar_du_group.c | 377 ------ drivers/gpu/drm/rcar-du/rcar_du_group.h | 65 - drivers/gpu/drm/rcar-du/rcar_du_kms.c | 1006 --------------- drivers/gpu/drm/rcar-du/rcar_du_kms.h | 44 - drivers/gpu/drm/rcar-du/rcar_du_plane.c | 831 ------------ drivers/gpu/drm/rcar-du/rcar_du_plane.h | 86 -- drivers/gpu/drm/rcar-du/rcar_du_regs.h | 553 -------- drivers/gpu/drm/rcar-du/rcar_du_vsp.c | 513 -------- drivers/gpu/drm/rcar-du/rcar_du_vsp.h | 93 -- drivers/gpu/drm/rcar-du/rcar_du_writeback.c | 246 ---- drivers/gpu/drm/rcar-du/rcar_du_writeback.h | 39 - drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c | 124 -- drivers/gpu/drm/rcar-du/rcar_lvds.c | 1035 --------------- drivers/gpu/drm/rcar-du/rcar_lvds.h | 41 - drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 111 -- drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c | 1106 ---------------- drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h | 31 - drivers/gpu/drm/rcar-du/rcar_mipi_dsi_regs.h | 176 --- drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi.c | 816 ------------ drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi_regs.h | 151 --- drivers/gpu/drm/renesas/Kconfig | 4 + drivers/gpu/drm/renesas/Makefile | 4 + drivers/gpu/drm/renesas/rcar-du/Kconfig | 82 ++ drivers/gpu/drm/renesas/rcar-du/Makefile | 18 + drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c | 217 ++++ drivers/gpu/drm/renesas/rcar-du/rcar_cmm.h | 58 + drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c | 1338 ++++++++++++++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h | 103 ++ drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c | 744 +++++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h | 152 +++ drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c | 137 ++ drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h | 29 + drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c | 377 ++++++ drivers/gpu/drm/renesas/rcar-du/rcar_du_group.h | 65 + drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c | 1006 +++++++++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.h | 44 + drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c | 831 ++++++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h | 86 ++ drivers/gpu/drm/renesas/rcar-du/rcar_du_regs.h | 553 ++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c | 513 ++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.h | 93 ++ .../gpu/drm/renesas/rcar-du/rcar_du_writeback.c | 246 ++++ .../gpu/drm/renesas/rcar-du/rcar_du_writeback.h | 39 + drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c | 124 ++ drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c | 1035 +++++++++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_lvds.h | 41 + drivers/gpu/drm/renesas/rcar-du/rcar_lvds_regs.h | 111 ++ drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c | 1106 ++++++++++++++++ drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.h | 31 + .../gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h | 176 +++ drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c | 816 ++++++++++++ .../gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h | 151 +++ drivers/gpu/drm/renesas/shmobile/Kconfig | 12 + drivers/gpu/drm/renesas/shmobile/Makefile | 8 + .../gpu/drm/renesas/shmobile/shmob_drm_backlight.c | 82 ++ .../gpu/drm/renesas/shmobile/shmob_drm_backlight.h | 19 + drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c | 712 +++++++++++ drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.h | 55 + drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c | 302 +++++ drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.h | 42 + drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.c | 155 +++ drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.h | 29 + drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c | 265 ++++ drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.h | 19 + drivers/gpu/drm/renesas/shmobile/shmob_drm_regs.h | 310 +++++ drivers/gpu/drm/shmobile/Kconfig | 12 - drivers/gpu/drm/shmobile/Makefile | 8 - drivers/gpu/drm/shmobile/shmob_drm_backlight.c | 82 -- drivers/gpu/drm/shmobile/shmob_drm_backlight.h | 19 - drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 712 ----------- drivers/gpu/drm/shmobile/shmob_drm_crtc.h | 55 - drivers/gpu/drm/shmobile/shmob_drm_drv.c | 302 ----- drivers/gpu/drm/shmobile/shmob_drm_drv.h | 42 - drivers/gpu/drm/shmobile/shmob_drm_kms.c | 155 --- drivers/gpu/drm/shmobile/shmob_drm_kms.h | 29 - drivers/gpu/drm/shmobile/shmob_drm_plane.c | 265 ---- drivers/gpu/drm/shmobile/shmob_drm_plane.h | 19 - drivers/gpu/drm/shmobile/shmob_drm_regs.h | 310 ----- 91 files changed, 12343 insertions(+), 12339 deletions(-) delete mode 100644 drivers/gpu/drm/rcar-du/Kconfig delete mode 100644 drivers/gpu/drm/rcar-du/Makefile delete mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_cmm.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_crtc.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_crtc.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_drv.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_drv.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_encoder.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_encoder.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_group.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_group.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_kms.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_kms.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_plane.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_plane.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_regs.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vsp.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vsp.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_writeback.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_lvds.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_lvds.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_lvds_regs.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c delete mode 100644 drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h delete mode 100644 drivers/gpu/drm/rcar-du/rcar_mipi_dsi_regs.h delete mode 100644 drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi.c delete mode 100644 drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi_regs.h create mode 100644 drivers/gpu/drm/renesas/Kconfig create mode 100644 drivers/gpu/drm/renesas/Makefile create mode 100644 drivers/gpu/drm/renesas/rcar-du/Kconfig create mode 100644 drivers/gpu/drm/renesas/rcar-du/Makefile create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_cmm.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_group.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_regs.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_lvds.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_lvds_regs.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h create mode 100644 drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c create mode 100644 drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h create mode 100644 drivers/gpu/drm/renesas/shmobile/Kconfig create mode 100644 drivers/gpu/drm/renesas/shmobile/Makefile create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.c create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.h create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.h create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.h create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.c create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.h create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.h create mode 100644 drivers/gpu/drm/renesas/shmobile/shmob_drm_regs.h delete mode 100644 drivers/gpu/drm/shmobile/Kconfig delete mode 100644 drivers/gpu/drm/shmobile/Makefile delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_backlight.c delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_backlight.h delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_crtc.c delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_crtc.h delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_drv.c delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_drv.h delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_kms.c delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_kms.h delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_plane.c delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_plane.h delete mode 100644 drivers/gpu/drm/shmobile/shmob_drm_regs.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index b344e1318ac3..38dc099ad8ff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ba3fb04bb691..41aa8b07252b 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -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" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a33257d2bc7f..982d9e06168a 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -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 index 53c356aed5d5..000000000000 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ /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 index b8f2c82651d9..000000000000 --- a/drivers/gpu/drm/rcar-du/Makefile +++ /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 index e2a67dda4658..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_cmm.c +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * R-Car Display Unit Color Management Module - * - * Copyright (C) 2019 Jacopo Mondi - */ - -#include -#include -#include -#include -#include - -#include - -#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 "); -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 index 628072acc98b..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_cmm.h +++ /dev/null @@ -1,58 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * R-Car Display Unit Color Management Module - * - * Copyright (C) 2019 Jacopo Mondi - */ - -#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 index 7e175dbfd892..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ /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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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, ¶ms); - if (rcrtc->extclock) - rcar_du_escr_divider(rcrtc->extclock, mode_clock, - ESCR_DCLKSEL_DCLKIN, ¶ms); - - 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, ©->state); - - return ©->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 index d0f38a8b3561..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ /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 -#include -#include - -#include -#include - -#include - -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 index 1ffde19cb87f..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#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 "); -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 index 5cfa2bb7ad93..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ /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 -#include - -#include - -#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 index 7ecec7b04a8d..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ /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 -#include - -#include -#include -#include - -#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 index e5ec8fbb3979..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ /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 - -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 index 2ccd2581f544..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ /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 -#include - -#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 index 55649ad86a10..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.h +++ /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 - -#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 index adfb36b0e815..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#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 index f31afeeee05a..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h +++ /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 - -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 index d759e0192181..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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, ©->state); - - return ©->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 index f9893d7d6dfc..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ /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 - -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 index 391de6661d8b..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ /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 index 45c05d0ffc70..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#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, ©->state); - - return ©->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 index 67630f0b6599..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h +++ /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 - -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 index 8cd37d7b8ae2..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_writeback.c +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * R-Car Display Unit Writeback Support - * - * Copyright (C) 2019 Laurent Pinchart - */ - -#include -#include -#include -#include -#include -#include -#include - -#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, ©->state); - - return ©->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 index a71c9c08cafa..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_writeback.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * R-Car Display Unit Writeback Support - * - * Copyright (C) 2019 Laurent Pinchart - */ - -#ifndef __RCAR_DU_WRITEBACK_H__ -#define __RCAR_DU_WRITEBACK_H__ - -#include - -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 index 18ed14911b98..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c +++ /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 -#include -#include - -#include -#include - -#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 "); -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 index ca215b588fd7..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 "); -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 index 887c63500000..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.h +++ /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 index ab0406a27d33..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h +++ /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 index e10e4d4b89a2..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c +++ /dev/null @@ -1,1106 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * R-Car MIPI DSI Encoder - * - * Copyright (C) 2020 Renesas Electronics Corporation - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 index 528a196e6edd..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h +++ /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 - */ - -#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 index f8114d11f2d1..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi_regs.h +++ /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 index aa95b85a2964..000000000000 --- a/drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 "); -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 index 1dbc16ec64a4..000000000000 --- a/drivers/gpu/drm/rcar-du/rzg2l_mipi_dsi_regs.h +++ /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 - -/* 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 index 000000000000..3777dad17f81 --- /dev/null +++ b/drivers/gpu/drm/renesas/Kconfig @@ -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 index 000000000000..ec0e89e7a592 --- /dev/null +++ b/drivers/gpu/drm/renesas/Makefile @@ -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 index 000000000000..53c356aed5d5 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/Kconfig @@ -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 index 000000000000..b8f2c82651d9 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/Makefile @@ -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 index 000000000000..e2a67dda4658 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * R-Car Display Unit Color Management Module + * + * Copyright (C) 2019 Jacopo Mondi + */ + +#include +#include +#include +#include +#include + +#include + +#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 "); +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 index 000000000000..628072acc98b --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_cmm.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * R-Car Display Unit Color Management Module + * + * Copyright (C) 2019 Jacopo Mondi + */ + +#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 index 000000000000..7e175dbfd892 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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, ¶ms); + if (rcrtc->extclock) + rcar_du_escr_divider(rcrtc->extclock, mode_clock, + ESCR_DCLKSEL_DCLKIN, ¶ms); + + 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, ©->state); + + return ©->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 index 000000000000..d0f38a8b3561 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h @@ -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 +#include +#include + +#include +#include + +#include + +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 index 000000000000..1ffde19cb87f --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 "); +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 index 000000000000..5cfa2bb7ad93 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h @@ -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 +#include + +#include + +#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 index 000000000000..7ecec7b04a8d --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c @@ -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 +#include + +#include +#include +#include + +#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 index 000000000000..e5ec8fbb3979 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h @@ -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 + +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 index 000000000000..2ccd2581f544 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c @@ -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 +#include + +#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 index 000000000000..55649ad86a10 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_group.h @@ -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 + +#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 index 000000000000..adfb36b0e815 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 index 000000000000..f31afeeee05a --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.h @@ -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 + +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 index 000000000000..d759e0192181 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ©->state); + + return ©->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 index 000000000000..f9893d7d6dfc --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h @@ -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 + +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 index 000000000000..391de6661d8b --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_regs.h @@ -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 index 000000000000..45c05d0ffc70 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#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, ©->state); + + return ©->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 index 000000000000..67630f0b6599 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.h @@ -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 + +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 index 000000000000..8cd37d7b8ae2 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car Display Unit Writeback Support + * + * Copyright (C) 2019 Laurent Pinchart + */ + +#include +#include +#include +#include +#include +#include +#include + +#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, ©->state); + + return ©->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 index 000000000000..a71c9c08cafa --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_writeback.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * R-Car Display Unit Writeback Support + * + * Copyright (C) 2019 Laurent Pinchart + */ + +#ifndef __RCAR_DU_WRITEBACK_H__ +#define __RCAR_DU_WRITEBACK_H__ + +#include + +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 index 000000000000..18ed14911b98 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c @@ -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 +#include +#include + +#include +#include + +#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 "); +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 index 000000000000..ca215b588fd7 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 index 000000000000..887c63500000 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.h @@ -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 index 000000000000..ab0406a27d33 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds_regs.h @@ -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 index 000000000000..e10e4d4b89a2 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c @@ -0,0 +1,1106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car MIPI DSI Encoder + * + * Copyright (C) 2020 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..528a196e6edd --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * R-Car DSI Encoder + * + * Copyright (C) 2022 Renesas Electronics Corporation + * + * Contact: Tomi Valkeinen + */ + +#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 index 000000000000..f8114d11f2d1 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h @@ -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 index 000000000000..aa95b85a2964 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L MIPI DSI Encoder Driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 index 000000000000..1dbc16ec64a4 --- /dev/null +++ b/drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h @@ -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 + +/* 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 index 000000000000..ad14112999ad --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/Kconfig @@ -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 index 000000000000..861edafed856 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/Makefile @@ -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 index 000000000000..794573badfe8 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.c @@ -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 + +#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 index 000000000000..d9abb7a60be5 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_backlight.h @@ -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 index 000000000000..11dd2bc803e7 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.c @@ -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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..21718843f46d --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_crtc.h @@ -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 +#include +#include + +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 index 000000000000..30493ce87419 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 "); +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 index 000000000000..4964ddd5ab74 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.h @@ -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 +#include +#include + +#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 index 000000000000..99381cc0abf3 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.c @@ -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 +#include +#include +#include +#include +#include + +#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 index 000000000000..0347b1fd2338 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_kms.h @@ -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 + +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 index 000000000000..850986cee848 --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c @@ -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 +#include +#include +#include +#include + +#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 index 000000000000..e72b21a4288f --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.h @@ -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 index 000000000000..058533685c4c --- /dev/null +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_regs.h @@ -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 +#include + +#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 index ad14112999ad..000000000000 --- a/drivers/gpu/drm/shmobile/Kconfig +++ /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 index 861edafed856..000000000000 --- a/drivers/gpu/drm/shmobile/Makefile +++ /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 index 794573badfe8..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_backlight.c +++ /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 - -#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 index d9abb7a60be5..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_backlight.h +++ /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 index 11dd2bc803e7..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ /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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index 21718843f46d..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h +++ /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 -#include -#include - -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 index 30493ce87419..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ /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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#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 "); -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 index 4964ddd5ab74..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.h +++ /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 -#include -#include - -#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 index 99381cc0abf3..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_kms.c +++ /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 -#include -#include -#include -#include -#include - -#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 index 0347b1fd2338..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_kms.h +++ /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 - -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 index 850986cee848..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ /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 -#include -#include -#include -#include - -#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 index e72b21a4288f..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.h +++ /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 index 058533685c4c..000000000000 --- a/drivers/gpu/drm/shmobile/shmob_drm_regs.h +++ /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 -#include - -#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__ */ -- cgit v1.2.3 From 0fcf8ffdfe9013b05807b99a4c28da59b2d39bec Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Tue, 23 May 2023 10:14:21 -0600 Subject: MAINTAINERS: Add Carl/Pranjal as QAIC reviewers Carl and Pranjal have been reviewing the QAIC patches. List them as reviewers so that they are copied on all developments which will make it easier for them to continue reviewing QAIC patches. Signed-off-by: Jeffrey Hugo Acked-by: Carl Vanderlip Acked-by: Pranjal Ramajor Asha Kanojiya Link: https://patchwork.freedesktop.org/patch/msgid/20230523161421.11017-1-quic_jhugo@quicinc.com --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index b344e1318ac3..aa0aa6c31772 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17388,6 +17388,8 @@ F: include/dt-bindings/clock/qcom,* QUALCOMM CLOUD AI (QAIC) DRIVER M: Jeffrey Hugo +R: Carl Vanderlip +R: Pranjal Ramajor Asha Kanojiya L: linux-arm-msm@vger.kernel.org L: dri-devel@lists.freedesktop.org S: Supported -- cgit v1.2.3