Received: by 2002:a05:6358:3188:b0:123:57c1:9b43 with SMTP id q8csp1767383rwd; Sun, 21 May 2023 06:02:46 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5ncfzdhMEjwnYS431fo4df6gDWc8xPej520EcMmpWE7etSn6AhR+Nv6scf6t/dgXPRzkQS X-Received: by 2002:a17:902:db85:b0:1a1:9020:f9c7 with SMTP id m5-20020a170902db8500b001a19020f9c7mr8176305pld.44.1684674165789; Sun, 21 May 2023 06:02:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684674165; cv=none; d=google.com; s=arc-20160816; b=nf82OdL10Xq2qraOJaTyVfKhf5dM0nxAelvePuq62A56X+6obihSD/D4cPt198FZIp stGnljm5g+W12HkUW9WfcSpTfDZEJSrEY8vGYnLCYO3FQ6yfWMcczUJP+BfYWnLN7Dat s8Yl5m0S8dws7KvaZ0tIq0Ldq4987LvEiHLIEULBGcOa+zw82SbH0MGNUSCc1TlJQLOT hf+tOjxbexjbrXvZWae1gR4NmzxYo44mViE0Rzkt1KkFm08RpV5LtHxeNn/XMwQnnaEV p1I6GIR2lxmfBl2lRbxLzXOwTjKDSJTGbIFtt+fhJz66nGiqqDISwBMVQdLURU/LU7tk Vmag== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:in-reply-to:from :references:cc:to:content-language:subject:user-agent:mime-version :date:message-id:dkim-signature; bh=P1EZXuC84p66hm2zGZuxnUvOiwA6QXXLLwp95/e8Cew=; b=GJFDKR5r1nwWVWgEdXL8CstMsfRbZNYIeVHLfxLFOtuvHbdt+Hj6DcN/tMfTEk1BG1 ApacjrOTr89zSIYJodT/FkXq3VSK7Z0Ri4FSaKZMluExV0aWrgt2js8nBjJxpnF1fr1B 5nkwYPdG7s1exA0F+VxGxVVYcGwSGrNeikLaz9fcuDUGMS6/v5dyKowUlopu9Fd2gwNH HpJ8M0HMEowYo7GYKwTlzZ8nQ5M/2kwGSRi8G7I7lAx2T3yIU2vsJpEYAzD4gHcTWQwG rpg7gQGqOzaoEwZOjioCR+c4duSsNtcwLOCVXkIMb4QTGo0hR7YUJsZ8ihnAfsaJ0igG qh0Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@xen0n.name header.s=mail header.b="c+sXQ8/D"; 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 Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id e19-20020a170902ed9300b0019ca5ddecfesi3095821plj.92.2023.05.21.06.02.31; Sun, 21 May 2023 06:02:45 -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; dkim=pass header.i=@xen0n.name header.s=mail header.b="c+sXQ8/D"; 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 S230085AbjEUMVi (ORCPT + 99 others); Sun, 21 May 2023 08:21:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57204 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229545AbjEUMVf (ORCPT ); Sun, 21 May 2023 08:21:35 -0400 Received: from mailbox.box.xen0n.name (mail.xen0n.name [115.28.160.31]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 98F3CAA; Sun, 21 May 2023 05:21:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=xen0n.name; s=mail; t=1684671687; bh=wnLQOa83cK+7cBAuEK2kTVKuSOC8Fg96UzEsUC0zFUw=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=c+sXQ8/DyMHTYNsdsfx7nUMO8D/YYBSqmuwH3K+GxRPQtPsLJUaTD9kac2VCBOLvs 1J8u45yxqWei1MCVJpgGS4RZPRQLfEyL2NC8DKlpnKWmgTZYGt1WbdihL1IijVZH18 j+Yt8M6WLW3K1eBpKfiAOaOPirqDndwUWYBoC52Q= Received: from [100.100.57.122] (unknown [58.34.185.106]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mailbox.box.xen0n.name (Postfix) with ESMTPSA id 65DDC60115; Sun, 21 May 2023 20:21:27 +0800 (CST) Message-ID: <26fd78b9-c074-8341-c99c-4e3b38cd861a@xen0n.name> Date: Sun, 21 May 2023 20:21:27 +0800 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Thunderbird/102.11.0 Subject: Re: [PATCH v14 1/2] drm: add kms driver for loongson display controller Content-Language: en-US To: Sui Jingfeng <15330273260@189.cn>, Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Sui Jingfeng , Li Yi , Sumit Semwal , Christian Koenig , Emil Velikov Cc: linaro-mm-sig@lists.linaro.org, loongson-kernel@lists.loongnix.cn, Geert Uytterhoeven , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Javier Martinez Canillas , Nathan Chancellor , Liu Peibao , linux-media@vger.kernel.org References: <20230520105718.325819-1-15330273260@189.cn> <20230520105718.325819-2-15330273260@189.cn> From: WANG Xuerui In-Reply-To: <20230520105718.325819-2-15330273260@189.cn> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.2 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,NICE_REPLY_A,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham 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 Hi, Someone else in a discussion group brought my attention to this series, that I've neglected for a long time because loongarch@lists.linux.dev isn't on the Cc list and I'm not subscribed to dri-devel. While I'm reasonably familiar with LoongArch internals and Linux in general, I don't regularly tinker with the graphics things, so I'm mainly focusing on the natural language usage and general code smells for my reviews below. Pardon me if some of the questions seem silly. (After going through the entirety of this: *please* spell-check your comment blocks, and correct obvious grammatical nits as best as you can. From my first impression, although a reader not familiar with LoongArch nor Chinese could go a long way in understanding this, some of the rest would be misunderstood, or don't make sense at all. And like 90% of the sentences are grammatically incorrect, i.e. are obvious "Chinglish". Maybe something like those ChatGPT-based services or someone in your company would help.) Also, publicly available documentation and references to them would be hugely beneficial. For now we have to trust your description here (that's very pictorial and pretty complete with explanations, by the way, and thanks for that), but it could be even better to provide more citations like "According to Section xx.xx of Loongson 7A1000 User Manual v1.0. I don't know how people usually handle the "pre-release hardware without docs yet" situation in drm side, though; we should adhere to the conventions if my understanding is not aligning with it. And finally, thanks for your patience all along. On 2023/5/20 18:57, Sui Jingfeng wrote: > From: Sui Jingfeng > > Loongson display controller IP has been integrated in both Loongson north > bridge chipset(ls7a1000/ls7a2000) and Loongson SoCs(ls2k1000/ls2k2000), it > has been even included in Loongson self-made BMC products. > > This display controller is a PCI device. It has two display pipes and each > display pipe support a primary plane and a cursor plane. For the DC in the "supports" > ls7a1000 and ls2k1000, each display pipe has a DVO output interface which > provide RGB888 signals, vertical & horizontal synchronisations and pixel "synchronisation" > clock. Each CRTC is able to support 1920x1080@60Hz, the maximum resolution "is capable of" sounds more natural? > of each display pipe 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 "supporting up to 3840x2160@30Hz" > 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 "The first display pipe additionally has a transparent VGA encoder"? > 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 scanout the tiled framebuffer rendered by the LoongGPU directly. "The DC in LS7A2000 is more feature-complete compared with the older revision: in addition to the above, it also has two hardware cursors, two hardware vblank counters and two scanout position recorders. It also supports tiled framebuffer format so the tiled output from the LoongGPU can be scanned out 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. This 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. > > v10 -> v11: > 1) On double screen case, the single giant framebuffer is referenced by > two GEM object, hence, it will be pinned by prepare_fb() at lease two > times. This cause its pin count > 1. V10 of this patch only unpin VRAM > BOs once when suspend, which is not correct on double screen case. V11 > of this patch unpin BOs until its pin count reach to zero when suspend. > Then, we make the S3 support complete finally. With v11, I can't see > any garbage data after resume. Tested on both ls7a1000 and ls7a2000 > platform, with single screen and double screen configuration. > 2) Fix vblank wait timeout when disable CRTC. > 3) Test against IGT, at least fbdev test and kms_flip test passed. > 4) Rewrite pixel PLL update function, magic numbers eliminated (Emil) > 5) Drop a few common hardware features description in lsdc_desc (Emil) > 6) Drop lsdc_mode_config_mode_valid(), instead add restrictions in dumb > create function. (Emil) > 7) Untangle the ls7a1000 case and ls7a2000 case completely (Thomas) > > v11 -> v12: > none > > v12 -> v13: > 1) Add benchmark to figure out the bandwidth of the hardware platform. > Usage: > # cd /sys/kernel/debug/dri/0/ > # cat benchmark > > 2) VRAM is filled with garbage data if uninitialized, add a buffer > clearing procedure, clear it on the BO creation time. > 3) Update copyrights and adjust coding style (Huacai) > > v13 -> v14: > 1) Trying to add async update support for cursor plane. > > Signed-off-by: Li Yi > Signed-off-by: Sui Jingfeng > Tested-by: Liu Peibao > Cc: Maarten Lankhorst > Cc: Maxime Ripard > Cc: Thomas Zimmermann > Cc: David Airlie > Cc: Daniel Vetter > Cc: Sumit Semwal > Cc: "Christian König" > Cc: Nathan Chancellor > Cc: Emil Velikov > Cc: Geert Uytterhoeven > Cc: Javier Martinez Canillas > --- > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/loongson/Kconfig | 17 + > drivers/gpu/drm/loongson/Makefile | 20 + > drivers/gpu/drm/loongson/lsdc_benchmark.c | 133 ++ > drivers/gpu/drm/loongson/lsdc_benchmark.h | 13 + > drivers/gpu/drm/loongson/lsdc_crtc.c | 1066 +++++++++++++++++ > drivers/gpu/drm/loongson/lsdc_debugfs.c | 91 ++ > drivers/gpu/drm/loongson/lsdc_device.c | 104 ++ > drivers/gpu/drm/loongson/lsdc_drv.c | 495 ++++++++ > drivers/gpu/drm/loongson/lsdc_drv.h | 451 +++++++ > drivers/gpu/drm/loongson/lsdc_gem.c | 324 +++++ > drivers/gpu/drm/loongson/lsdc_gem.h | 37 + > drivers/gpu/drm/loongson/lsdc_gfxpll.c | 199 +++ > drivers/gpu/drm/loongson/lsdc_gfxpll.h | 52 + > drivers/gpu/drm/loongson/lsdc_i2c.c | 179 +++ > drivers/gpu/drm/loongson/lsdc_i2c.h | 29 + > drivers/gpu/drm/loongson/lsdc_irq.c | 71 ++ > drivers/gpu/drm/loongson/lsdc_irq.h | 16 + > drivers/gpu/drm/loongson/lsdc_output.h | 21 + > drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 161 +++ > drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 531 ++++++++ > drivers/gpu/drm/loongson/lsdc_pixpll.c | 481 ++++++++ > drivers/gpu/drm/loongson/lsdc_pixpll.h | 86 ++ > drivers/gpu/drm/loongson/lsdc_plane.c | 781 ++++++++++++ > drivers/gpu/drm/loongson/lsdc_probe.c | 56 + > drivers/gpu/drm/loongson/lsdc_probe.h | 12 + > drivers/gpu/drm/loongson/lsdc_regs.h | 402 +++++++ > drivers/gpu/drm/loongson/lsdc_ttm.c | 610 ++++++++++ > drivers/gpu/drm/loongson/lsdc_ttm.h | 99 ++ > 30 files changed, 6540 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_benchmark.c > create mode 100644 drivers/gpu/drm/loongson/lsdc_benchmark.h > 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_device.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_gfxpll.c > create mode 100644 drivers/gpu/drm/loongson/lsdc_gfxpll.h > create mode 100644 drivers/gpu/drm/loongson/lsdc_i2c.c > create mode 100644 drivers/gpu/drm/loongson/lsdc_i2c.h > 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.h > create mode 100644 drivers/gpu/drm/loongson/lsdc_output_7a1000.c > create mode 100644 drivers/gpu/drm/loongson/lsdc_output_7a2000.c > create mode 100644 drivers/gpu/drm/loongson/lsdc_pixpll.c > create mode 100644 drivers/gpu/drm/loongson/lsdc_pixpll.h > create mode 100644 drivers/gpu/drm/loongson/lsdc_plane.c > 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 Drop "it may"; "including" should be enough. > + 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. Just "loongson"? I know it's like this for ages (at least dating back to the MIPS days) but you really don't want to imply Loongson is mainly a GPU company. Something like "loongson_drm" or "lsdc" or "gsgpu" could be better. > + > + 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..9158816ece8e > --- /dev/null > +++ b/drivers/gpu/drm/loongson/Makefile > @@ -0,0 +1,20 @@ > +# SPDX-License-Identifier: GPL-2.0 > + > +loongson-y := \ > + lsdc_benchmark.o \ > + lsdc_crtc.o \ > + lsdc_debugfs.o \ > + lsdc_device.o \ > + lsdc_drv.o \ > + lsdc_gem.o \ > + lsdc_gfxpll.o \ > + lsdc_i2c.o \ > + lsdc_irq.o \ > + lsdc_output_7a1000.o \ > + lsdc_output_7a2000.o \ > + lsdc_plane.o \ > + lsdc_pixpll.o \ > + lsdc_probe.o \ > + lsdc_ttm.o > + > +obj-$(CONFIG_DRM_LOONGSON) += loongson.o > diff --git a/drivers/gpu/drm/loongson/lsdc_benchmark.c b/drivers/gpu/drm/loongson/lsdc_benchmark.c > new file mode 100644 > index 000000000000..82961531d84c > --- /dev/null > +++ b/drivers/gpu/drm/loongson/lsdc_benchmark.c > @@ -0,0 +1,133 @@ > +// SPDX-License-Identifier: GPL-2.0+ Is it GPL-2.0, GPL-2.0-only, or GPL-2.0+? Please make sure all license IDs are consistent. > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include > + > +#include "lsdc_benchmark.h" > +#include "lsdc_drv.h" > +#include "lsdc_gem.h" > +#include "lsdc_ttm.h" > + > +typedef void (*lsdc_copy_proc_t)(struct lsdc_bo *src_bo, > + struct lsdc_bo *dst_bo, > + unsigned int size, > + int n); > + > +static void lsdc_copy_gtt_to_vram_cpu(struct lsdc_bo *src_bo, > + struct lsdc_bo *dst_bo, > + unsigned int size, > + int n) > +{ > + lsdc_bo_kmap(src_bo); > + lsdc_bo_kmap(dst_bo); > + > + while (n--) > + memcpy_toio(dst_bo->kptr, src_bo->kptr, size); > + > + lsdc_bo_kunmap(src_bo); > + lsdc_bo_kunmap(dst_bo); > +} > + > +static void lsdc_copy_vram_to_gtt_cpu(struct lsdc_bo *src_bo, > + struct lsdc_bo *dst_bo, > + unsigned int size, > + int n) > +{ > + lsdc_bo_kmap(src_bo); > + lsdc_bo_kmap(dst_bo); > + > + while (n--) > + memcpy_fromio(dst_bo->kptr, src_bo->kptr, size); > + > + lsdc_bo_kunmap(src_bo); > + lsdc_bo_kunmap(dst_bo); > +} > + > +static void lsdc_copy_gtt_to_gtt_cpu(struct lsdc_bo *src_bo, > + struct lsdc_bo *dst_bo, > + unsigned int size, > + int n) > +{ > + lsdc_bo_kmap(src_bo); > + lsdc_bo_kmap(dst_bo); > + > + while (n--) > + memcpy(dst_bo->kptr, src_bo->kptr, size); > + > + lsdc_bo_kunmap(src_bo); > + lsdc_bo_kunmap(dst_bo); > +} > + > +static void lsdc_benchmark_copy(struct lsdc_device *ldev, > + unsigned int size, > + unsigned int n, > + u32 src_domain, > + u32 dst_domain, > + lsdc_copy_proc_t copy_proc, > + struct drm_printer *p) > +{ > + struct drm_device *ddev = &ldev->base; > + struct lsdc_bo *src_bo; > + struct lsdc_bo *dst_bo; > + unsigned long start_jiffies; > + unsigned long end_jiffies; > + unsigned int throughput; > + unsigned int time; > + > + src_bo = lsdc_bo_create_kernel_pinned(ddev, src_domain, size); > + dst_bo = lsdc_bo_create_kernel_pinned(ddev, dst_domain, size); > + > + start_jiffies = jiffies; > + > + copy_proc(src_bo, dst_bo, size, n); > + > + end_jiffies = jiffies; > + > + lsdc_bo_free_kernel_pinned(src_bo); > + lsdc_bo_free_kernel_pinned(dst_bo); > + > + time = jiffies_to_msecs(end_jiffies - start_jiffies); > + > + throughput = (n * (size >> 10)) / time; > + > + drm_printf(p, > + "Copy bo of %ukB %u times from %s to %s in %ums: %uMB/s\n", > + size >> 10, n, > + lsdc_domain_to_str(src_domain), > + lsdc_domain_to_str(dst_domain), > + time, throughput); > +} > + > +int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p) > +{ > + unsigned int buffer_size = 1920 * 1080 * 4; > + unsigned int iteration = 60; > + > + lsdc_benchmark_copy(ldev, > + buffer_size, > + iteration, > + LSDC_GEM_DOMAIN_GTT, > + LSDC_GEM_DOMAIN_GTT, > + lsdc_copy_gtt_to_gtt_cpu, > + p); > + > + lsdc_benchmark_copy(ldev, > + buffer_size, > + iteration, > + LSDC_GEM_DOMAIN_GTT, > + LSDC_GEM_DOMAIN_VRAM, > + lsdc_copy_gtt_to_vram_cpu, > + p); > + > + lsdc_benchmark_copy(ldev, > + buffer_size, > + iteration, > + LSDC_GEM_DOMAIN_VRAM, > + LSDC_GEM_DOMAIN_GTT, > + lsdc_copy_vram_to_gtt_cpu, > + p); > + > + return 0; > +} > diff --git a/drivers/gpu/drm/loongson/lsdc_benchmark.h b/drivers/gpu/drm/loongson/lsdc_benchmark.h > new file mode 100644 > index 000000000000..2bf9406eae9c > --- /dev/null > +++ b/drivers/gpu/drm/loongson/lsdc_benchmark.h > @@ -0,0 +1,13 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#ifndef __LSDC_BENCHMARK_H__ > +#define __LSDC_BENCHMARK_H__ > + > +#include "lsdc_drv.h" > + > +int lsdc_show_benchmark_copy(struct lsdc_device *ldev, struct drm_printer *p); > + > +#endif > diff --git a/drivers/gpu/drm/loongson/lsdc_crtc.c b/drivers/gpu/drm/loongson/lsdc_crtc.c > new file mode 100644 > index 000000000000..de2c1d514baa > --- /dev/null > +++ b/drivers/gpu/drm/loongson/lsdc_crtc.c > @@ -0,0 +1,1066 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include > + > +#include > +#include > +#include > +#include > + > +#include "lsdc_drv.h" > + > +/* > + * The soft reset cause the vblank counter reset to zero, but the address > + * and other settings in the crtc register remains. > + */ > + > +static void lsdc_crtc0_soft_reset(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); > + > + val &= CFG_VALID_BITS_MASK; > + > + /* soft reset bit, active low */ > + val &= ~CFG_RESET_N; > + > + val &= ~CFG_PIX_FMT_MASK; > + > + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); > + > + udelay(5); > + > + val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE; > + > + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); > + > + mdelay(20); > +} > + > +static void lsdc_crtc1_soft_reset(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); > + > + val &= CFG_VALID_BITS_MASK; > + > + /* soft reset bit, active low */ > + val &= ~CFG_RESET_N; > + > + val &= ~CFG_PIX_FMT_MASK; > + > + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); > + > + udelay(5); > + > + val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE; > + > + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); > + > + msleep(20); So many magic sleeps without documentation? > +} > + > +static void lsdc_crtc0_enable(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); > + > + /* > + * This may happens on extremely rare case, luckily, a soft reset "may happen on extremely rare cases;" > + * can helps to bring it back to normal. We add a warn here, hope "can help bringing it back to normal. We issue a warning here, hoping to" > + * to catch something if it happens. > + */ > + > + if (val & CRTC_ANCHORED) { > + drm_warn(&ldev->base, "%s anchored\n", lcrtc->base.name); > + return lsdc_crtc0_soft_reset(lcrtc); > + } > + > + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val | CFG_OUTPUT_ENABLE); > +} > + > +static void lsdc_crtc0_disable(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_clr(ldev, LSDC_CRTC0_CFG_REG, CFG_OUTPUT_ENABLE); > + > + udelay(9); > +} > + > +static void lsdc_crtc1_enable(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); > + if (val & CRTC_ANCHORED) { > + drm_warn(&ldev->base, "%s anchored\n", lcrtc->base.name); > + return lsdc_crtc1_soft_reset(lcrtc); > + } Duplication of code? You may want to duplicate the comment here too as de-duplication with macro seems too heavy here. > + > + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val | CFG_OUTPUT_ENABLE); > +} > + > +static void lsdc_crtc1_disable(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_clr(ldev, LSDC_CRTC1_CFG_REG, CFG_OUTPUT_ENABLE); > + > + udelay(9); > +} > + > +/* All loongson display controller support scanout position hardware */ Commit message implies only 7A2000+ LSDC IPs have the "scanout position recorders". Either that part or this code would need tweaking... > + > +static void lsdc_crtc0_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG); > + > + *hpos = val >> 16; > + *vpos = val & 0xffff; > +} > + > +static void lsdc_crtc1_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC1_SCAN_POS_REG); > + > + *hpos = val >> 16; > + *vpos = val & 0xffff; > +} > + > +static void lsdc_crtc0_enable_vblank(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); > +} > + > +static void lsdc_crtc0_disable_vblank(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); > +} > + > +static void lsdc_crtc1_enable_vblank(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); > +} > + > +static void lsdc_crtc1_disable_vblank(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); > +} > + > +static void lsdc_crtc0_flip(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_PAGE_FLIP); > +} > + > +static void lsdc_crtc1_flip(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_PAGE_FLIP); > +} > + > +/* > + * CRTC0 clone from CRTC1 or CRTC1 clone from CRTC0 using hardware logic. "Hardware-assisted cloning between CRTCs"? > + * Hardware engineer say this would help to saving bandwidth on clone mode. "Hardware engineers say this would help save bandwidth in clone mode" > + * > + * This may useful on custom clone application. "This may be useful for custom applications of clone mode"? > + */ > + > +static void lsdc_crtc0_clone(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_HW_CLONE); > +} > + > +static void lsdc_crtc1_clone(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_HW_CLONE); > +} > + > +static void lsdc_crtc0_set_mode(struct lsdc_crtc *lcrtc, > + const struct drm_display_mode *mode) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_wreg32(ldev, LSDC_CRTC0_HDISPLAY_REG, > + (mode->crtc_htotal << 16) | mode->crtc_hdisplay); > + > + lsdc_wreg32(ldev, LSDC_CRTC0_VDISPLAY_REG, > + (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); > + > + lsdc_wreg32(ldev, LSDC_CRTC0_HSYNC_REG, > + (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN); > + > + lsdc_wreg32(ldev, LSDC_CRTC0_VSYNC_REG, > + (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN); > +} > + > +static void lsdc_crtc1_set_mode(struct lsdc_crtc *lcrtc, > + const struct drm_display_mode *mode) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + lsdc_wreg32(ldev, LSDC_CRTC1_HDISPLAY_REG, > + (mode->crtc_htotal << 16) | mode->crtc_hdisplay); > + > + lsdc_wreg32(ldev, LSDC_CRTC1_VDISPLAY_REG, > + (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); > + > + lsdc_wreg32(ldev, LSDC_CRTC1_HSYNC_REG, > + (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN); > + > + lsdc_wreg32(ldev, LSDC_CRTC1_VSYNC_REG, > + (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN); > +} > + > +/* > + * This is required for S3 support. > + * > + * After resume from suspend, LSDC_CRTCx_CFG_REG (x=0 or 1)is filled with "resuming", "are filled" > + * garbarge value which may cause the CRTC completely hang. This function "to completely hang" > + * give a minimal setting to the affected registers. This also override "minimally resets the affected registers" > + * the firmware's setting on startup, eliminate potential blinding setting. "This also overrides the boot-time setting by the firmware, eliminating potentially blinding settings"? > + * > + * Making the CRTC works on our own now, this is similar with the functional What do you mean by "work on our own"? Do you mean re-initializing the CRTC without firmware help? > + * of GPU POST(Power On Self Test). Only touch CRTC hardware related part. > + */ > + > +static void lsdc_crtc0_reset(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + struct drm_crtc *crtc = &lcrtc->base; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); > + > + /* This help to see what is it */ > + drm_dbg(&ldev->base, "value of %s configure register: %x\n", "config register" should be enough? > + crtc->name, val); > + > + /* > + * Help the CRTC get out of soft reset sate, as the CRTC is completely > + * halt on soft reset mode (BIT(20) = 0). It does not event generate > + * vblank, cause vblank wait timeout. This happends when resume from > + * S3. > + * > + * Also give a sane format, after resume from suspend S3, this > + * register is filled with garbarge value. A meaningless value may > + * also cause the CRTC halt or stall. > + */ This block of comment is mostly a duplication of the function's doc comment above; I'd suggest removing this block. > + > + val = CFG_RESET_N | LSDC_PF_XRGB8888; > + > + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); > +} > + > +static void lsdc_crtc1_reset(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + struct drm_crtc *crtc = &lcrtc->base; > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); > + > + drm_dbg(&ldev->base, "value of %s configue register: %x\n", "config" > + crtc->name, val); > + > + /* > + * Help the CRTC get out of soft reset sate, give a sane format, > + * Otherwise it will halt or stall there. > + */ > + val = CFG_RESET_N | LSDC_PF_XRGB8888; > + > + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); > +} > + > +static const struct lsdc_crtc_hw_ops ls7a1000_crtc_hw_ops[2] = { > + { > + .enable = lsdc_crtc0_enable, > + .disable = lsdc_crtc0_disable, > + .enable_vblank = lsdc_crtc0_enable_vblank, > + .disable_vblank = lsdc_crtc0_disable_vblank, > + .flip = lsdc_crtc0_flip, > + .clone = lsdc_crtc0_clone, > + .set_mode = lsdc_crtc0_set_mode, > + .get_scan_pos = lsdc_crtc0_scan_pos, > + .soft_reset = lsdc_crtc0_soft_reset, > + .reset = lsdc_crtc0_reset, > + }, > + { > + .enable = lsdc_crtc1_enable, > + .disable = lsdc_crtc1_disable, > + .enable_vblank = lsdc_crtc1_enable_vblank, > + .disable_vblank = lsdc_crtc1_disable_vblank, > + .flip = lsdc_crtc1_flip, > + .clone = lsdc_crtc1_clone, > + .set_mode = lsdc_crtc1_set_mode, > + .get_scan_pos = lsdc_crtc1_scan_pos, > + .soft_reset = lsdc_crtc1_soft_reset, > + .reset = lsdc_crtc1_reset, > + }, > +}; > + > +/* > + * The 32-bit hardware vblank counter is available since ls7a2000/ls2k2000, > + * The counter grow up even the CRTC is being disabled, it will got reset "The counters increase even when the CRTC is disabled, and will be reset if the crtc is soft-reset" I don't think you're describing the transient behavior when the CRTC is *being* disabled / soft-reset, i.e. *in the middle of* disabling/soft-resetting, hence I've fixed the tenses for you. Also please change all mentions of lowercase "ls7a2000" or "ls2k2000" in comments to consistent uppercase equivalents; it's easier for human eyes that way. > + * if the crtc is being soft reset. > + * > + * Those registers are also readable for ls7a1000, but its value does not > + * change. "but the values cannot be changed" > + */ > + > +static u32 lsdc_crtc0_get_vblank_count(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + return lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG); > +} > + > +static u32 lsdc_crtc1_get_vblank_count(struct lsdc_crtc *lcrtc) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + > + return lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG); > +} > + > +/* > + * The DMA step bit field is available since ls7a2000/ls2k2000, for support > + * odd resolutions. But a large DMA step may save bandwidth. Behavior of "for supporting"; "a larger" > + * writing thoes bits field on ls7a1000/ls2k1000 is underfined. "those bitfields"; "undefined" > + */ > + > +static void lsdc_crtc0_set_dma_step(struct lsdc_crtc *lcrtc, > + enum lsdc_dma_steps dma_step) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); > + > + val &= ~CFG_DMA_STEP_MASK; > + val |= dma_step << CFG_DMA_STEP_SHIFT; > + > + lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); > +} > + > +static void lsdc_crtc1_set_dma_step(struct lsdc_crtc *lcrtc, > + enum lsdc_dma_steps dma_step) > +{ > + struct lsdc_device *ldev = lcrtc->ldev; > + u32 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); > + > + val &= ~CFG_DMA_STEP_MASK; > + val |= dma_step << CFG_DMA_STEP_SHIFT; > + > + lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); > +} > + > +static const struct lsdc_crtc_hw_ops ls7a2000_crtc_hw_ops[2] = { > + { > + .enable = lsdc_crtc0_enable, > + .disable = lsdc_crtc0_disable, > + .enable_vblank = lsdc_crtc0_enable_vblank, > + .disable_vblank = lsdc_crtc0_disable_vblank, > + .flip = lsdc_crtc0_flip, > + .clone = lsdc_crtc0_clone, > + .set_mode = lsdc_crtc0_set_mode, > + .soft_reset = lsdc_crtc0_soft_reset, > + .get_scan_pos = lsdc_crtc0_scan_pos, > + .set_dma_step = lsdc_crtc0_set_dma_step, > + .get_vblank_counter = lsdc_crtc0_get_vblank_count, > + .reset = lsdc_crtc0_reset, > + }, > + { > + .enable = lsdc_crtc1_enable, > + .disable = lsdc_crtc1_disable, > + .enable_vblank = lsdc_crtc1_enable_vblank, > + .disable_vblank = lsdc_crtc1_disable_vblank, > + .flip = lsdc_crtc1_flip, > + .clone = lsdc_crtc1_clone, > + .set_mode = lsdc_crtc1_set_mode, > + .get_scan_pos = lsdc_crtc1_scan_pos, > + .soft_reset = lsdc_crtc1_soft_reset, > + .set_dma_step = lsdc_crtc1_set_dma_step, > + .get_vblank_counter = lsdc_crtc1_get_vblank_count, > + .reset = lsdc_crtc1_reset, > + }, > +}; > + > +static void lsdc_crtc_reset(struct drm_crtc *crtc) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + struct lsdc_crtc_state *priv_crtc_state; > + > + if (crtc->state) > + crtc->funcs->atomic_destroy_state(crtc, crtc->state); > + > + priv_crtc_state = kzalloc(sizeof(*priv_crtc_state), GFP_KERNEL); > + > + if (!priv_crtc_state) > + __drm_atomic_helper_crtc_reset(crtc, NULL); > + else > + __drm_atomic_helper_crtc_reset(crtc, &priv_crtc_state->base); > + > + /* > + * Reset the crtc hardware, this is need for S3 support, > + * otherwise, wait for vblank timeout may happen. "Reset the crtc hardware which is needed for S3, otherwise vblank timeout may happen"? > + */ > + ops->reset(lcrtc); > +} > + > +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; > +} > + > +static u32 lsdc_crtc_get_vblank_counter(struct drm_crtc *crtc) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + > + return ops->get_vblank_counter(lcrtc); > +} > + > +static int lsdc_crtc_enable_vblank(struct drm_crtc *crtc) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + > + ops->enable_vblank(lcrtc); > + > + return 0; > +} > + > +static void lsdc_crtc_disable_vblank(struct drm_crtc *crtc) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + > + ops->disable_vblank(lcrtc); > +} > + > +/* > + * CRTC related debugfs > + * > + * Primary planes and cursor planes are also belong to the CRTC for our case, "also belong to the CRTC in our cases" > + * so also append other registers to here, for sake of convenient. "so the other registers are also appended here for convenience" > + */ > + > +#define REG_DEF(reg) { \ > + .name = __stringify_1(LSDC_##reg##_REG), \ > + .offset = LSDC_##reg##_REG, \ > +} > + > +static const struct lsdc_reg32 lsdc_crtc_regs_array[2][21] = { > + [0] = { > + 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(CRTC0_VSYNC_COUNTER), > + REG_DEF(CRTC0_SCAN_POS), > + REG_DEF(CRTC0_STRIDE), > + REG_DEF(CRTC0_FB1_ADDR_HI), > + REG_DEF(CRTC0_FB1_ADDR_LO), > + REG_DEF(CRTC0_FB0_ADDR_HI), > + REG_DEF(CRTC0_FB0_ADDR_LO), > + REG_DEF(CURSOR0_CFG), > + REG_DEF(CURSOR0_POSITION), > + REG_DEF(CURSOR0_BG_COLOR), > + REG_DEF(CURSOR0_FG_COLOR), > + }, > + [1] = { > + 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), > + REG_DEF(CRTC1_VSYNC_COUNTER), > + REG_DEF(CRTC1_SCAN_POS), > + REG_DEF(CRTC1_STRIDE), > + REG_DEF(CRTC1_FB1_ADDR_HI), > + REG_DEF(CRTC1_FB1_ADDR_LO), > + REG_DEF(CRTC1_FB0_ADDR_HI), > + REG_DEF(CRTC1_FB0_ADDR_LO), > + REG_DEF(CURSOR1_CFG), > + REG_DEF(CURSOR1_POSITION), > + REG_DEF(CURSOR1_BG_COLOR), > + REG_DEF(CURSOR1_FG_COLOR), > + }, > +}; > + > +static int lsdc_crtc_show_regs(struct seq_file *m, void *arg) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; > + struct lsdc_device *ldev = lcrtc->ldev; > + unsigned int i; > + > + for (i = 0; i < lcrtc->nreg; i++) { > + const struct lsdc_reg32 *preg = &lcrtc->preg[i]; > + u32 offset = preg->offset; > + > + seq_printf(m, "%s (0x%04x): 0x%08x\n", > + preg->name, offset, lsdc_rreg32(ldev, offset)); > + } > + > + return 0; > +} > + > +static int lsdc_crtc_show_scan_position(struct seq_file *m, void *arg) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + int x, y; > + > + ops->get_scan_pos(lcrtc, &x, &y); > + > + seq_printf(m, "scanout position: x: %08u, y: %08u\n", x, y); > + > + return 0; > +} > + > +static int lsdc_crtc_show_vblank_counter(struct seq_file *m, void *arg) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + > + if (ops->get_vblank_counter) > + seq_printf(m, "%s vblank counter: %08u\n\n", > + lcrtc->base.name, > + ops->get_vblank_counter(lcrtc)); > + > + return 0; > +} > + > +static int lsdc_pixpll_show_clock(struct seq_file *m, void *arg) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; > + struct lsdc_pixpll *pixpll = &lcrtc->pixpll; > + const struct lsdc_pixpll_funcs *funcs = pixpll->funcs; > + struct drm_crtc *crtc = &lcrtc->base; > + struct drm_display_mode *mode = &crtc->state->mode; > + struct drm_printer printer = drm_seq_file_printer(m); > + unsigned int out_khz; > + > + out_khz = funcs->get_rate(pixpll); > + > + seq_printf(m, "%s: %dx%d@%d\n", crtc->name, > + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode)); > + > + seq_printf(m, "Pixel clock required: %d kHz\n", mode->clock); > + seq_printf(m, "Actual frequency output: %u kHz\n", out_khz); > + seq_printf(m, "Diff: %d kHz\n", out_khz - mode->clock); > + > + funcs->print(pixpll, &printer); > + > + return 0; > +} > + > +static struct drm_info_list lsdc_crtc_debugfs_list[2][4] = { > + [0] = { > + { "regs", lsdc_crtc_show_regs, 0, NULL }, > + { "pixclk", lsdc_pixpll_show_clock, 0, NULL }, > + { "scanpos", lsdc_crtc_show_scan_position, 0, NULL }, > + { "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL }, > + }, > + [1] = { > + { "regs", lsdc_crtc_show_regs, 0, NULL }, > + { "pixclk", lsdc_pixpll_show_clock, 0, NULL }, > + { "scanpos", lsdc_crtc_show_scan_position, 0, NULL }, > + { "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL }, > + }, > +}; > + > +/* operate manually */ > + > +static int lsdc_crtc_man_op_show(struct seq_file *m, void *data) > +{ > + seq_puts(m, "soft_reset: soft reset this CRTC\n"); > + seq_puts(m, "enable: enable this CRTC\n"); > + seq_puts(m, "disable: disable this CRTC\n"); > + seq_puts(m, "flip: trigger the page flip\n"); > + seq_puts(m, "clone: clone the another crtc with hardware logic\n"); > + > + return 0; > +} > + > +static int lsdc_crtc_man_op_open(struct inode *inode, struct file *file) > +{ > + struct drm_crtc *crtc = inode->i_private; > + > + return single_open(file, lsdc_crtc_man_op_show, crtc); > +} > + > +static ssize_t lsdc_crtc_man_op_write(struct file *file, > + const char __user *ubuf, > + size_t len, > + loff_t *offp) > +{ > + struct seq_file *m = file->private_data; > + struct lsdc_crtc *lcrtc = m->private; > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + > + char buf[16]; > + > + if (len > sizeof(buf) - 1) > + return -EINVAL; > + > + if (copy_from_user(buf, ubuf, len)) > + return -EFAULT; > + > + buf[len] = '\0'; > + > + if (sysfs_streq(buf, "soft_reset")) > + ops->soft_reset(lcrtc); > + else if (sysfs_streq(buf, "enable")) > + ops->enable(lcrtc); > + else if (sysfs_streq(buf, "disable")) > + ops->disable(lcrtc); > + else if (sysfs_streq(buf, "flip")) > + ops->flip(lcrtc); > + else if (sysfs_streq(buf, "clone")) > + ops->clone(lcrtc); > + > + return len; > +} > + > +static const struct file_operations lsdc_crtc_man_op_fops = { > + .owner = THIS_MODULE, > + .open = lsdc_crtc_man_op_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > + .write = lsdc_crtc_man_op_write, > +}; > + > +static int lsdc_crtc_late_register(struct drm_crtc *crtc) > +{ > + struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + struct drm_minor *minor = crtc->dev->primary; > + unsigned int index = dispipe->index; > + unsigned int i; > + > + lcrtc->preg = lsdc_crtc_regs_array[index]; > + lcrtc->nreg = ARRAY_SIZE(lsdc_crtc_regs_array[index]); > + lcrtc->p_info_list = lsdc_crtc_debugfs_list[index]; > + lcrtc->n_info_list = ARRAY_SIZE(lsdc_crtc_debugfs_list[index]); > + > + for (i = 0; i < lcrtc->n_info_list; ++i) > + lcrtc->p_info_list[i].data = lcrtc; > + > + drm_debugfs_create_files(lcrtc->p_info_list, > + lcrtc->n_info_list, > + crtc->debugfs_entry, > + minor); > + > + /* supported manual operations */ > + debugfs_create_file("ops", 0644, crtc->debugfs_entry, lcrtc, > + &lsdc_crtc_man_op_fops); > + > + return 0; > +} > + > +static void lsdc_crtc_atomic_print_state(struct drm_printer *p, > + const struct drm_crtc_state *state) > +{ > + const struct lsdc_crtc_state *priv_state; > + const struct lsdc_pixpll_parms *pparms; > + > + priv_state = container_of_const(state, struct lsdc_crtc_state, base); > + pparms = &priv_state->pparms; > + > + drm_printf(p, "\tInput clock divider = %u\n", pparms->div_ref); > + drm_printf(p, "\tMedium clock Multiplier = %u\n", pparms->loopc); "multiplier" for consistent casing > + drm_printf(p, "\tOutput clock divider = %u\n", pparms->div_out); > +} > + > +static const struct drm_crtc_funcs ls7a1000_crtc_funcs = { > + .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, > + .late_register = lsdc_crtc_late_register, > + .enable_vblank = lsdc_crtc_enable_vblank, > + .disable_vblank = lsdc_crtc_disable_vblank, > + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, > + .atomic_print_state = lsdc_crtc_atomic_print_state, > +}; > + > +static const struct drm_crtc_funcs ls7a2000_crtc_funcs = { > + .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, > + .late_register = lsdc_crtc_late_register, > + .get_vblank_counter = lsdc_crtc_get_vblank_counter, > + .enable_vblank = lsdc_crtc_enable_vblank, > + .disable_vblank = lsdc_crtc_disable_vblank, > + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, > + .atomic_print_state = lsdc_crtc_atomic_print_state, > +}; > + > +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; > + unsigned int pitch; > + > + 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_kms(ddev, "mode %dx%d, pixel clock=%d is too high\n", > + mode->hdisplay, mode->vdisplay, mode->clock); > + return MODE_CLOCK_HIGH; > + } > + > + /* 4 for DRM_FORMAT_XRGB8888 */ > + pitch = mode->hdisplay * 4; > + > + if (pitch % descp->pitch_align) { > + drm_dbg_kms(ddev, "aligned to %u bytes is required: %u\n", "alignment of %u bytes" > + descp->pitch_align, pitch); > + return MODE_BAD_WIDTH; > + } > + > + return MODE_OK; > +} > + > +static int lsdc_pixpll_atomic_check(struct drm_crtc *crtc, > + struct drm_crtc_state *state) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + struct lsdc_pixpll *pixpll = &lcrtc->pixpll; > + const struct lsdc_pixpll_funcs *pfuncs = pixpll->funcs; > + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); > + unsigned int clock = state->mode.clock; > + int ret; > + > + ret = pfuncs->compute(pixpll, clock, &priv_state->pparms); > + if (ret) { > + drm_warn(crtc->dev, "find PLL parms for %ukHz failed\n", clock); "failed to find PLL params for" > + 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_mode_set_nofb(struct drm_crtc *crtc) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *crtc_hw_ops = lcrtc->hw_ops; > + struct lsdc_pixpll *pixpll = &lcrtc->pixpll; > + const struct lsdc_pixpll_funcs *pixpll_funcs = pixpll->funcs; > + struct drm_crtc_state *state = crtc->state; > + struct drm_display_mode *mode = &state->mode; > + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); > + > + pixpll_funcs->update(pixpll, &priv_state->pparms); > + > + if (crtc_hw_ops->set_dma_step) { > + unsigned int width_in_bytes = mode->hdisplay * 4; > + enum lsdc_dma_steps dma_step; > + > + /* > + * Using large dma step as much as possible, for improving "Using DMA step as large as possible" > + * hardware DMA efficiency. > + */ > + if (width_in_bytes % 256 == 0) > + dma_step = LSDC_DMA_STEP_256_BYTES; > + else if (width_in_bytes % 128 == 0) > + dma_step = LSDC_DMA_STEP_128_BYTES; > + else if (width_in_bytes % 64 == 0) > + dma_step = LSDC_DMA_STEP_64_BYTES; > + else /* width_in_bytes % 32 == 0 */ > + dma_step = LSDC_DMA_STEP_32_BYTES; > + > + crtc_hw_ops->set_dma_step(lcrtc, dma_step); > + } > + > + crtc_hw_ops->set_mode(lcrtc, mode); > +} > + > +static void lsdc_crtc_send_vblank(struct drm_crtc *crtc) > +{ > + struct drm_device *ddev = crtc->dev; > + unsigned long flags; > + > + if (!crtc->state || !crtc->state->event) > + return; > + > + drm_dbg(ddev, "send vblank manually\n"); "manually sending vblank" > + > + spin_lock_irqsave(&ddev->event_lock, flags); > + drm_crtc_send_vblank_event(crtc, crtc->state->event); > + crtc->state->event = NULL; > + spin_unlock_irqrestore(&ddev->event_lock, flags); > +} > + > +static void lsdc_crtc_atomic_enable(struct drm_crtc *crtc, > + struct drm_atomic_state *state) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + > + drm_crtc_vblank_on(crtc); > + > + drm_dbg(crtc->dev, "%s enable\n", crtc->name); "enabling %s\n" > + > + ops->enable(lcrtc); > +} > + > +static void lsdc_crtc_atomic_disable(struct drm_crtc *crtc, > + struct drm_atomic_state *state) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + > + drm_crtc_vblank_off(crtc); > + > + ops->disable(lcrtc); > + > + drm_dbg(crtc->dev, "%s disable\n", crtc->name); "disabling %s\n" > + > + /* > + * Make sure we issue a vblank event after disabling the CRTC if > + * someone was waiting it. "waiting for" > + */ > + lsdc_crtc_send_vblank(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_crtc *lcrtc = to_lsdc_crtc(crtc); > + const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; > + int vsw, vbp, vactive_start, vactive_end, vfp_end; > + int x, y; > + > + 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(); > + > + ops->get_scan_pos(lcrtc, &x, &y); > + > + if (y > vactive_end) > + y = y - vfp_end - vactive_start; > + else > + y -= vactive_start; > + > + *vpos = y; > + *hpos = x; > + > + if (etime) > + *etime = ktime_get(); > + > + return true; > +} > + > +static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = { > + .mode_valid = lsdc_crtc_mode_valid, > + .mode_set_nofb = lsdc_crtc_mode_set_nofb, > + .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 ls7a1000_crtc_init(struct drm_device *ddev, > + struct drm_crtc *crtc, > + struct drm_plane *primary, > + struct drm_plane *cursor, > + unsigned int index) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + int ret; > + > + ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index); > + if (ret) { > + drm_err(ddev, "crtc init with pll failed: %d\n", ret); "crtc pll init failed"? > + return ret; > + } > + > + lcrtc->ldev = to_lsdc(ddev); > + lcrtc->hw_ops = &ls7a1000_crtc_hw_ops[index]; > + > + ret = drm_crtc_init_with_planes(ddev, > + crtc, > + primary, > + cursor, > + &ls7a1000_crtc_funcs, > + "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); > + > + ret = drm_mode_crtc_set_gamma_size(crtc, 256); > + if (ret) > + return ret; > + > + drm_crtc_enable_color_mgmt(crtc, 0, false, 256); > + > + return 0; > +} > + > +int ls7a2000_crtc_init(struct drm_device *ddev, > + struct drm_crtc *crtc, > + struct drm_plane *primary, > + struct drm_plane *cursor, > + unsigned int index) > +{ > + struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); > + int ret; > + > + ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index); > + if (ret) { > + drm_err(ddev, "crtc init with pll failed: %d\n", ret); > + return ret; > + } > + > + lcrtc->ldev = to_lsdc(ddev); > + lcrtc->hw_ops = &ls7a2000_crtc_hw_ops[index]; > + > + ret = drm_crtc_init_with_planes(ddev, > + crtc, > + primary, > + cursor, > + &ls7a2000_crtc_funcs, > + "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); > + > + 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..9ad2c5a0add2 > --- /dev/null > +++ b/drivers/gpu/drm/loongson/lsdc_debugfs.c > @@ -0,0 +1,91 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include > + > +#include "lsdc_benchmark.h" > +#include "lsdc_drv.h" > +#include "lsdc_gem.h" > +#include "lsdc_probe.h" > +#include "lsdc_ttm.h" > + > +/* device level debugfs */ > + > +static int lsdc_identify(struct seq_file *m, void *arg) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data; > + const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp); > + u8 impl, rev; > + > + loongson_cpu_get_prid(&impl, &rev); > + > + seq_printf(m, "Running on cpu 0x%x, cpu revision: 0x%x\n", > + impl, rev); Is this really needed/relevant for LSDC identification? AFAICS the loongson_cpu_get_prid helper has only one use (that's here), so if it's not absolutely necessary you can just get rid of that function and lsdc_probe.h altogether. > + > + seq_printf(m, "Contained in: %s\n", gfx->model); "model: " would be more appropriate for a piece of info looking like a "gfx->model"? > + > + 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; > +} > + > +static int lsdc_show_gfxpll_clock(struct seq_file *m, void *arg) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data; > + struct drm_printer printer = drm_seq_file_printer(m); > + struct loongson_gfxpll *gfxpll = ldev->gfxpll; > + > + gfxpll->funcs->print(gfxpll, &printer, true); > + > + return 0; > +} > + > +static int lsdc_show_benchmark(struct seq_file *m, void *arg) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct lsdc_device *ldev = (struct lsdc_device *)node->info_ent->data; > + struct drm_printer printer = drm_seq_file_printer(m); > + > + lsdc_show_benchmark_copy(ldev, &printer); > + > + return 0; > +} > + > +static struct drm_info_list lsdc_debugfs_list[] = { > + { "chips", lsdc_identify, 0, NULL }, > + { "clocks", lsdc_show_gfxpll_clock, 0, NULL }, > + { "mm", lsdc_show_mm, 0, NULL }, > + { "bos", lsdc_show_buffer_object, 0, NULL }, > + { "benchmark", lsdc_show_benchmark, 0, NULL }, > +}; > + > +void lsdc_debugfs_init(struct drm_minor *minor) > +{ > + struct drm_device *ddev = minor->dev; > + struct lsdc_device *ldev = to_lsdc(ddev); > + unsigned int N = ARRAY_SIZE(lsdc_debugfs_list); "n" because it's not a macro definition? > + unsigned int i; > + > + for (i = 0; i < N; ++i) > + lsdc_debugfs_list[i].data = ldev; > + > + drm_debugfs_create_files(lsdc_debugfs_list, > + N, > + minor->debugfs_root, > + minor); > + > + lsdc_ttm_debugfs_init(ldev); > +} > diff --git a/drivers/gpu/drm/loongson/lsdc_device.c b/drivers/gpu/drm/loongson/lsdc_device.c > new file mode 100644 > index 000000000000..b4152f228beb > --- /dev/null > +++ b/drivers/gpu/drm/loongson/lsdc_device.c > @@ -0,0 +1,104 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include > + > +#include "lsdc_drv.h" > + > +static const struct lsdc_kms_funcs ls7a1000_kms_funcs = { > + .create_i2c = lsdc_create_i2c_chan, > + .irq_handler = ls7a1000_dc_irq_handler, > + .output_init = ls7a1000_output_init, > + .cursor_plane_init = ls7a1000_cursor_plane_init, > + .primary_plane_init = lsdc_primary_plane_init, > + .crtc_init = ls7a1000_crtc_init, > +}; > + > +static const struct lsdc_kms_funcs ls7a2000_kms_funcs = { > + .create_i2c = lsdc_create_i2c_chan, > + .irq_handler = ls7a2000_dc_irq_handler, > + .output_init = ls7a2000_output_init, > + .cursor_plane_init = ls7a2000_cursor_plane_init, > + .primary_plane_init = lsdc_primary_plane_init, > + .crtc_init = ls7a2000_crtc_init, > +}; > + > +static const struct loongson_gfx_desc ls7a1000_gfx = { > + .dc = { > + .num_of_crtc = 2, > + .max_pixel_clk = 200000, > + .max_width = 2048, > + .max_height = 2048, > + .num_of_hw_cursor = 1, > + .hw_cursor_w = 32, > + .hw_cursor_h = 32, > + .pitch_align = 256, > + .mc_bits = 40, > + .has_vblank_counter = false, > + .funcs = &ls7a1000_kms_funcs, > + }, > + .conf_reg_base = LS7A1000_CONF_REG_BASE, > + .gfxpll = { > + .reg_offset = LS7A1000_PLL_GFX_REG, > + .reg_size = 8, > + }, > + .pixpll = { > + [0] = { > + .reg_offset = LS7A1000_PIXPLL0_REG, > + .reg_size = 8, > + }, > + [1] = { > + .reg_offset = LS7A1000_PIXPLL1_REG, > + .reg_size = 8, > + }, > + }, > + .chip_id = CHIP_LS7A1000, > + .model = "LS7A1000 bridge chipset", > +}; > + > +static const struct loongson_gfx_desc ls7a2000_gfx = { > + .dc = { > + .num_of_crtc = 2, > + .max_pixel_clk = 350000, > + .max_width = 4096, > + .max_height = 4096, > + .num_of_hw_cursor = 2, > + .hw_cursor_w = 64, > + .hw_cursor_h = 64, > + .pitch_align = 64, > + .mc_bits = 40, /* Support 48 but using 40 for backward compatibility */ For compatibility with what? > + .has_vblank_counter = true, > + .funcs = &ls7a2000_kms_funcs, > + }, > + .conf_reg_base = LS7A2000_CONF_REG_BASE, > + .gfxpll = { > + .reg_offset = LS7A2000_PLL_GFX_REG, > + .reg_size = 8, > + }, > + .pixpll = { > + [0] = { > + .reg_offset = LS7A2000_PIXPLL0_REG, > + .reg_size = 8, > + }, > + [1] = { > + .reg_offset = LS7A2000_PIXPLL1_REG, > + .reg_size = 8, > + }, > + }, > + .chip_id = CHIP_LS7A2000, > + .model = "LS7A2000 bridge chipset", > +}; > + > +const struct lsdc_desc * > +lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip_id) > +{ > + if (chip_id == CHIP_LS7A1000) > + return &ls7a1000_gfx.dc; > + > + if (chip_id == CHIP_LS7A2000) > + return &ls7a2000_gfx.dc; I suppose more are to be added later, so better use a switch statement here. > + > + return ERR_PTR(-ENODEV); > +} > diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c > new file mode 100644 > index 000000000000..98e2c28f886f > --- /dev/null > +++ b/drivers/gpu/drm/loongson/lsdc_drv.c > @@ -0,0 +1,495 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include > + > +#include