From patchwork Mon Apr 3 17:13:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 78645 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp2471732vqo; Mon, 3 Apr 2023 10:37:14 -0700 (PDT) X-Google-Smtp-Source: AKy350aspRisLH+lbgrnestBGDx6otmbyM+Z89oeqiRvwC8WqdluMS0G38T7kJhFDFT/OD8CrSSM X-Received: by 2002:a17:903:18b:b0:19e:8267:9590 with SMTP id z11-20020a170903018b00b0019e82679590mr44456189plg.41.1680543434015; Mon, 03 Apr 2023 10:37:14 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1680543433; cv=none; d=google.com; s=arc-20160816; b=kwsK26hVWkcES6Ig9ay6USR3OLAGF42EieS8SHBpJk8UiE7ZDWhBPKh1xz3/wuiwm/ OXEDATZ5DShg0zXmXsZGL7rRUP3RAcAx1In1wFs/LuiLYitpdrNdd/zrHUd9iDAw8/0f thK9c+U1MMUzOvszGsYBJuqpbDkU75gif/ud5Vyv9s23vG8YyLo0IZ9nAQ1yKusvXvTT EZgucfcYrBX2RhXgpylS5Bx9V1nhI0/zgcDJHYOncpZoUKB9Igh34J130MMEBA7Xdcnp RVRmeM0/aN9ask1/E+73yAjpytwF+GnLuz6GN2MWcYfdr7Us+wR5sN/XLGmU9uQcJmUB NFpA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=viTfjcARbSInoM+wbqUg0lh+xANjPBzvNXTK+LpDjYg=; b=IXP/5xgDOqEJ74w+Sk2DyGMZ2avoR0TNTYljib+sZL7lFBunomvoqoYT+HYhWFqhWF 3jVzzsMNQTkMyYv4UtQPMi+9SPIiJuXnsPjXRNhjjDYPP3r8XwcQAXz6PEEBq1wV1Cnw jM+SbsMvDp+B7PuRNBKTbuYxLouWDcwTyAfggCqtXZm6MTxfV99I+LPVC14hBGNHtKFr uBUw07r2wb5+TcmBzl5bSBh9R8Hykj7CA3c0Dpf4Jz2AM0hPr5/kWPOjpWuCy9ZFIIWU 2lAkoXST7rXzVo0f1DP9nQhtULKxEBitlpFk1uIdOFibcZA/RqLTeIEXA/KBIVoqM8ZO k6jA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id x2-20020a1709027c0200b00199385bd2c9si8187455pll.214.2023.04.03.10.37.01; Mon, 03 Apr 2023 10:37:13 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233082AbjDCRNX (ORCPT + 99 others); Mon, 3 Apr 2023 13:13:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49016 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231411AbjDCRNR (ORCPT ); Mon, 3 Apr 2023 13:13:17 -0400 Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E56D3212D; Mon, 3 Apr 2023 10:13:10 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.133]) by gateway (Coremail) with SMTP id _____8AxJFwlCStk5CUWAA--.34065S3; Tue, 04 Apr 2023 01:13:09 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.133]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxSL0gCStkqJIUAA--.16596S4; Tue, 04 Apr 2023 01:13:08 +0800 (CST) From: Sui Jingfeng To: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , suijingfeng , Sumit Semwal , Christian Koenig Cc: nathan@kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Li Yi Subject: [PATCH v10 2/2] drm: add kms driver for loongson display controller Date: Tue, 4 Apr 2023 01:13:04 +0800 Message-Id: <20230403171304.2157326-3-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230403171304.2157326-1-suijingfeng@loongson.cn> References: <20230403171304.2157326-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8DxSL0gCStkqJIUAA--.16596S4 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBjvtXoWDur4xXw1DXFWkKryUWFWDtwb_yoW8GF1fJw b_CFy2vFyDGw1DCF1Fgw4UGw4xtas3CrZ7ZF4DWFWftFZ3trsrX345ArZ8XrWxurWUuw18 JFyDG34vkryIyF1IgjkaLaAFLSUrUUUU5b8apTn2vfkv8UJUUUU8wcxFpf9Il3svdxBIda Vrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3UjIYCTnIWjp_ UUUYX7kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI8IcIk0rVWrJV Cq3wAFIxvE14AKwVWUXVWUAwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK021l84AC jcxK6xIIjxv20xvE14v26ryj6F1UM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4j6F4UM2 8EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E14v26r4j6r4UJwAa w2AFwI0_JF0_Jw1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0cIa020Ex4CE44 I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_Jw0_WrylYx0Ex4A2 jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwIxGrwCY1x0262 kKe7AKxVWUtVW8ZwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwCFI7km 07C267AKxVWUAVWUtwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r 1rMI8E67AF67kF1VAFwI0_Jw0_GFylIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVW5 JVW7JwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF04k26cxKx2IYs7xG6r 1j6r1xMIIF0xvEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr0_Gr1U YxBIdaVFxhVjvjDU0xZFpf9x07jr9NsUUUUU= X-Spam-Status: No, score=-0.0 required=5.0 tests=SPF_HELO_PASS,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1762176475087965159?= X-GMAIL-MSGID: =?utf-8?q?1762177511799454170?= Loongson display controller IP has been integrated in both Loongson north bridge chipsets(ls7a1000/ls7a2000) and SoCs(ls2k1000/ls2k2000), it has been included in Loongson self-made BMC products. This display controller is a PCI device in all of chips mentiond, it has two display pipes which support primary planes and cursor plane. For the DC in ls7a1000 and ls2k1000, each display pipe has a DVO output interface which provide RGB888 signals, vertical & horizontal synchronisations and the pixel clock. Each CRTC is able to support 1920x1080@60Hz, the maximum resolution is 2048x2048 according to the hardware spec. For the DC in LS7A2000, each display pipe is equipped with a built-in HDMI encoder which is compliant with the HDMI 1.4 specification, thus it support 3840x2160@30Hz. The first display pipe is also equipped with a transparent vga encoder which is parallel with the HDMI encoder. The DC in LS7A2000 is more complete compare with the one in old chips, besides above feature, it has two hardware cursors, two hardware vblank counter and two scanout position recorders unit. It also support tiled framebuffer format which can be used to scan out the framebuffer rendered by the LoongGPU directly. v1 -> v2: 1) Use hpd status reg when polling for ls7a2000 2) Fix all warnings emerged when compile with W=1 v2 -> v3: 1) Add COMPILE_TEST in Kconfig and make the driver off by default 2) Alphabetical sorting headers (Thomas) 3) Untangle register access functions as much as possible (Thomas) 4) Switch to TTM based memory manager and prefer cached mapping for Loongson SoC (Thomas) 5) Add chip id detection method, now all models are distinguishable. 6) Revise builtin HDMI phy driver, nearly all main stream mode below 4K@30Hz is tested, this driver supported these mode very well including clone display mode and extend display mode. v3 -> v4: 1) Quickly fix a small mistake. v4 -> v5: 1) Drop potential support for Loongson 2K series SoC temporary, this part should be resend with the DT binding patch in the future. 2) Add per display pipe debugfs support to the builtin HDMI encoder. 3) Rewrite atomic_update() for hardware cursors plane(Thomas) 4) Rewrite encoder and connector initialization part, untangle it according to the chip(Thomas). v5 -> v6: 1) Remove stray code which didn't get used, say lsdc_of_get_reserved_ram 2) Fix all typos I could found, make sentences and code more readable 3) Untangle lsdc_hdmi*_connector_detect() function according to the pipe 4) After a serious consideration, we rename this driver as loongson. Because we also have drivers toward the LoongGPU IP in LS7A2000 and LS2K2000. Besides, there are also drivers about the external encoder, HDMI audio driver and vbios support etc. This patch only provide DC driver part, my teammate Li Yi believe that loongson will be more suitable for loongson graphics than lsdc in the long run. loongson.ko = LSDC + LoongGPU + encoders driver + vbios/DT ... v6 -> v7: 1) Add prime support, self-sharing is works. sharing buffer with etnaviv is also tested, and its works with limitation. 2) Implement buffer objects tracking with list_head. 3) S3(sleep to RAM) is tested on ls3a5000+ls7a2000 evb and it works. 4) Rewrite lsdc_bo_move, since ttm core stop allocating resources during BO creation. Patch V1 ~ V6 of this series no longer works on latest kernel. Thus, we send V7 to revival them. v7 -> v8: 1) Zero a compile warnnings on 32-bit platform, compile with W=1 2) Revise lsdc_bo_gpu_offset() and minor cleanup 3) Pageflip tested on the virtual terminal with following commands modetest -M loongson -s 32:1920x1080 -v modetest -M loongson -s 34:1920x1080 -v -F tiles It works like a charm, when running pageflip test with dual screnn configuration, another two additional bo created by the modetest emerged, VRAM usage up to 40+MB, well we have at least 64MB, still enough. # cat bos bo[0000]: size: 8112kB VRAM bo[0001]: size: 16kB VRAM bo[0002]: size: 16kB VRAM bo[0003]: size: 16208kB VRAM bo[0004]: size: 8112kB VRAM bo[0005]: size: 8112kB VRAM v8 -> v9: 1) Select I2C and I2C_ALGOBIT in Kconfig and should depend on MMU. 2) Using pci_get_domain_bus_and_slot to get the GPU device. 3) Other minor improvements. Those patches are tested on ls3a5000 + ls7a1000 CRB, ls3a5000 + ls7a2000 evb, and lemote a1901 board(ls3a4000 + ls7a1000). On loongson mips CPU, the write combine support should be enabled, to get a decent performance for writing framebuffer data to the VRAM. v9 -> v10: 1) Revise lsdc_drm_freeze() to implement S3 completely and correctly. I suddenly realized that pinned buffer can not move and VRAM lost power when sleep to RAM. Thus, the data in the buffer who is pinned in VRAM will get lost when resume. Yet it's not big problem because we are software rendering solution which relay on the CPU update the front framebuffer. We can see the garbage data when resume from S3, but the screen will show correct image as I move the cursor which is due to the cpu repaint. v10 of this patch make S3 perfect by unpin all of BOs in VRAM, evict them all to system RAM. Signed-off-by: Li Yi Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/loongson/Kconfig | 17 + drivers/gpu/drm/loongson/Makefile | 16 + drivers/gpu/drm/loongson/lsdc_crtc.c | 381 ++++++++++++++++ drivers/gpu/drm/loongson/lsdc_debugfs.c | 261 +++++++++++ drivers/gpu/drm/loongson/lsdc_drv.c | 526 ++++++++++++++++++++++ drivers/gpu/drm/loongson/lsdc_drv.h | 324 ++++++++++++++ drivers/gpu/drm/loongson/lsdc_gem.c | 294 +++++++++++++ drivers/gpu/drm/loongson/lsdc_gem.h | 26 ++ drivers/gpu/drm/loongson/lsdc_i2c.c | 171 +++++++ drivers/gpu/drm/loongson/lsdc_irq.c | 86 ++++ drivers/gpu/drm/loongson/lsdc_irq.h | 12 + drivers/gpu/drm/loongson/lsdc_output.c | 563 ++++++++++++++++++++++++ drivers/gpu/drm/loongson/lsdc_output.h | 14 + drivers/gpu/drm/loongson/lsdc_plane.c | 432 ++++++++++++++++++ drivers/gpu/drm/loongson/lsdc_pll.c | 338 ++++++++++++++ drivers/gpu/drm/loongson/lsdc_pll.h | 76 ++++ drivers/gpu/drm/loongson/lsdc_probe.c | 86 ++++ drivers/gpu/drm/loongson/lsdc_probe.h | 11 + drivers/gpu/drm/loongson/lsdc_regs.h | 370 ++++++++++++++++ drivers/gpu/drm/loongson/lsdc_ttm.c | 439 ++++++++++++++++++ drivers/gpu/drm/loongson/lsdc_ttm.h | 72 +++ 23 files changed, 4518 insertions(+) create mode 100644 drivers/gpu/drm/loongson/Kconfig create mode 100644 drivers/gpu/drm/loongson/Makefile create mode 100644 drivers/gpu/drm/loongson/lsdc_crtc.c create mode 100644 drivers/gpu/drm/loongson/lsdc_debugfs.c create mode 100644 drivers/gpu/drm/loongson/lsdc_drv.c create mode 100644 drivers/gpu/drm/loongson/lsdc_drv.h create mode 100644 drivers/gpu/drm/loongson/lsdc_gem.c create mode 100644 drivers/gpu/drm/loongson/lsdc_gem.h create mode 100644 drivers/gpu/drm/loongson/lsdc_i2c.c create mode 100644 drivers/gpu/drm/loongson/lsdc_irq.c create mode 100644 drivers/gpu/drm/loongson/lsdc_irq.h create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c create mode 100644 drivers/gpu/drm/loongson/lsdc_output.h create mode 100644 drivers/gpu/drm/loongson/lsdc_plane.c create mode 100644 drivers/gpu/drm/loongson/lsdc_pll.c create mode 100644 drivers/gpu/drm/loongson/lsdc_pll.h create mode 100644 drivers/gpu/drm/loongson/lsdc_probe.c create mode 100644 drivers/gpu/drm/loongson/lsdc_probe.h create mode 100644 drivers/gpu/drm/loongson/lsdc_regs.h create mode 100644 drivers/gpu/drm/loongson/lsdc_ttm.c create mode 100644 drivers/gpu/drm/loongson/lsdc_ttm.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ba3fb04bb691..d1fa87d2acb7 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -331,6 +331,8 @@ source "drivers/gpu/drm/v3d/Kconfig" source "drivers/gpu/drm/vc4/Kconfig" +source "drivers/gpu/drm/loongson/Kconfig" + source "drivers/gpu/drm/etnaviv/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a33257d2bc7f..131531453b8e 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -194,3 +194,4 @@ obj-y += gud/ obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ +obj-$(CONFIG_DRM_LOONGSON) += loongson/ diff --git a/drivers/gpu/drm/loongson/Kconfig b/drivers/gpu/drm/loongson/Kconfig new file mode 100644 index 000000000000..df6946d505fa --- /dev/null +++ b/drivers/gpu/drm/loongson/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 + +config DRM_LOONGSON + tristate "DRM support for Loongson Graphics" + depends on DRM && PCI && MMU + select DRM_KMS_HELPER + select DRM_TTM + select I2C + select I2C_ALGOBIT + help + This is a DRM driver for Loongson Graphics, it may including + LS7A2000, LS7A1000, LS2K2000 and LS2K1000 etc. Loongson LS7A + series are bridge chipset, while Loongson LS2K series are SoC. + + If "M" is selected, the module will be called loongson. + + If in doubt, say "N". diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile new file mode 100644 index 000000000000..c5dcc7c9b348 --- /dev/null +++ b/drivers/gpu/drm/loongson/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 + +loongson-y := \ + lsdc_crtc.o \ + lsdc_debugfs.o \ + lsdc_drv.o \ + lsdc_gem.o \ + lsdc_i2c.o \ + lsdc_irq.o \ + lsdc_output.o \ + lsdc_plane.o \ + lsdc_pll.o \ + lsdc_probe.o \ + lsdc_ttm.o + +obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/lsdc_crtc.c b/drivers/gpu/drm/loongson/lsdc_crtc.c new file mode 100644 index 000000000000..48950c476a90 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_crtc.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include + +#include "lsdc_drv.h" + +static u32 lsdc_get_vblank_counter_pipe0(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + + return lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG); +} + +static u32 lsdc_get_vblank_counter_pipe1(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + + return lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG); +} + +static int lsdc_enable_vblank_pipe0(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + + lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); + + return 0; +} + +static void lsdc_disable_vblank_pipe0(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + + lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); +} + +static int lsdc_enable_vblank_pipe1(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + + lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); + + return 0; +} + +static void lsdc_disable_vblank_pipe1(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + + lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); +} + +static void lsdc_crtc_reset(struct drm_crtc *crtc) +{ + struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + struct lsdc_crtc_state *priv_crtc_state; + unsigned int index = dispipe->index; + u32 val; + + val = LSDC_PF_XRGB8888 | CFG_RESET_N; + if (ldev->descp->chip == CHIP_LS7A2000) + val |= LSDC_DMA_STEP_64_BYTES; + + lsdc_crtc_wreg32(ldev, LSDC_CRTC0_CFG_REG, index, val); + + if (ldev->descp->chip == CHIP_LS7A2000) { + val = PHY_CLOCK_EN | PHY_DATA_EN; + lsdc_crtc_wreg32(ldev, LSDC_CRTC0_PANEL_CONF_REG, index, val); + } + + if (crtc->state) { + priv_crtc_state = to_lsdc_crtc_state(crtc->state); + __drm_atomic_helper_crtc_destroy_state(&priv_crtc_state->base); + kfree(priv_crtc_state); + } + + priv_crtc_state = kzalloc(sizeof(*priv_crtc_state), GFP_KERNEL); + if (!priv_crtc_state) + return; + + __drm_atomic_helper_crtc_reset(crtc, &priv_crtc_state->base); +} + +static void lsdc_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); + + __drm_atomic_helper_crtc_destroy_state(&priv_state->base); + + kfree(priv_state); +} + +static struct drm_crtc_state * +lsdc_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct lsdc_crtc_state *new_priv_state; + struct lsdc_crtc_state *old_priv_state; + + new_priv_state = kzalloc(sizeof(*new_priv_state), GFP_KERNEL); + if (!new_priv_state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &new_priv_state->base); + + old_priv_state = to_lsdc_crtc_state(crtc->state); + + memcpy(&new_priv_state->pparms, &old_priv_state->pparms, + sizeof(new_priv_state->pparms)); + + return &new_priv_state->base; +} + +#define LSDC_CRTC_FUNCS(pipe, pfn_get_vblank_counter) \ + .reset = lsdc_crtc_reset, \ + .destroy = drm_crtc_cleanup, \ + .set_config = drm_atomic_helper_set_config, \ + .page_flip = drm_atomic_helper_page_flip, \ + .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state, \ + .atomic_destroy_state = lsdc_crtc_atomic_destroy_state, \ + .get_vblank_counter = pfn_get_vblank_counter, \ + .enable_vblank = lsdc_enable_vblank_##pipe, \ + .disable_vblank = lsdc_disable_vblank_##pipe, \ + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp + +static const struct drm_crtc_funcs lsdc_crtc_funcs_array[2][LSDC_NUM_CRTC] = { + { + { + LSDC_CRTC_FUNCS(pipe0, NULL), + }, + { + LSDC_CRTC_FUNCS(pipe1, NULL), + } + }, + { + { + LSDC_CRTC_FUNCS(pipe0, lsdc_get_vblank_counter_pipe0), + }, + { + LSDC_CRTC_FUNCS(pipe1, lsdc_get_vblank_counter_pipe1), + } + } +}; + +static enum drm_mode_status +lsdc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_desc *descp = ldev->descp; + + if (mode->hdisplay > descp->max_width) + return MODE_BAD_HVALUE; + if (mode->vdisplay > descp->max_height) + return MODE_BAD_VVALUE; + + if (mode->clock > descp->max_pixel_clk) { + drm_dbg(ddev, "mode %dx%d, pixel clock=%d is too high\n", + mode->hdisplay, mode->vdisplay, mode->clock); + return MODE_CLOCK_HIGH; + } + + /* TODO: check RGB565 support, 4 for DRM_FORMAT_XRGB8888 */ + if ((mode->hdisplay * 4) % descp->pitch_align) { + drm_dbg(ddev, "stride align to %u bytes is required\n", + descp->pitch_align); + return MODE_BAD_WIDTH; + } + + return MODE_OK; +} + +static int lsdc_pixpll_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); + struct lsdc_pll *pixpll = &dispipe->pixpll; + const struct lsdc_pixpll_funcs *pfuncs = pixpll->funcs; + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); + struct lsdc_pll_parms *pout = &priv_state->pparms; + unsigned int clock = state->mode.clock; + int ret; + + ret = pfuncs->compute(pixpll, clock, pout); + if (ret) { + drm_warn(crtc->dev, "Find PLL parms for %ukHz failed\n", clock); + return -EINVAL; + } + + return 0; +} + +static int lsdc_crtc_helper_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); + + if (!crtc_state->enable) + return 0; + + return lsdc_pixpll_atomic_check(crtc, crtc_state); +} + +static void lsdc_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_desc *descp = ldev->descp; + struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); + struct lsdc_pll *pixpll = &dispipe->pixpll; + const struct lsdc_pixpll_funcs *clk_func = pixpll->funcs; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(crtc_state); + struct drm_display_mode *mode = &crtc_state->mode; + unsigned int index = dispipe->index; + unsigned int width_in_bytes = mode->crtc_hdisplay * 4; + u32 val; + + lsdc_crtc_wreg32(ldev, LSDC_CRTC0_HDISPLAY_REG, index, + (mode->crtc_htotal << 16) | mode->crtc_hdisplay); + + lsdc_crtc_wreg32(ldev, LSDC_CRTC0_VDISPLAY_REG, index, + (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); + + lsdc_crtc_wreg32(ldev, LSDC_CRTC0_HSYNC_REG, index, + (mode->crtc_hsync_end << 16) | + mode->crtc_hsync_start | + HSYNC_EN); + + lsdc_crtc_wreg32(ldev, LSDC_CRTC0_VSYNC_REG, index, + (mode->crtc_vsync_end << 16) | + mode->crtc_vsync_start | + VSYNC_EN); + + val = lsdc_crtc_rreg32(ldev, LSDC_CRTC0_CFG_REG, index); + /* clear old dma step settings */ + val &= ~CFG_DMA_STEP_MASK; + + if (descp->chip == CHIP_LS7A2000) { + /* + * Using large dma step as much as possible, + * for improve hardware DMA efficiency. + */ + if (width_in_bytes % 256 == 0) + val |= LSDC_DMA_STEP_256_BYTES; + else if (width_in_bytes % 128 == 0) + val |= LSDC_DMA_STEP_128_BYTES; + else if (width_in_bytes % 64 == 0) + val |= LSDC_DMA_STEP_64_BYTES; + else /* width_in_bytes % 32 == 0 */ + val |= LSDC_DMA_STEP_32_BYTES; + } + + clk_func->update(pixpll, &priv_state->pparms); + + lsdc_crtc_wreg32(ldev, LSDC_CRTC0_CFG_REG, index, val | CFG_OUTPUT_EN); + + drm_crtc_vblank_on(crtc); +} + +static void lsdc_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + unsigned int index = dispipe->index; + + lsdc_crtc_ureg32_clr(ldev, LSDC_CRTC0_CFG_REG, index, CFG_OUTPUT_EN); + + drm_crtc_vblank_off(crtc); +} + +static void lsdc_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + else + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static bool lsdc_crtc_get_scanout_position(struct drm_crtc *crtc, + bool in_vblank_irq, + int *vpos, + int *hpos, + ktime_t *stime, + ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + unsigned int index = dispipe->index; + int vsw, vbp, vactive_start, vactive_end, vfp_end, y; + u32 val = 0; + + vsw = mode->crtc_vsync_end - mode->crtc_vsync_start; + vbp = mode->crtc_vtotal - mode->crtc_vsync_end; + + vactive_start = vsw + vbp + 1; + vactive_end = vactive_start + mode->crtc_vdisplay; + + /* last scan line before VSYNC */ + vfp_end = mode->crtc_vtotal; + + if (stime) + *stime = ktime_get(); + + val = lsdc_crtc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG, index); + + y = (val & 0xffff); + + if (y < vactive_start) + y -= vactive_start; + else if (y > vactive_end) + y = y - vfp_end - vactive_start; + else + y -= vactive_start; + + *vpos = y; + *hpos = val >> 16; + + if (etime) + *etime = ktime_get(); + + return true; +} + +static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = { + .mode_valid = lsdc_crtc_mode_valid, + .atomic_enable = lsdc_crtc_atomic_enable, + .atomic_disable = lsdc_crtc_atomic_disable, + .atomic_check = lsdc_crtc_helper_atomic_check, + .atomic_flush = lsdc_crtc_atomic_flush, + .get_scanout_position = lsdc_crtc_get_scanout_position, +}; + +int lsdc_crtc_init(struct drm_device *ddev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + unsigned int index) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_desc *descp = ldev->descp; + unsigned int has = descp->has_vblank_counter; + int ret; + + ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor, + &lsdc_crtc_funcs_array[has][index], + "CRTC-%d", + index); + if (ret) { + drm_err(ddev, "crtc init with planes failed: %d\n", ret); + return ret; + } + + drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs); + + drm_dbg(ddev, "%s initialized %s vblank counter support\n", + crtc->name, has ? "with" : "without"); + + ret = drm_mode_crtc_set_gamma_size(crtc, 256); + if (ret) + return ret; + + drm_crtc_enable_color_mgmt(crtc, 0, false, 256); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_debugfs.c b/drivers/gpu/drm/loongson/lsdc_debugfs.c new file mode 100644 index 000000000000..3eba22e57ee6 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_debugfs.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include + +#include "lsdc_drv.h" +#include "lsdc_gem.h" +#include "lsdc_probe.h" + +#ifdef CONFIG_DEBUG_FS + +static int lsdc_identify(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u8 imp, rev; + + loongson_cpu_get_prid(&imp, &rev); + + seq_printf(m, "I'm in %s, running on cpu 0x%x, cpu revision: 0x%x\n", + chip_to_str(ldev->descp->chip), imp, rev); + + return 0; +} + +static int lsdc_show_clock(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, ddev) { + struct lsdc_display_pipe *pipe = crtc_to_display_pipe(crtc); + struct lsdc_pll *pixpll = &pipe->pixpll; + const struct lsdc_pixpll_funcs *funcs = pixpll->funcs; + struct drm_display_mode *mode = &crtc->state->mode; + struct lsdc_pll_parms parms; + unsigned int out_khz; + + out_khz = funcs->get_clock_rate(pixpll, &parms); + + seq_printf(m, "Display pipe %u: %dx%d\n", + pipe->index, mode->hdisplay, mode->vdisplay); + + seq_printf(m, "Frequency actually output: %u kHz\n", out_khz); + seq_printf(m, "Pixel clock required: %d kHz\n", mode->clock); + seq_printf(m, "diff: %d kHz\n", out_khz - mode->clock); + + seq_printf(m, "div_ref=%u, loopc=%u, div_out=%u\n", + parms.div_ref, parms.loopc, parms.div_out); + + seq_printf(m, "hsync_start=%d, hsync_end=%d, htotal=%d\n", + mode->hsync_start, mode->hsync_end, mode->htotal); + seq_printf(m, "vsync_start=%d, vsync_end=%d, vtotal=%d\n\n", + mode->vsync_start, mode->vsync_end, mode->vtotal); + } + + return 0; +} + +static int lsdc_show_mm(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct drm_printer p = drm_seq_file_printer(m); + + drm_mm_print(&ddev->vma_offset_manager->vm_addr_space_mm, &p); + + return 0; +} + +#define REG_DEF(reg) { __stringify_1(LSDC_##reg##_REG), LSDC_##reg##_REG } +static const struct { + const char *name; + u32 reg_offset; +} lsdc_regs_array[] = { + REG_DEF(CURSOR0_CFG), + REG_DEF(CURSOR0_POSITION), + REG_DEF(CURSOR0_BG_COLOR), + REG_DEF(CURSOR0_FG_COLOR), + REG_DEF(CRTC0_CFG), + REG_DEF(CRTC0_FB_ORIGIN), + REG_DEF(CRTC0_PANEL_CONF), + REG_DEF(CRTC0_HDISPLAY), + REG_DEF(CRTC0_HSYNC), + REG_DEF(CRTC0_VDISPLAY), + REG_DEF(CRTC0_VSYNC), + REG_DEF(CRTC0_GAMMA_INDEX), + REG_DEF(CRTC0_GAMMA_DATA), + REG_DEF(CRTC0_SYNC_DEVIATION), + REG_DEF(INT), + REG_DEF(CRTC1_CFG), + REG_DEF(CRTC1_FB_ORIGIN), + REG_DEF(CRTC1_PANEL_CONF), + REG_DEF(CRTC1_HDISPLAY), + REG_DEF(CRTC1_HSYNC), + REG_DEF(CRTC1_VDISPLAY), + REG_DEF(CRTC1_VSYNC), + REG_DEF(CRTC1_GAMMA_INDEX), + REG_DEF(CRTC1_GAMMA_DATA), + REG_DEF(CRTC1_SYNC_DEVIATION), +}; + +static int lsdc_show_regs(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + int i; + + for (i = 0; i < ARRAY_SIZE(lsdc_regs_array); i++) { + u32 offset = lsdc_regs_array[i].reg_offset; + const char *name = lsdc_regs_array[i].name; + + seq_printf(m, "%s (0x%04x): 0x%08x\n", + name, offset, lsdc_rreg32(ldev, offset)); + } + + return 0; +} + +static int lsdc_show_vblank_counter(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + + seq_printf(m, "CRTC-0 vblank counter: %08u\n", + lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG)); + + seq_printf(m, "CRTC-1 vblank counter: %08u\n", + lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG)); + + return 0; +} + +static int lsdc_show_scan_position(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 p0 = lsdc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG); + u32 p1 = lsdc_rreg32(ldev, LSDC_CRTC1_SCAN_POS_REG); + + seq_printf(m, "CRTC-0: x: %08u, y: %08u\n", p0 >> 16, p0 & 0xFFFF); + seq_printf(m, "CRTC-1: x: %08u, y: %08u\n", p1 >> 16, p1 & 0xFFFF); + + return 0; +} + +static int lsdc_show_primary_addr(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 lo, hi; + u32 val; + + val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + if (val & CFG_FB_IN_USING) { + lo = lsdc_rreg32(ldev, LSDC_CRTC0_FB1_LO_ADDR_REG); + hi = lsdc_rreg32(ldev, LSDC_CRTC0_FB1_HI_ADDR_REG); + seq_printf(m, "CRTC-0 using fb1: 0x%x:%x\n", hi, lo); + } else { + lo = lsdc_rreg32(ldev, LSDC_CRTC0_FB0_LO_ADDR_REG); + hi = lsdc_rreg32(ldev, LSDC_CRTC0_FB0_HI_ADDR_REG); + seq_printf(m, "CRTC-0 using fb0: 0x%x:%x\n", hi, lo); + } + + val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + if (val & CFG_FB_IN_USING) { + lo = lsdc_rreg32(ldev, LSDC_CRTC1_FB1_LO_ADDR_REG); + hi = lsdc_rreg32(ldev, LSDC_CRTC1_FB1_HI_ADDR_REG); + seq_printf(m, "CRTC-1 using fb1: 0x%x:%x\n", hi, lo); + } else { + lo = lsdc_rreg32(ldev, LSDC_CRTC1_FB0_LO_ADDR_REG); + hi = lsdc_rreg32(ldev, LSDC_CRTC1_FB0_HI_ADDR_REG); + seq_printf(m, "CRTC-1 using fb0: 0x%x:%x\n", hi, lo); + } + + return 0; +} + +/* To verify if TTM_PL_FLAG_TOPDOWN works */ +static int lsdc_show_cursor_addr(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 lo, hi; + + lo = lsdc_rreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG); + hi = lsdc_rreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG); + seq_printf(m, "CURSOR0: 0x%x:%x\n", hi, lo); + + lo = lsdc_rreg32(ldev, LSDC_CURSOR1_ADDR_LO_REG); + hi = lsdc_rreg32(ldev, LSDC_CURSOR1_ADDR_HI_REG); + seq_printf(m, "CURSOR1: 0x%x:%x\n", hi, lo); + + return 0; +} + +static int lsdc_show_stride(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 stride0 = lsdc_rreg32(ldev, LSDC_CRTC0_STRIDE_REG); + u32 stride1 = lsdc_rreg32(ldev, LSDC_CRTC1_STRIDE_REG); + + seq_printf(m, "PIPE-0 stride: %u\n", stride0); + seq_printf(m, "PIPE-1 stride: %u\n", stride1); + + return 0; +} + +static int lsdc_trigger_flip_fb(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + u32 val; + + val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val | CFG_PAGE_FLIP); + seq_puts(m, "CRTC-0 flip triggered\n"); + + val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val | CFG_PAGE_FLIP); + seq_puts(m, "CRTC-1 flip triggered\n"); + + return 0; +} + +static struct drm_info_list lsdc_debugfs_list[] = { + { "chip", lsdc_identify, 0, NULL }, + { "clocks", lsdc_show_clock, 0 }, + { "mm", lsdc_show_mm, 0, NULL }, + { "regs", lsdc_show_regs, 0 }, + { "vblanks", lsdc_show_vblank_counter, 0, NULL }, + { "scan_pos", lsdc_show_scan_position, 0, NULL }, + { "fb_addr", lsdc_show_primary_addr, 0, NULL }, + { "cursor_addr", lsdc_show_cursor_addr, 0, NULL }, + { "flip", lsdc_trigger_flip_fb, 0, NULL }, + { "stride", lsdc_show_stride, 0, NULL }, + { "bos", lsdc_show_buffer_object, 0, NULL }, +}; + +#endif + +void lsdc_debugfs_init(struct drm_minor *minor) +{ +#ifdef CONFIG_DEBUG_FS + drm_debugfs_create_files(lsdc_debugfs_list, + ARRAY_SIZE(lsdc_debugfs_list), + minor->debugfs_root, + minor); +#endif +} diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c new file mode 100644 index 000000000000..1225ea8ed44d --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include + +#include