From patchwork Tue Dec 7 14:27:13 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 521712 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 944DFC433F5 for ; Tue, 7 Dec 2021 14:28:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237806AbhLGObk (ORCPT ); Tue, 7 Dec 2021 09:31:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44112 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237799AbhLGObj (ORCPT ); Tue, 7 Dec 2021 09:31:39 -0500 Received: from mail-pf1-x429.google.com (mail-pf1-x429.google.com [IPv6:2607:f8b0:4864:20::429]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1DFB3C061574; Tue, 7 Dec 2021 06:28:09 -0800 (PST) Received: by mail-pf1-x429.google.com with SMTP id i12so13643483pfd.6; Tue, 07 Dec 2021 06:28:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Q0MsKoLxSvs7X9cZpCJWtx19tEgg6uxw/DuKyl42aFU=; b=CG+hc5tTr+VSZGM2hFcB8L+V4tPAiUfdKz0qzNu6DF4/y/TtAoRcVhtuZgSXkGos64 CnUSjtOhWgmvbnsAdd3jdnBBSzTzRDdQEDVRAXatjD1KQXwIYfjs5M8GdWeVZhEhfpej NEDGMLRArLyH65mDrgZHHkgWebyvuswuNGHmi7dPHhZqHRscna3xZFrmFlchMTV/5HSH 6Gn9ZBs3xv9jMm7oUWvCzlr+NDJP21tQUi+ZRFbUSzq2h2ttbIXuJG1GQXIgSfSSoLNW snW8bTiKVyVPhPG7v32JoM7C7rRtUkuzxRcRjBqwfKYKKuky45HXc4E3cfdiYZWau52N kCHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Q0MsKoLxSvs7X9cZpCJWtx19tEgg6uxw/DuKyl42aFU=; b=KTepMJ55O4K22d5I9TQVCSaUab0luYZBqbmnNsTx7t4Z8ZG5w1ATjcHKeDPbioL+K0 5e0yJhXdJOmS0uds/DXA2cttv5FqOLlcQ8Eqb3tuMBfHIhpsiJuXl8q5UQb8+AMGz9j5 rFeqK5eyi42/2hL11L0ZxWjslBXp6TLxcZXLODMV+aiSUXL61kspRWS6COPqDZli4tLu YNwNvDoUhLXqp1/LIc6l5J2+JfMLDKUON2ozA1ahjcelDsWeAVMam/dkhEqGiuw3Yk+i vEDLyvoaBgU3uIEMFK2rgyX9eCcgtE60aiBA3fbw0uEn+zRzdNfxiEAObmkpz9Ahb5oL FJKA== X-Gm-Message-State: AOAM532wVL1K17LnCU2z2C2ddLYQuXH9k2Uq6pmHKR/vilbrxhqVUMFy 2t6BsRU5HtwjZ5eGP0qQc28= X-Google-Smtp-Source: ABdhPJxsYIQ5qAEwOevUHie4l3unr3f34PSBXJLKZunzKtAkBYcCTL6+PRYNkPT4ndYRjkhFMi1c6g== X-Received: by 2002:a63:2b8a:: with SMTP id r132mr23647643pgr.434.1638887288551; Tue, 07 Dec 2021 06:28:08 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([103.6.5.6]) by smtp.gmail.com with ESMTPSA id d20sm16373247pfl.88.2021.12.07.06.28.01 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 07 Dec 2021 06:28:08 -0800 (PST) From: Kevin Tang To: maarten.lankhorst@linux.intel.com, mripard@kernel.org, sean@poorly.run, airlied@linux.ie, daniel@ffwll.ch, robh+dt@kernel.org, mark.rutland@arm.com, kevin3.tang@gmail.com, pony1.wu@gmail.com Cc: orsonzhai@gmail.com, zhang.lyra@gmail.com, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org Subject: [PATCH v8 2/6] drm/sprd: add Unisoc's drm kms master Date: Tue, 7 Dec 2021 22:27:13 +0800 Message-Id: <20211207142717.30296-3-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.29.0 In-Reply-To: <20211207142717.30296-1-kevin3.tang@gmail.com> References: <20211207142717.30296-1-kevin3.tang@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Adds drm support for the Unisoc's display subsystem. This is drm kms driver, this driver provides support for the application framework in Android, Yocto and more. Application framework can access Unisoc's display internal peripherals through libdrm or libkms, it's test ok by modetest (DRM/KMS test tool) and Android HWComposer. v4: - Move the devm_drm_dev_alloc to master_ops->bind function. - The managed drmm_mode_config_init() it is no longer necessary for drivers to explicitly call drm_mode_config_cleanup, so delete it. v5: - Remove subdir-ccflgas-y for Makefile. - Keep the selects sorted by alphabet for Kconfig. Cc: Orson Zhai Cc: Chunyan Zhang Signed-off-by: Kevin Tang --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/sprd/Kconfig | 11 ++ drivers/gpu/drm/sprd/Makefile | 3 + drivers/gpu/drm/sprd/sprd_drm.c | 203 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_drm.h | 16 +++ 6 files changed, 236 insertions(+) create mode 100644 drivers/gpu/drm/sprd/Kconfig create mode 100644 drivers/gpu/drm/sprd/Makefile create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index fb1446170..f50abc84f 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -395,6 +395,8 @@ source "drivers/gpu/drm/xlnx/Kconfig" source "drivers/gpu/drm/gud/Kconfig" +source "drivers/gpu/drm/sprd/Kconfig" + config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && MMU && HYPERV diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 1c41156de..e6bab8df7 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -132,3 +132,4 @@ obj-$(CONFIG_DRM_TIDSS) += tidss/ obj-y += xlnx/ obj-y += gud/ obj-$(CONFIG_DRM_HYPERV) += hyperv/ +obj-$(CONFIG_DRM_SPRD) += sprd/ diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig new file mode 100644 index 000000000..726c3e76d --- /dev/null +++ b/drivers/gpu/drm/sprd/Kconfig @@ -0,0 +1,11 @@ +config DRM_SPRD + tristate "DRM Support for Unisoc SoCs Platform" + depends on ARCH_SPRD || COMPILE_TEST + depends on DRM && OF + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select DRM_KMS_HELPER + help + Choose this option if you have a Unisoc chipset. + If M is selected the module will be called sprd_drm. + diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile new file mode 100644 index 000000000..9850f00b8 --- /dev/null +++ b/drivers/gpu/drm/sprd/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y := sprd_drm.o diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c new file mode 100644 index 000000000..bb87f28f2 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_drm.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sprd_drm.h" + +#define DRIVER_NAME "sprd" +#define DRIVER_DESC "Spreadtrum SoCs' DRM Driver" +#define DRIVER_DATE "20200201" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static void sprd_drm_mode_config_init(struct drm_device *drm) +{ + drm->mode_config.min_width = 0; + drm->mode_config.min_height = 0; + drm->mode_config.max_width = 8192; + drm->mode_config.max_height = 8192; + drm->mode_config.allow_fb_modifiers = true; + + drm->mode_config.funcs = &sprd_drm_mode_config_funcs; + drm->mode_config.helper_private = &sprd_drm_mode_config_helper; +} + +DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops); + +static struct drm_driver sprd_drm_drv = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &sprd_drm_fops, + + /* GEM Operations */ + DRM_GEM_CMA_DRIVER_OPS, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int sprd_drm_bind(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm; + struct sprd_drm *sprd; + int ret; + + sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm, drm); + if (IS_ERR(sprd)) + return PTR_ERR(sprd); + + drm = &sprd->drm; + platform_set_drvdata(pdev, drm); + + ret = drmm_mode_config_init(drm); + if (ret) + return ret; + + sprd_drm_mode_config_init(drm); + + /* bind and init sub drivers */ + ret = component_bind_all(drm->dev, drm); + if (ret) { + drm_err(drm, "failed to bind all component.\n"); + return ret; + } + + /* vblank init */ + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret) { + drm_err(drm, "failed to initialize vblank.\n"); + goto err_unbind_all; + } + + /* reset all the states of crtc/plane/encoder/connector */ + drm_mode_config_reset(drm); + + /* init kms poll for handling hpd */ + drm_kms_helper_poll_init(drm); + + ret = drm_dev_register(drm, 0); + if (ret < 0) + goto err_kms_helper_poll_fini; + + return 0; + +err_kms_helper_poll_fini: + drm_kms_helper_poll_fini(drm); +err_unbind_all: + component_unbind_all(drm->dev, drm); + return ret; +} + +static void sprd_drm_unbind(struct device *dev) +{ + struct drm_device *drm = dev_get_drvdata(dev); + + drm_dev_unregister(drm); + + drm_kms_helper_poll_fini(drm); + + component_unbind_all(drm->dev, drm); +} + +static const struct component_master_ops drm_component_ops = { + .bind = sprd_drm_bind, + .unbind = sprd_drm_unbind, +}; + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int sprd_drm_probe(struct platform_device *pdev) +{ + return drm_of_component_probe(&pdev->dev, compare_of, &drm_component_ops); +} + +static int sprd_drm_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &drm_component_ops); + return 0; +} + +static void sprd_drm_shutdown(struct platform_device *pdev) +{ + struct drm_device *drm = platform_get_drvdata(pdev); + + if (!drm) { + drm_warn(drm, "drm device is not available, no shutdown\n"); + return; + } + + drm_atomic_helper_shutdown(drm); +} + +static const struct of_device_id drm_match_table[] = { + { .compatible = "sprd,display-subsystem", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, drm_match_table); + +static struct platform_driver sprd_drm_driver = { + .probe = sprd_drm_probe, + .remove = sprd_drm_remove, + .shutdown = sprd_drm_shutdown, + .driver = { + .name = "sprd-drm-drv", + .of_match_table = drm_match_table, + }, +}; + +static struct platform_driver *sprd_drm_drivers[] = { + &sprd_drm_driver, +}; + +static int __init sprd_drm_init(void) +{ + return platform_register_drivers(sprd_drm_drivers, + ARRAY_SIZE(sprd_drm_drivers)); +} + +static void __exit sprd_drm_exit(void) +{ + platform_unregister_drivers(sprd_drm_drivers, + ARRAY_SIZE(sprd_drm_drivers)); +} + +module_init(sprd_drm_init); +module_exit(sprd_drm_exit); + +MODULE_AUTHOR("Leon He "); +MODULE_AUTHOR("Kevin Tang "); +MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/sprd/sprd_drm.h b/drivers/gpu/drm/sprd/sprd_drm.h new file mode 100644 index 000000000..9781fd591 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_drm.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#ifndef _SPRD_DRM_H_ +#define _SPRD_DRM_H_ + +#include +#include + +struct sprd_drm { + struct drm_device drm; +}; + +#endif /* _SPRD_DRM_H_ */ From patchwork Tue Dec 7 14:27:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 521711 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 47F77C433F5 for ; Tue, 7 Dec 2021 14:28:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237841AbhLGOcR (ORCPT ); Tue, 7 Dec 2021 09:32:17 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44198 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237836AbhLGOb4 (ORCPT ); Tue, 7 Dec 2021 09:31:56 -0500 Received: from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com [IPv6:2607:f8b0:4864:20::1033]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DC74FC061574; Tue, 7 Dec 2021 06:28:25 -0800 (PST) Received: by mail-pj1-x1033.google.com with SMTP id gx15-20020a17090b124f00b001a695f3734aso2867837pjb.0; Tue, 07 Dec 2021 06:28:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yyHpueR6SwuararuOaUqN/JAk6o4mVZveWZtILl2Ja0=; b=KtMpiGnscnisAoatj3zghGB7QGcLSB9SvhYxGhIvHqCE8CL6y8uezDPFmW3JD+Cz0L u7D5BZBo1dKmgU+5E/F0p2VNyZqcfBnnF39Qr71USXSMQzq2tzs1UaeyGgl0mnjQcA2f UZAU6C4djXDSvuaU/q0/fG+U/g13vLfhpO9ga4xOZ7g88NT4ClTf7z74/08NpjTVCZC3 FPWEjAEC6935NRjaH1xiRmTsTnqEeFkrfBrl3oLFVISo1H7iqd39yUVQEADd/LxBNteG 0tOfAiPYerwAObYl9TLOdW9eMVfS4qvwmpNpQ4ovD20dtumGHssehnVnwpvmNz9vcD6O 0Etw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yyHpueR6SwuararuOaUqN/JAk6o4mVZveWZtILl2Ja0=; b=x0fXbzMvBgDbr8Dw59sgwCgvYvPdi2kwtWr0/VNP4LeTqeRNpVNTJiQq/yil1cMTwT Y/9gcNM/iyV+/fY87+ud9z4PWJNkonbBGGsTCzksIDUWxywIWHcs9/GXwIHQxNpHG5a2 JRNReCGrMKXkwHGrhzLwokh536D/vjP2c0YykHV28Dt0PN1zHKyQRNkD/FONtZXDXa5H SnZ2GfOj3vcQfD3CkG5qZLr4nJq9gnPIZsm7DWi2B+q0y5yfwHBMfyS5eRAivHVqF6qk S7+mXfAZ6AS3EmKiMAldGGtJYTwyxtDyMOyOh9OH5ivz4napeAWT28yof6ZvKkBSPlgD xQEg== X-Gm-Message-State: AOAM530iqCoGG80iAAGtQRYn9ANsx9xu0kEwG4He9BW6ZzrBf9gEvnsq DChvLZSnrRh6JvZezTwM5jQ= X-Google-Smtp-Source: ABdhPJzYvFGm/dRi00Kx1xXtkFRPeZ9gV/YiUAC1hE5HeKKtPEfI29R3wo5qVItFvwcpL8r5iZm1mQ== X-Received: by 2002:a17:90b:4a05:: with SMTP id kk5mr7020217pjb.142.1638887305225; Tue, 07 Dec 2021 06:28:25 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([103.6.5.6]) by smtp.gmail.com with ESMTPSA id d20sm16373247pfl.88.2021.12.07.06.28.19 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 07 Dec 2021 06:28:24 -0800 (PST) From: Kevin Tang To: maarten.lankhorst@linux.intel.com, mripard@kernel.org, sean@poorly.run, airlied@linux.ie, daniel@ffwll.ch, robh+dt@kernel.org, mark.rutland@arm.com, kevin3.tang@gmail.com, pony1.wu@gmail.com Cc: orsonzhai@gmail.com, zhang.lyra@gmail.com, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org Subject: [PATCH v8 4/6] drm/sprd: add Unisoc's drm display controller driver Date: Tue, 7 Dec 2021 22:27:15 +0800 Message-Id: <20211207142717.30296-5-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.29.0 In-Reply-To: <20211207142717.30296-1-kevin3.tang@gmail.com> References: <20211207142717.30296-1-kevin3.tang@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem. It's support multi planes, scaler, rotation, PQ(Picture Quality) and more. v2: - Use drm_xxx to replace all DRM_XXX. - Use kzalloc to replace devm_kzalloc for sprd_dpu structure init. v3: - Remove dpu_layer stuff layer and commit layers by aotmic_update v4: - Use drmm_helpers to allocate crtc and planes. - Move rotation enum definitions to crtc layer reg bitfields. - Move allocate crtc and planes to bind function. v5: - Fix the checkpatch warnings. - Use mode_set_nofb instead of mode_valid callback. - Follow the OF-Graph bindings, use of_graph_get_port_by_id instead of of_parse_phandle. - Use zpos to represent the layer position. - Rebase to last drm misc branch. v6: - Disable and clear interrupts before register dpu IRQ - Init dpi config used by crtc_state->adjusted_mode on mode_set_nofb - Remove enable_irq and disable_irq function call. - Remove drm_format_info function call. v7: - Remove iommu error interrupt handling function. Cc: Orson Zhai Cc: Chunyan Zhang Signed-off-by: Kevin Tang Reviewed-by: Maxime Ripard --- drivers/gpu/drm/sprd/Kconfig | 1 + drivers/gpu/drm/sprd/Makefile | 3 +- drivers/gpu/drm/sprd/sprd_dpu.c | 867 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_dpu.h | 109 ++++ drivers/gpu/drm/sprd/sprd_drm.c | 1 + drivers/gpu/drm/sprd/sprd_drm.h | 2 + 6 files changed, 982 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig index 726c3e76d..37762c333 100644 --- a/drivers/gpu/drm/sprd/Kconfig +++ b/drivers/gpu/drm/sprd/Kconfig @@ -5,6 +5,7 @@ config DRM_SPRD select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER select DRM_KMS_HELPER + select VIDEOMODE_HELPERS help Choose this option if you have a Unisoc chipset. If M is selected the module will be called sprd_drm. diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile index 9850f00b8..ab12b95e6 100644 --- a/drivers/gpu/drm/sprd/Makefile +++ b/drivers/gpu/drm/sprd/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y := sprd_drm.o +obj-y := sprd_drm.o \ + sprd_dpu.o diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c new file mode 100644 index 000000000..1d10d0998 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_dpu.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sprd_drm.h" +#include "sprd_dpu.h" + +/* Global control registers */ +#define REG_DPU_CTRL 0x04 +#define REG_DPU_CFG0 0x08 +#define REG_PANEL_SIZE 0x20 +#define REG_BLEND_SIZE 0x24 +#define REG_BG_COLOR 0x2C + +/* Layer0 control registers */ +#define REG_LAY_BASE_ADDR0 0x30 +#define REG_LAY_BASE_ADDR1 0x34 +#define REG_LAY_BASE_ADDR2 0x38 +#define REG_LAY_CTRL 0x40 +#define REG_LAY_SIZE 0x44 +#define REG_LAY_PITCH 0x48 +#define REG_LAY_POS 0x4C +#define REG_LAY_ALPHA 0x50 +#define REG_LAY_CROP_START 0x5C + +/* Interrupt control registers */ +#define REG_DPU_INT_EN 0x1E0 +#define REG_DPU_INT_CLR 0x1E4 +#define REG_DPU_INT_STS 0x1E8 + +/* DPI control registers */ +#define REG_DPI_CTRL 0x1F0 +#define REG_DPI_H_TIMING 0x1F4 +#define REG_DPI_V_TIMING 0x1F8 + +/* MMU control registers */ +#define REG_MMU_EN 0x800 +#define REG_MMU_VPN_RANGE 0x80C +#define REG_MMU_PPN1 0x83C +#define REG_MMU_RANGE1 0x840 +#define REG_MMU_PPN2 0x844 +#define REG_MMU_RANGE2 0x848 + +/* Global control bits */ +#define BIT_DPU_RUN BIT(0) +#define BIT_DPU_STOP BIT(1) +#define BIT_DPU_REG_UPDATE BIT(2) +#define BIT_DPU_IF_EDPI BIT(0) + +/* Layer control bits */ +#define BIT_DPU_LAY_EN BIT(0) +#define BIT_DPU_LAY_LAYER_ALPHA (0x01 << 2) +#define BIT_DPU_LAY_COMBO_ALPHA (0x02 << 2) +#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE (0x00 << 4) +#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE (0x01 << 4) +#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE (0x02 << 4) +#define BIT_DPU_LAY_FORMAT_ARGB8888 (0x03 << 4) +#define BIT_DPU_LAY_FORMAT_RGB565 (0x04 << 4) +#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3 (0x00 << 8) +#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0 (0x01 << 8) +#define BIT_DPU_LAY_NO_SWITCH (0x00 << 10) +#define BIT_DPU_LAY_RB_OR_UV_SWITCH (0x01 << 10) +#define BIT_DPU_LAY_MODE_BLEND_NORMAL (0x00 << 16) +#define BIT_DPU_LAY_MODE_BLEND_PREMULT (0x01 << 16) +#define BIT_DPU_LAY_ROTATION_0 (0x00 << 20) +#define BIT_DPU_LAY_ROTATION_90 (0x01 << 20) +#define BIT_DPU_LAY_ROTATION_180 (0x02 << 20) +#define BIT_DPU_LAY_ROTATION_270 (0x03 << 20) +#define BIT_DPU_LAY_ROTATION_0_M (0x04 << 20) +#define BIT_DPU_LAY_ROTATION_90_M (0x05 << 20) +#define BIT_DPU_LAY_ROTATION_180_M (0x06 << 20) +#define BIT_DPU_LAY_ROTATION_270_M (0x07 << 20) + +/* Interrupt control & status bits */ +#define BIT_DPU_INT_DONE BIT(0) +#define BIT_DPU_INT_TE BIT(1) +#define BIT_DPU_INT_ERR BIT(2) +#define BIT_DPU_INT_UPDATE_DONE BIT(4) +#define BIT_DPU_INT_VSYNC BIT(5) + +/* DPI control bits */ +#define BIT_DPU_EDPI_TE_EN BIT(8) +#define BIT_DPU_EDPI_FROM_EXTERNAL_PAD BIT(10) +#define BIT_DPU_DPI_HALT_EN BIT(16) + +static const u32 layer_fmts[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV16, + DRM_FORMAT_NV61, + DRM_FORMAT_YUV420, + DRM_FORMAT_YVU420, +}; + +struct sprd_plane { + struct drm_plane base; +}; + +static int dpu_wait_stop_done(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + int rc; + + if (ctx->stopped) + return 0; + + rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop, + msecs_to_jiffies(500)); + ctx->evt_stop = false; + + ctx->stopped = true; + + if (!rc) { + drm_err(dpu->drm, "dpu wait for stop done time out!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int dpu_wait_update_done(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + int rc; + + ctx->evt_update = false; + + rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update, + msecs_to_jiffies(500)); + + if (!rc) { + drm_err(dpu->drm, "dpu wait for reg update done time out!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static u32 drm_format_to_dpu(struct drm_framebuffer *fb) +{ + u32 format = 0; + + switch (fb->format->format) { + case DRM_FORMAT_BGRA8888: + /* BGRA8888 -> ARGB8888 */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; + format |= BIT_DPU_LAY_FORMAT_ARGB8888; + break; + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + /* RGBA8888 -> ABGR8888 */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; + fallthrough; + case DRM_FORMAT_ABGR8888: + /* RB switch */ + format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + fallthrough; + case DRM_FORMAT_ARGB8888: + format |= BIT_DPU_LAY_FORMAT_ARGB8888; + break; + case DRM_FORMAT_XBGR8888: + /* RB switch */ + format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + fallthrough; + case DRM_FORMAT_XRGB8888: + format |= BIT_DPU_LAY_FORMAT_ARGB8888; + break; + case DRM_FORMAT_BGR565: + /* RB switch */ + format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + fallthrough; + case DRM_FORMAT_RGB565: + format |= BIT_DPU_LAY_FORMAT_RGB565; + break; + case DRM_FORMAT_NV12: + /* 2-Lane: Yuv420 */ + format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE; + /* Y endian */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + format |= BIT_DPU_LAY_NO_SWITCH; + break; + case DRM_FORMAT_NV21: + /* 2-Lane: Yuv420 */ + format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE; + /* Y endian */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + break; + case DRM_FORMAT_NV16: + /* 2-Lane: Yuv422 */ + format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE; + /* Y endian */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; + /* UV endian */ + format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + break; + case DRM_FORMAT_NV61: + /* 2-Lane: Yuv422 */ + format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE; + /* Y endian */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + format |= BIT_DPU_LAY_NO_SWITCH; + break; + case DRM_FORMAT_YUV420: + format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE; + /* Y endian */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + format |= BIT_DPU_LAY_NO_SWITCH; + break; + case DRM_FORMAT_YVU420: + format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE; + /* Y endian */ + format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + break; + default: + break; + } + + return format; +} + +static u32 drm_rotation_to_dpu(struct drm_plane_state *state) +{ + u32 rotation = 0; + + switch (state->rotation) { + default: + case DRM_MODE_ROTATE_0: + rotation = BIT_DPU_LAY_ROTATION_0; + break; + case DRM_MODE_ROTATE_90: + rotation = BIT_DPU_LAY_ROTATION_90; + break; + case DRM_MODE_ROTATE_180: + rotation = BIT_DPU_LAY_ROTATION_180; + break; + case DRM_MODE_ROTATE_270: + rotation = BIT_DPU_LAY_ROTATION_270; + break; + case DRM_MODE_REFLECT_Y: + rotation = BIT_DPU_LAY_ROTATION_180_M; + break; + case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90): + rotation = BIT_DPU_LAY_ROTATION_90_M; + break; + case DRM_MODE_REFLECT_X: + rotation = BIT_DPU_LAY_ROTATION_0_M; + break; + case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90): + rotation = BIT_DPU_LAY_ROTATION_270_M; + break; + } + + return rotation; +} + +static u32 drm_blend_to_dpu(struct drm_plane_state *state) +{ + u32 blend = 0; + + switch (state->pixel_blend_mode) { + case DRM_MODE_BLEND_COVERAGE: + /* alpha mode select - combo alpha */ + blend |= BIT_DPU_LAY_COMBO_ALPHA; + /* Normal mode */ + blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL; + break; + case DRM_MODE_BLEND_PREMULTI: + /* alpha mode select - combo alpha */ + blend |= BIT_DPU_LAY_COMBO_ALPHA; + /* Pre-mult mode */ + blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT; + break; + case DRM_MODE_BLEND_PIXEL_NONE: + default: + /* don't do blending, maybe RGBX */ + /* alpha mode select - layer alpha */ + blend |= BIT_DPU_LAY_LAYER_ALPHA; + break; + } + + return blend; +} + +static void sprd_dpu_layer(struct sprd_dpu *dpu, struct drm_plane_state *state) +{ + struct dpu_context *ctx = &dpu->ctx; + struct drm_gem_cma_object *cma_obj; + struct drm_framebuffer *fb = state->fb; + u32 addr, size, offset, pitch, blend, format, rotation; + u32 src_x = state->src_x >> 16; + u32 src_y = state->src_y >> 16; + u32 src_w = state->src_w >> 16; + u32 src_h = state->src_h >> 16; + u32 dst_x = state->crtc_x; + u32 dst_y = state->crtc_y; + u32 alpha = state->alpha; + u32 index = state->zpos; + int i; + + offset = (dst_x & 0xffff) | (dst_y << 16); + size = (src_w & 0xffff) | (src_h << 16); + + for (i = 0; i < fb->format->num_planes; i++) { + cma_obj = drm_fb_cma_get_gem_obj(fb, i); + addr = cma_obj->paddr + fb->offsets[i]; + + if (i == 0) + layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, addr, index); + else if (i == 1) + layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, addr, index); + else + layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, addr, index); + } + + if (fb->format->num_planes == 3) { + /* UV pitch is 1/2 of Y pitch */ + pitch = (fb->pitches[0] / fb->format->cpp[0]) | + (fb->pitches[0] / fb->format->cpp[0] << 15); + } else { + pitch = fb->pitches[0] / fb->format->cpp[0]; + } + + layer_reg_wr(ctx, REG_LAY_POS, offset, index); + layer_reg_wr(ctx, REG_LAY_SIZE, size, index); + layer_reg_wr(ctx, REG_LAY_CROP_START, + src_y << 16 | src_x, index); + layer_reg_wr(ctx, REG_LAY_ALPHA, alpha, index); + layer_reg_wr(ctx, REG_LAY_PITCH, pitch, index); + + format = drm_format_to_dpu(fb); + blend = drm_blend_to_dpu(state); + rotation = drm_rotation_to_dpu(state); + + layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN | + format | + blend | + rotation, + index); +} + +static void sprd_dpu_flip(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + /* + * Make sure the dpu is in stop status. DPU has no shadow + * registers in EDPI mode. So the config registers can only be + * updated in the rising edge of DPU_RUN bit. + */ + if (ctx->if_type == SPRD_DPU_IF_EDPI) + dpu_wait_stop_done(dpu); + + /* update trigger and wait */ + if (ctx->if_type == SPRD_DPU_IF_DPI) { + if (!ctx->stopped) { + dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE); + dpu_wait_update_done(dpu); + } + + dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR); + } else if (ctx->if_type == SPRD_DPU_IF_EDPI) { + dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN); + + ctx->stopped = false; + } +} + +static void sprd_dpu_init(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + u32 int_mask = 0; + + writel(0x00, ctx->base + REG_BG_COLOR); + writel(0x00, ctx->base + REG_MMU_EN); + writel(0x00, ctx->base + REG_MMU_PPN1); + writel(0xffff, ctx->base + REG_MMU_RANGE1); + writel(0x00, ctx->base + REG_MMU_PPN2); + writel(0xffff, ctx->base + REG_MMU_RANGE2); + writel(0x1ffff, ctx->base + REG_MMU_VPN_RANGE); + + if (ctx->if_type == SPRD_DPU_IF_DPI) { + /* use dpi as interface */ + dpu_reg_clr(ctx, REG_DPU_CFG0, BIT_DPU_IF_EDPI); + /* disable Halt function for SPRD DSI */ + dpu_reg_clr(ctx, REG_DPI_CTRL, BIT_DPU_DPI_HALT_EN); + /* select te from external pad */ + dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD); + + /* enable dpu update done INT */ + int_mask |= BIT_DPU_INT_UPDATE_DONE; + /* enable dpu done INT */ + int_mask |= BIT_DPU_INT_DONE; + /* enable dpu dpi vsync */ + int_mask |= BIT_DPU_INT_VSYNC; + /* enable dpu TE INT */ + int_mask |= BIT_DPU_INT_TE; + /* enable underflow err INT */ + int_mask |= BIT_DPU_INT_ERR; + } else if (ctx->if_type == SPRD_DPU_IF_EDPI) { + /* use edpi as interface */ + dpu_reg_set(ctx, REG_DPU_CFG0, BIT_DPU_IF_EDPI); + /* use external te */ + dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_FROM_EXTERNAL_PAD); + /* enable te */ + dpu_reg_set(ctx, REG_DPI_CTRL, BIT_DPU_EDPI_TE_EN); + + /* enable stop done INT */ + int_mask |= BIT_DPU_INT_DONE; + /* enable TE INT */ + int_mask |= BIT_DPU_INT_TE; + } + + writel(int_mask, ctx->base + REG_DPU_INT_EN); +} + +static void sprd_dpu_fini(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + writel(0x00, ctx->base + REG_DPU_INT_EN); + writel(0xff, ctx->base + REG_DPU_INT_CLR); +} + +static void sprd_dpi_init(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + u32 reg_val; + u32 size; + + size = (ctx->vm.vactive << 16) | ctx->vm.hactive; + writel(size, ctx->base + REG_PANEL_SIZE); + writel(size, ctx->base + REG_BLEND_SIZE); + + if (ctx->if_type == SPRD_DPU_IF_DPI) { + /* set dpi timing */ + reg_val = ctx->vm.hsync_len << 0 | + ctx->vm.hback_porch << 8 | + ctx->vm.hfront_porch << 20; + writel(reg_val, ctx->base + REG_DPI_H_TIMING); + + reg_val = ctx->vm.vsync_len << 0 | + ctx->vm.vback_porch << 8 | + ctx->vm.vfront_porch << 20; + writel(reg_val, ctx->base + REG_DPI_V_TIMING); + } +} + +void sprd_dpu_run(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN); + + ctx->stopped = false; +} + +void sprd_dpu_stop(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + if (ctx->if_type == SPRD_DPU_IF_DPI) + dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP); + + dpu_wait_stop_done(dpu); +} + +static int sprd_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_crtc_state *crtc_state; + u32 fmt; + + if (!plane_state->fb || !plane_state->crtc) + return 0; + + fmt = drm_format_to_dpu(plane_state->fb); + if (!fmt) + return -EINVAL; + + crtc_state = drm_atomic_get_crtc_state(plane_state->state, plane_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + return drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); +} + +static void sprd_plane_atomic_update(struct drm_plane *drm_plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + drm_plane); + struct sprd_dpu *dpu = to_sprd_crtc(new_state->crtc); + + /* start configure dpu layers */ + sprd_dpu_layer(dpu, new_state); +} + +static void sprd_plane_atomic_disable(struct drm_plane *drm_plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + drm_plane); + struct sprd_dpu *dpu = to_sprd_crtc(old_state->crtc); + + layer_reg_wr(&dpu->ctx, REG_LAY_CTRL, 0x00, old_state->zpos); +} + +static void sprd_plane_create_properties(struct sprd_plane *plane, int index) +{ + unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE); + + /* create rotation property */ + drm_plane_create_rotation_property(&plane->base, + DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_MASK | + DRM_MODE_REFLECT_MASK); + + /* create alpha property */ + drm_plane_create_alpha_property(&plane->base); + + /* create blend mode property */ + drm_plane_create_blend_mode_property(&plane->base, supported_modes); + + /* create zpos property */ + drm_plane_create_zpos_immutable_property(&plane->base, index); +} + +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = { + .atomic_check = sprd_plane_atomic_check, + .atomic_update = sprd_plane_atomic_update, + .atomic_disable = sprd_plane_atomic_disable, +}; + +static const struct drm_plane_funcs sprd_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static struct sprd_plane *sprd_planes_init(struct drm_device *drm) +{ + struct sprd_plane *plane, *primary; + enum drm_plane_type plane_type; + int i; + + for (i = 0; i < 6; i++) { + plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY : + DRM_PLANE_TYPE_OVERLAY; + + plane = drmm_universal_plane_alloc(drm, struct sprd_plane, base, + 1, &sprd_plane_funcs, + layer_fmts, ARRAY_SIZE(layer_fmts), + NULL, plane_type, NULL); + if (IS_ERR(plane)) { + drm_err(drm, "failed to init drm plane: %d\n", i); + return plane; + } + + drm_plane_helper_add(&plane->base, &sprd_plane_helper_funcs); + + sprd_plane_create_properties(plane, i); + + if (i == 0) + primary = plane; + } + + return primary; +} + +static void sprd_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + + drm_display_mode_to_videomode(mode, &dpu->ctx.vm); + + sprd_dpi_init(dpu); +} + +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + + sprd_dpu_init(dpu); + + drm_crtc_vblank_on(&dpu->base); +} + +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + struct drm_device *drm = dpu->base.dev; + + drm_crtc_vblank_off(&dpu->base); + + sprd_dpu_fini(dpu); + + spin_lock_irq(&drm->event_lock); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&drm->event_lock); +} + +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) + +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + struct drm_device *drm = dpu->base.dev; + + sprd_dpu_flip(dpu); + + spin_lock_irq(&drm->event_lock); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&drm->event_lock); +} + +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + + dpu_reg_set(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC); + + return 0; +} + +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + + dpu_reg_clr(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC); +} + +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = { + .mode_set_nofb = sprd_crtc_mode_set_nofb, + .atomic_flush = sprd_crtc_atomic_flush, + .atomic_enable = sprd_crtc_atomic_enable, + .atomic_disable = sprd_crtc_atomic_disable, +}; + +static const struct drm_crtc_funcs sprd_crtc_funcs = { + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = sprd_crtc_enable_vblank, + .disable_vblank = sprd_crtc_disable_vblank, +}; + +static struct sprd_dpu *sprd_crtc_init(struct drm_device *drm, + struct drm_plane *primary, struct device *dev) +{ + struct device_node *port; + struct sprd_dpu *dpu; + + dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base, + primary, NULL, + &sprd_crtc_funcs, NULL); + if (IS_ERR(dpu)) { + drm_err(drm, "failed to init crtc\n"); + return dpu; + } + drm_crtc_helper_add(&dpu->base, &sprd_crtc_helper_funcs); + + /* + * set crtc port so that drm_of_find_possible_crtcs call works + */ + port = of_graph_get_port_by_id(dev->of_node, 0); + if (!port) { + drm_err(drm, "failed to found crtc output port for %s\n", + dev->of_node->full_name); + return ERR_PTR(-EINVAL); + } + dpu->base.port = port; + of_node_put(port); + + return dpu; +} + +static irqreturn_t sprd_dpu_isr(int irq, void *data) +{ + struct sprd_dpu *dpu = data; + struct dpu_context *ctx = &dpu->ctx; + u32 reg_val, int_mask = 0; + + reg_val = readl(ctx->base + REG_DPU_INT_STS); + + /* disable err interrupt */ + if (reg_val & BIT_DPU_INT_ERR) { + int_mask |= BIT_DPU_INT_ERR; + drm_warn(dpu->drm, "Warning: dpu underflow!\n"); + } + + /* dpu update done isr */ + if (reg_val & BIT_DPU_INT_UPDATE_DONE) { + ctx->evt_update = true; + wake_up_interruptible_all(&ctx->wait_queue); + } + + /* dpu stop done isr */ + if (reg_val & BIT_DPU_INT_DONE) { + ctx->evt_stop = true; + wake_up_interruptible_all(&ctx->wait_queue); + } + + if (reg_val & BIT_DPU_INT_VSYNC) + drm_crtc_handle_vblank(&dpu->base); + + writel(reg_val, ctx->base + REG_DPU_INT_CLR); + dpu_reg_clr(ctx, REG_DPU_INT_EN, int_mask); + + return IRQ_HANDLED; +} + +static int sprd_dpu_context_init(struct sprd_dpu *dpu, + struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dpu_context *ctx = &dpu->ctx; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!ctx->base) { + dev_err(dev, "failed to map dpu registers\n"); + return -EFAULT; + } + + ctx->irq = platform_get_irq(pdev, 0); + if (ctx->irq < 0) { + dev_err(dev, "failed to get dpu irq\n"); + return ctx->irq; + } + + /* disable and clear interrupts before register dpu IRQ. */ + writel(0x00, ctx->base + REG_DPU_INT_EN); + writel(0xff, ctx->base + REG_DPU_INT_CLR); + + ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr, + IRQF_TRIGGER_NONE, "DPU", dpu); + if (ret) { + dev_err(dev, "failed to register dpu irq handler\n"); + return ret; + } + + init_waitqueue_head(&ctx->wait_queue); + + return 0; +} + +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm = data; + struct sprd_dpu *dpu; + struct sprd_plane *plane; + int ret; + + plane = sprd_planes_init(drm); + if (IS_ERR(plane)) + return PTR_ERR(plane); + + dpu = sprd_crtc_init(drm, &plane->base, dev); + if (IS_ERR(dpu)) + return PTR_ERR(dpu); + + dpu->drm = drm; + dev_set_drvdata(dev, dpu); + + ret = sprd_dpu_context_init(dpu, dev); + if (ret) + return ret; + + return 0; +} + +static const struct component_ops dpu_component_ops = { + .bind = sprd_dpu_bind, +}; + +static const struct of_device_id dpu_match_table[] = { + { .compatible = "sprd,sharkl3-dpu" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, dpu_match_table); + +static int sprd_dpu_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &dpu_component_ops); +} + +static int sprd_dpu_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dpu_component_ops); + + return 0; +} + +struct platform_driver sprd_dpu_driver = { + .probe = sprd_dpu_probe, + .remove = sprd_dpu_remove, + .driver = { + .name = "sprd-dpu-drv", + .of_match_table = dpu_match_table, + }, +}; + +MODULE_AUTHOR("Leon He "); +MODULE_AUTHOR("Kevin Tang "); +MODULE_DESCRIPTION("Unisoc Display Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/sprd/sprd_dpu.h b/drivers/gpu/drm/sprd/sprd_dpu.h new file mode 100644 index 000000000..157a78f24 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_dpu.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#ifndef __SPRD_DPU_H__ +#define __SPRD_DPU_H__ + +#include +#include +#include +#include +#include +#include +#include