From patchwork Thu Jul 11 17:55:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 13730953 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id ACD40C3DA49 for ; Thu, 11 Jul 2024 17:55:37 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 7916810EADB; Thu, 11 Jul 2024 17:55:36 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=linux.dev header.i=@linux.dev header.b="bDIN2MpK"; dkim-atps=neutral Received: from out-179.mta1.migadu.com (out-179.mta1.migadu.com [95.215.58.179]) by gabe.freedesktop.org (Postfix) with ESMTPS id 5E79E10E025 for ; Thu, 11 Jul 2024 17:55:34 +0000 (UTC) X-Envelope-To: mripard@kernel.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1720720532; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2zjtF7hdlVTSTJjmI17thydLodaQQO05xyo5+5Uchd0=; b=bDIN2MpKJ7IX9IDSywQJnq12e2e2tnTMCVLmSHkhi04IxLjQqG3c8Uu74vDZduMFCLqPak 348PS4D1eXSJasR42+dTm1jTkiO1Y643VRahoqIpdvd1OwuaoqK1rz5Vth1bQX1VjCAuGb nPTWSwhHu8rad91Ov5XWZnKK+K8+YN0= X-Envelope-To: tzimmermann@suse.de X-Envelope-To: linux-kernel@vger.kernel.org X-Envelope-To: dri-devel@lists.freedesktop.org X-Envelope-To: sui.jingfeng@linux.dev X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Sui Jingfeng Subject: [PATCH v3 1/1] drm/loongson: Introduce component framework support Date: Fri, 12 Jul 2024 01:55:23 +0800 Message-ID: <20240711175523.538457-2-sui.jingfeng@linux.dev> In-Reply-To: <20240711175523.538457-1-sui.jingfeng@linux.dev> References: <20240711175523.538457-1-sui.jingfeng@linux.dev> MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" In some display subsystems, the functionality of a PCIe device may too complex to be managed by a single driver. A split of the functionality into child devices can help to achieve better modularity. For example, KMS drivers who has a dependency on external modules will suffer from the deferral probe problem of those modules. The problem is that the DRM driver has to tear down everything when it receives '-EPROBE_DEFER', even though the independent part of the entire driver is not very closely related. For Loongson Graphics, the GPU and the display controller are two distinct PCIe devices. Despite hardware units that come along with PCIe are actually all ready to be driven, DRM driver still need to wait all of its dependency ready before it can register the service to userspace. The idea is to migrate(offload) the dependency to submodule, creating submodule by creating platform device manually. Such submodules are functional as agents, which will be responsible for attaching needed external modules for the main body of entire KMS drive. It's also for better modularity, as output hardware units are relatively standalone IPs. Add dummy GPU driver as a subcomponent, use the component framework to bind those scatter software/hardware entities into one. Move DRM-related codes into the loongson_drm_master_bind() function. Move output-related things into the submodule, since the encoder/connector/ transcoder are sometimes depend on external module. The display controller and GPIO-I2Cs go with the PCIe master, as they have no dependency on external modules. Make lsdc PCI driver as a subcomponent as well, creating a platform device as proxy. The proxy will responsible attach the common drm routines to our hardware, which keep the whole drm driver balanced (and fair) enough for both the DC and GPU. The lsdc PCI device is the haidden(implicit) parent, while the proxy is the explicit master, which deal with drm related affairs for its parent. The master won't bound until all submodules are ready, submodules are responsible to return '-EPROBE_DEFER' back to the driver core if it needs to do so. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 5 + drivers/gpu/drm/loongson/loonggpu_pci_drv.c | 156 ++++++++++++ drivers/gpu/drm/loongson/loonggpu_pci_drv.h | 33 +++ drivers/gpu/drm/loongson/loongson_device.c | 1 + drivers/gpu/drm/loongson/loongson_drv.c | 157 ++++++++++++ drivers/gpu/drm/loongson/loongson_module.c | 38 ++- drivers/gpu/drm/loongson/loongson_module.h | 17 ++ drivers/gpu/drm/loongson/lsdc_drv.c | 235 ++++++++---------- drivers/gpu/drm/loongson/lsdc_drv.h | 56 ++--- drivers/gpu/drm/loongson/lsdc_i2c.c | 43 ++-- drivers/gpu/drm/loongson/lsdc_i2c.h | 11 +- drivers/gpu/drm/loongson/lsdc_irq.c | 21 +- drivers/gpu/drm/loongson/lsdc_output.c | 208 ++++++++++++++++ drivers/gpu/drm/loongson/lsdc_output.h | 36 ++- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 7 +- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 17 +- drivers/gpu/drm/loongson/lsdc_ttm.c | 4 +- drivers/gpu/drm/loongson/lsdc_ttm.h | 2 +- 18 files changed, 832 insertions(+), 215 deletions(-) create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/loonggpu_pci_drv.h create mode 100644 drivers/gpu/drm/loongson/loongson_drv.c create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 91e72bd900c1..cdc60ec975e4 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -9,6 +9,7 @@ loongson-y := \ lsdc_gfxpll.o \ lsdc_i2c.o \ lsdc_irq.o \ + lsdc_output.o \ lsdc_output_7a1000.o \ lsdc_output_7a2000.o \ lsdc_plane.o \ @@ -16,7 +17,11 @@ loongson-y := \ lsdc_probe.o \ lsdc_ttm.o +loongson-y += \ + loonggpu_pci_drv.o + loongson-y += loongson_device.o \ + loongson_drv.o \ loongson_module.o obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/loonggpu_pci_drv.c b/drivers/gpu/drm/loongson/loonggpu_pci_drv.c new file mode 100644 index 000000000000..a34c213171e0 --- /dev/null +++ b/drivers/gpu/drm/loongson/loonggpu_pci_drv.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include + +#include +#include + +#include "loongson_module.h" +#include "loonggpu_pci_drv.h" + +static int loonggpu_get_version(struct loonggpu_device *gpu) +{ + u32 hw_info = loong_rreg32(gpu, 0x8C); + u8 host_id; + u8 revision; + + /* LoongGPU hardware info */ + gpu->ver_major = (hw_info >> 8) & 0x0F; + gpu->ver_minor = (hw_info & 0xF0) >> 4; + revision = hw_info & 0x0F; + host_id = (hw_info >> 16) & 0xFF; + + drm_info(gpu->drm, "LoongGPU(TM): LG%x%x0, revision: %x, Host: %s\n", + gpu->ver_major, gpu->ver_minor, revision, + host_id ? "LS2K2000" : "LS7A2000"); + + return 0; +} + +static irqreturn_t loonggpu_irq_handler(int irq, void *arg) +{ + struct loonggpu_device *gpu = arg; + + drm_info(gpu->drm, "LoongGPU interrupted\n"); + + return IRQ_HANDLED; +} + +static int loonggpu_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm = data; + struct loonggpu_device *gpu; + int ret; + + gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL); + if (!gpu) + return -ENOMEM; + + gpu->reg_base = pcim_iomap(pdev, 0, 0); + if (!gpu->reg_base) + return -ENOMEM; + + gpu->pdev = pdev; + gpu->drm = drm; + + loonggpu_get_version(gpu); + + dev_set_drvdata(dev, gpu); + + ret = devm_request_irq(dev, + pdev->irq, + loonggpu_irq_handler, + IRQF_SHARED, + dev_name(dev), + gpu); + if (ret) + return ret; + + drm_info(gpu->drm, "LoongGPU irq: %d\n", pdev->irq); + + return 0; +} + +static void loonggpu_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct loonggpu_device *gpu = dev_get_drvdata(dev); + + if (gpu) { + pcim_iounmap(pdev, gpu->reg_base); + devm_kfree(dev, gpu); + } +} + +static const struct component_ops loonggpu_component_ops = { + .bind = loonggpu_component_bind, + .unbind = loonggpu_component_unbind, +}; + +static int loonggpu_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + dev_info(&pdev->dev, "LoongGPU PCI driver probed\n"); + + return component_add(&pdev->dev, &loonggpu_component_ops); +} + +static void loonggpu_pci_remove(struct pci_dev *pdev) +{ + component_del(&pdev->dev, &loonggpu_component_ops); +} + +static const struct pci_device_id loonggpu_pci_id_list[] = { + {PCI_VDEVICE(LOONGSON, 0x7a25), CHIP_LS7A2000}, + { }, +}; + +struct pci_driver loonggpu_pci_driver = { + .name = "LoongGPU", + .id_table = loonggpu_pci_id_list, + .probe = loonggpu_pci_probe, + .remove = loonggpu_pci_remove, +}; + +void loonggpu_pci_match_add(struct device *master, + struct component_match **matchptr) +{ + struct pci_dev *gpu; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(loonggpu_pci_id_list); ++i) { + unsigned int did = loonggpu_pci_id_list[i].device; + + gpu = pci_get_device(PCI_VENDOR_ID_LOONGSON, did, NULL); + if (gpu) { + component_match_add(master, matchptr, + component_compare_dev, &gpu->dev); + pci_dev_put(gpu); + + dev_dbg(master, "match added, name: %s, %u\n", + dev_name(&gpu->dev), i); + return; + } + } +} + +MODULE_DEVICE_TABLE(pci, loonggpu_pci_id_list); diff --git a/drivers/gpu/drm/loongson/loonggpu_pci_drv.h b/drivers/gpu/drm/loongson/loonggpu_pci_drv.h new file mode 100644 index 000000000000..35b176ff01d0 --- /dev/null +++ b/drivers/gpu/drm/loongson/loonggpu_pci_drv.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __LOONGGPU_PCI_DRV_H__ +#define __LOONGGPU_PCI_DRV_H__ + +#include +#include + +struct loonggpu_device { + struct pci_dev *pdev; + struct drm_device *drm; + + void __iomem *reg_base; + + u32 ver_major; + u32 ver_minor; + u32 revision; +}; + +static inline u32 loong_rreg32(struct loonggpu_device *ldev, u32 offset) +{ + return readl(ldev->reg_base + offset); +} + +static inline void loong_wreg32(struct loonggpu_device *ldev, u32 offset, u32 val) +{ + writel(val, ldev->reg_base + offset); +} + +void loonggpu_pci_match_add(struct device *master, + struct component_match **matchptr); + +#endif diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c index 9986c8a2a255..f0e3d6544291 100644 --- a/drivers/gpu/drm/loongson/loongson_device.c +++ b/drivers/gpu/drm/loongson/loongson_device.c @@ -4,6 +4,7 @@ */ #include +#include #include "lsdc_drv.h" diff --git a/drivers/gpu/drm/loongson/loongson_drv.c b/drivers/gpu/drm/loongson/loongson_drv.c new file mode 100644 index 000000000000..b5273bbf03e6 --- /dev/null +++ b/drivers/gpu/drm/loongson/loongson_drv.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "loonggpu_pci_drv.h" +#include "loongson_module.h" +#include "lsdc_drv.h" +#include "lsdc_gem.h" +#include "lsdc_output.h" +#include "lsdc_ttm.h" + +static struct platform_device *loongson_dev; + +static void loongson_device_postfini(void *data) +{ + struct platform_device *proxy = data; + + platform_device_unregister(proxy); +} + +int loongson_device_preinit(struct device *parent, void *data) +{ + struct platform_device *proxy; + int ret; + + proxy = platform_device_register_resndata(parent, + DRIVER_NAME, + PLATFORM_DEVID_NONE, + NULL, 0, + data, sizeof(void *)); + if (IS_ERR(proxy)) { + dev_err(parent, "failed to register loongson proxy device\n"); + return PTR_ERR(proxy); + } + + ret = devm_add_action_or_reset(parent, loongson_device_postfini, proxy); + if (ret) + return ret; + + loongson_dev = proxy; + + return 0; +} + +static int loongson_drm_master_bind(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev->parent); + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_desc *descp = ldev->descp; + int num_pipe = descp->num_of_crtc; + int ret; + + loongson_gfxpll_create(&ldev->base, &ldev->gfxpll); + + ret = lsdc_mode_config_init(ddev, descp); + if (ret) + return ret; + + ret = component_bind_all(dev, ddev); + if (ret) { + dev_err(dev, "master bind all failed: %d\n", ret); + return ret; + } + + ret = lsdc_modeset_init(ldev, num_pipe, descp->funcs, loongson_vblank); + if (ret) + return ret; + + if (loongson_vblank) { + ret = drm_vblank_init(ddev, num_pipe); + if (ret) + return ret; + } + + lsdc_gem_init(ddev); + + ret = lsdc_ttm_init(ddev); + if (ret) { + drm_err(ddev, "Memory manager init failed: %d\n", ret); + return ret; + } + + drm_mode_config_reset(ddev); + + drmm_kms_helper_poll_init(ddev); + + ret = drm_dev_register(ddev, 0); + if (ret) + return ret; + + drm_fbdev_ttm_setup(ddev, 32); + + return 0; +} + +static void loongson_drm_master_unbind(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev->parent); + + drm_atomic_helper_shutdown(ddev); + + component_unbind_all(dev, ddev); +} + +static const struct component_master_ops loongson_drm_master_ops = { + .bind = loongson_drm_master_bind, + .unbind = loongson_drm_master_unbind, +}; + +static int loongson_drm_driver_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct drm_device *ddev = *(void **)dev->platform_data; + struct component_match *matches = NULL; + + dev_set_drvdata(dev, ddev); + + lsdc_output_match_add(dev, &matches); + + lsdc_pci_match_add(dev, &matches); + + loonggpu_pci_match_add(dev, &matches); + + return component_master_add_with_match(dev, + &loongson_drm_master_ops, + matches); +} + +static void loongson_drm_driver_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + component_master_del(dev, &loongson_drm_master_ops); +} + +struct platform_driver loongson_drm_platform_driver = { + .probe = loongson_drm_driver_probe, + .remove_new = loongson_drm_driver_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; diff --git a/drivers/gpu/drm/loongson/loongson_module.c b/drivers/gpu/drm/loongson/loongson_module.c index d2a51bd395f6..836b6a40a9f1 100644 --- a/drivers/gpu/drm/loongson/loongson_module.c +++ b/drivers/gpu/drm/loongson/loongson_module.c @@ -4,6 +4,7 @@ */ #include +#include #include