From patchwork Mon Dec 7 14:50:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11957965 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.5 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1E8A2C433FE for ; Tue, 8 Dec 2020 08:33:18 +0000 (UTC) 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 mail.kernel.org (Postfix) with ESMTPS id D1AAD23A7D for ; Tue, 8 Dec 2020 08:33:17 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D1AAD23A7D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=dri-devel-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 15C386E9C5; Tue, 8 Dec 2020 08:32:26 +0000 (UTC) Received: from mail-pf1-x42e.google.com (mail-pf1-x42e.google.com [IPv6:2607:f8b0:4864:20::42e]) by gabe.freedesktop.org (Postfix) with ESMTPS id C91136E85F for ; Mon, 7 Dec 2020 14:50:38 +0000 (UTC) Received: by mail-pf1-x42e.google.com with SMTP id i3so6619892pfd.6 for ; Mon, 07 Dec 2020 06:50:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=vrrEiyyZ4w15dxdldM0El36dYiAlZNUkE/azvm2gXww=; b=AY16VVGgkE9e7fMqvn+JJTlDm04VVbqbxGWOtyPG1a8qgcBmeTOWCAgxUcFLt1dxOo jeE5hbDZB6S+oKldBajg3LFQP/frVZDveH1rOzq7+kSfuFi1/E5mrwiqsjOIuzua7DdO Q1erIYF7qJfg+x1730gvs2DHqr911Z74cJXxQsTqXxytdCT0HpuQwOFTpE2/rF4+pgim ShsyJ1cRBIAU88WBSQIo3qYUcu5bpYNRVOx63MCyVxHTEHQ+v+8hd7i/8DMwSUmgIhXn f7r8nzTYCd6f7LrdoiF2qiGfqz5dtnYis/ewlo2gk7HjV6nUKZ9MlDtniKdWrGH6hGvX sUPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=vrrEiyyZ4w15dxdldM0El36dYiAlZNUkE/azvm2gXww=; b=lus9qCybfJuhsqtmRC70Ydwdzw61YpK1kUtV692KtggTWMZsEhpCDFehMmIcMhN/ET KKTEE4cF5bwo+fxCgT8osTAUtlSV7svJun78w2hSbqH40MnkCKRmNgNB+ncmSer2xEOn VSemAPRLw4T9L/dtfCC4hNA8x4J8hnIw/ThdhjIHbdDN9gjMXlsiOZk38KFkfD0Ivyin 47ILGkLoi87pnfsn7xnwIQIW35vEf1g6yzqMeq2Ymq05rvejhJ1QwF2gQo33YPPBHKka R1GYeFDiyMsqobXLhC8anA7cvNnK1Zxg3iLNF1g1oW+Lp9rDJDsyKdDTlzYXurRE5r48 JJSg== X-Gm-Message-State: AOAM5337embIm+MwLRSXOBg9UOyQFKwSFJb/F+yCGIOtTnfV0HeN6hzz KSFgH6xynx85usHvcne5h8GxMDE6/6oJxA== X-Google-Smtp-Source: ABdhPJzqTGwAZtfbeQlp10zIoKme4tqqT1RSN9sFTLNtmDvRWBpwawh+ZzCn2fgffRuTGWqhUq9uGw== X-Received: by 2002:a63:f708:: with SMTP id x8mr13092525pgh.369.1607352638443; Mon, 07 Dec 2020 06:50:38 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id u25sm10360249pgl.68.2020.12.07.06.50.35 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 07 Dec 2020 06:50:37 -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 Subject: [PATCH v1 1/6] dt-bindings: display: add Unisoc's drm master bindings Date: Mon, 7 Dec 2020 22:50:21 +0800 Message-Id: <1607352626-26088-2-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> References: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Tue, 08 Dec 2020 08:32:06 +0000 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: , Cc: orsonzhai@gmail.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, zhang.lyra@gmail.com MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Kevin Tang The Unisoc DRM master device is a virtual device needed to list all DPU devices or other display interface nodes that comprise the graphics subsystem Unisoc's display pipeline have several components as below description, multi display controllers and corresponding physical interfaces. For different display scenarios, dpu0 and dpu1 maybe binding to different encoder. E.g: dpu0 and dpu1 both binding to DSI for dual mipi-dsi display; dpu0 binding to DSI for primary display, and dpu1 binding to DP for external display; Cc: Orson Zhai Cc: Chunyan Zhang Signed-off-by: Kevin Tang Reviewed-by: Rob Herring --- .../display/sprd/sprd,display-subsystem.yaml | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,display-subsystem.yaml diff --git a/Documentation/devicetree/bindings/display/sprd/sprd,display-subsystem.yaml b/Documentation/devicetree/bindings/display/sprd/sprd,display-subsystem.yaml new file mode 100644 index 0000000..3d107e9 --- /dev/null +++ b/Documentation/devicetree/bindings/display/sprd/sprd,display-subsystem.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/sprd/sprd,display-subsystem.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Unisoc DRM master device + +maintainers: + - Kevin Tang + +description: | + The Unisoc DRM master device is a virtual device needed to list all + DPU devices or other display interface nodes that comprise the + graphics subsystem. + + Unisoc's display pipeline have several components as below description, + multi display controllers and corresponding physical interfaces. + For different display scenarios, dpu0 and dpu1 maybe binding to different + encoder. + + E.g: + dpu0 and dpu1 both binding to DSI for dual mipi-dsi display; + dpu0 binding to DSI for primary display, and dpu1 binding to DP for external display; + + +-----------------------------------------+ + | | + | +---------+ | + +----+ | +----+ +---------+ |DPHY/CPHY| | +------+ + | +----->+dpu0+--->+MIPI|DSI +--->+Combo +----->+Panel0| + |AXI | | +----+ +---------+ +---------+ | +------+ + | | | ^ | + | | | | | + | | | +-----------+ | + | | | | | + |APB | | +--+-+ +-----------+ +---+ | +------+ + | +----->+dpu1+--->+DisplayPort+--->+PHY+--------->+Panel1| + | | | +----+ +-----------+ +---+ | +------+ + +----+ | | + +-----------------------------------------+ + +properties: + compatible: + const: sprd,display-subsystem + + ports: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Should contain a list of phandles pointing to display interface port + of DPU devices. + +required: + - compatible + - ports + +additionalProperties: false + +examples: + - | + display-subsystem { + compatible = "sprd,display-subsystem"; + ports = <&dpu_out>; + }; + From patchwork Mon Dec 7 14:50:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11958001 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.5 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 32B3DC433FE for ; Tue, 8 Dec 2020 08:34:04 +0000 (UTC) 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 mail.kernel.org (Postfix) with ESMTPS id E56BE23A61 for ; Tue, 8 Dec 2020 08:34:03 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E56BE23A61 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=dri-devel-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5E0206E96E; Tue, 8 Dec 2020 08:32:27 +0000 (UTC) Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by gabe.freedesktop.org (Postfix) with ESMTPS id C19176E862 for ; Mon, 7 Dec 2020 14:50:42 +0000 (UTC) Received: by mail-pg1-x535.google.com with SMTP id t3so9049499pgi.11 for ; Mon, 07 Dec 2020 06:50:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=qsfbB3ftc4SWRo6fqf2S6NDmY/XvU+iKe8BxMSK5i4w=; b=cmg2T7+AJCDyGn3IeGNNxalJsZxSJ5YTqgBNJF99XffGtxxE0TDhDRhgmUDvizISlR ENtVAgVS7AIKGhipsYDOx7wDHlPIi0/ua937wOHh5MWS7H/L70eUbrBuHH+8xeui0aIM VKeSKYsJwD+H9yoyONWlhuixrOPPHBNafShf3wam08luKVJoGkpYh9OLq8ghipnigDAH McRgG3/hAv2UE++jOuc4yFIRIIKtoc/2snW0C+hu52HzZYa6ZgOq4YE1yJRTKNOYnjZW i/yXK48KTivqzAt0zi3TfkOmnxw8/Cu1s5c/BHyplYnqZrDZZ3tgmOT0xVNmjEdd76S+ Ol3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=qsfbB3ftc4SWRo6fqf2S6NDmY/XvU+iKe8BxMSK5i4w=; b=cAHitCTLS1cj9oiCNgE8juAG5MdVKvZXeePhkBNoJJj5o9p1xDGQSqcPKrcLE9M7Ma vtuUC9Wd8sIpom3EBlB5u9ybJ6H8qK8qYC8ttnZq7/ZCV0WondZnBlQP6UvWhJ3LUdEh EIchkeGrm3YEISpmfUiGBXu1j1kqBMRuvuKtBosOfC4/NjkZbQfpkCjUBjmQaeSlwRjA OWxBKau1PEoKwT9zlrcfdCl29Tm+mUeBmlPMZ3kgUcYUvX3/JdjJ70gAg39rht/Ca7ho PtLOACYvs56RThIk26jVPGnto9r3FVFagVjzqL2uqIkN92VKtTI3UZyS+V8fRteXn7RV EuYw== X-Gm-Message-State: AOAM5323xypq+iPs6dzZncDMfTmoCXhNFhKg3nTceKjqXjgMrNkRehQA DVIE4ah6rqJ/GYMqyrjJdX0= X-Google-Smtp-Source: ABdhPJyKpX2a7mvALsnTaLYQx7tlpM/+NFda76qppB4xZEX9EENG5FPCfTTvxUA69OanZtLLIaYL9Q== X-Received: by 2002:a17:902:b209:b029:d8:e821:efc6 with SMTP id t9-20020a170902b209b02900d8e821efc6mr16222157plr.5.1607352642179; Mon, 07 Dec 2020 06:50:42 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id u25sm10360249pgl.68.2020.12.07.06.50.38 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 07 Dec 2020 06:50:41 -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 Subject: [PATCH v1 2/6] drm/sprd: add Unisoc's drm kms master Date: Mon, 7 Dec 2020 22:50:22 +0800 Message-Id: <1607352626-26088-3-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> References: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Tue, 08 Dec 2020 08:32:06 +0000 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: , Cc: orsonzhai@gmail.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, zhang.lyra@gmail.com MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 internel peripherals through libdrm or libkms, it's test ok by modetest (DRM/KMS test tool) and Android HWComposer. 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 | 12 +++ drivers/gpu/drm/sprd/Makefile | 5 + drivers/gpu/drm/sprd/sprd_drm.c | 222 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_drm.h | 16 +++ 6 files changed, 258 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 147d61b..15b4e13 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -388,6 +388,8 @@ source "drivers/gpu/drm/tidss/Kconfig" source "drivers/gpu/drm/xlnx/Kconfig" +source "drivers/gpu/drm/sprd/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 8156900..d3001e7 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -124,3 +124,4 @@ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ obj-$(CONFIG_DRM_MCDE) += mcde/ obj-$(CONFIG_DRM_TIDSS) += tidss/ obj-y += xlnx/ +obj-$(CONFIG_DRM_SPRD) += sprd/ diff --git a/drivers/gpu/drm/sprd/Kconfig b/drivers/gpu/drm/sprd/Kconfig new file mode 100644 index 0000000..6e80cc9 --- /dev/null +++ b/drivers/gpu/drm/sprd/Kconfig @@ -0,0 +1,12 @@ +config DRM_SPRD + tristate "DRM Support for Unisoc SoCs Platform" + depends on ARCH_SPRD || COMPILE_TEST + depends on DRM && OF + select DRM_KMS_HELPER + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select DRM_MIPI_DSI + 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 0000000..86d95d9 --- /dev/null +++ b/drivers/gpu/drm/sprd/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +subdir-ccflags-y += -I$(srctree)/$(src) + +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 0000000..ec101de --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_drm.c @@ -0,0 +1,222 @@ +// 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 drm_device *drm = dev_get_drvdata(dev); + int ret; + + 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"); + goto err_dc_cleanup; + } + + /* 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; + } + /* with irq_enabled = true, we can use the vblank feature. */ + drm->irq_enabled = true; + + /* 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); +err_dc_cleanup: + drm_mode_config_cleanup(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); + + drm_mode_config_cleanup(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) +{ + struct device *dev = &pdev->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; + + ret = dma_set_mask_and_coherent(dev, ~0UL); + if (ret) { + drm_err(drm, "dma_set_mask_and_coherent failed (%d)\n", ret); + return ret; + } + + platform_set_drvdata(pdev, drm); + + return drm_of_component_probe(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) +{ + int ret; + + ret = platform_register_drivers(sprd_drm_drivers, + ARRAY_SIZE(sprd_drm_drivers)); + return ret; +} + +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 0000000..9781fd5 --- /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 Mon Dec 7 14:50:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11957927 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.5 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 47AA3C433FE for ; Tue, 8 Dec 2020 08:32:31 +0000 (UTC) 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 mail.kernel.org (Postfix) with ESMTPS id 00F2A23A7D for ; Tue, 8 Dec 2020 08:32:30 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 00F2A23A7D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=dri-devel-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 77B086E951; Tue, 8 Dec 2020 08:32:07 +0000 (UTC) Received: from mail-pf1-x432.google.com (mail-pf1-x432.google.com [IPv6:2607:f8b0:4864:20::432]) by gabe.freedesktop.org (Postfix) with ESMTPS id 5D2DD6E860 for ; Mon, 7 Dec 2020 14:50:46 +0000 (UTC) Received: by mail-pf1-x432.google.com with SMTP id q22so10179489pfk.12 for ; Mon, 07 Dec 2020 06:50:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Q+XDWKoV9MHFRTeqn/T5sJtop0zObfyxbDEIDUOub8M=; b=a/t4WS+UXOw4MusT8Kq3gn+Q4qwCLdI2ylQZsJMGgBF0XZuzKhh8A15IpnvNQEhmBf Jhch1zQqElf55Tv5lPhAWTwz4I7y95mdSxihEPRl3212uZU8JG+ibZBx2VHu17V9mmsv Z6z9RcCgA7yH/tklZLuiR3JM2mYlh7kLuC2BkwCgalqmYnTRCWZcTMmgiUQkZFE7uAn7 kA9H4D8+jZ/O7aPn/V8B2xTpNVeV+dwXXThZxUvLQ2UEFl9c+ExBeG1ZiuN+WkonXCyX MimL/5K0bdb9qX2EswgHkdTxvclM7mCLpMUkR+ZPT9x6YAFjj/uVVdzDc6zuw8aOvmIn kJyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Q+XDWKoV9MHFRTeqn/T5sJtop0zObfyxbDEIDUOub8M=; b=F5sXfBWb/Vx4H2y0jaTWidpk7ryHhIMpZR9YkjQES5tQBjre32TbYyKoDGUqgpQE7n Qrns5RHcezvJWRmK0fLRykDSxCgTFBaQM9g9OM6cAlOIyEEE88REEto4tW6tWSBzPDv0 KiM95schUGOkfXj+O0jHzqsQB5+sHpVbt23bFOSjiM/W8U//Yn2YWYH7yUAiQdvF46fG t6u5uP/W3jKPlmw9UzlCCZ/ORJBqc6Vy6XgkEdk7Zq98OLGhxFD3/iIZ4wvLO6TpnlUj KPleGo/RauikH48czDKPfqfm1F47TTrsYJ0JUTIYos2g9IWgdD6xOFCPyTsdU87z746a 0t/Q== X-Gm-Message-State: AOAM533onSlySNwFb9HHkfQJmTrObfIkHxkX0JAypPaTnSl8w41bwXrA t8PwdslLNrDJ0k8BzeP65N4= X-Google-Smtp-Source: ABdhPJyapXvnIlFnQSDoSn3Mv1vOkBksKLdqOkVzV4BEWWUGxXSejNhG8BAdCLx03cZ8/XO/NrCScQ== X-Received: by 2002:a05:6a00:1684:b029:18b:665e:5211 with SMTP id k4-20020a056a001684b029018b665e5211mr16718994pfc.20.1607352646011; Mon, 07 Dec 2020 06:50:46 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id u25sm10360249pgl.68.2020.12.07.06.50.42 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 07 Dec 2020 06:50:45 -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 Subject: [PATCH v1 3/6] dt-bindings: display: add Unisoc's dpu bindings Date: Mon, 7 Dec 2020 22:50:23 +0800 Message-Id: <1607352626-26088-4-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> References: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Tue, 08 Dec 2020 08:32:06 +0000 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: , Cc: orsonzhai@gmail.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, zhang.lyra@gmail.com MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Kevin Tang DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs which transfers the image data from a video memory buffer to an internal LCD interface. Cc: Orson Zhai Cc: Chunyan Zhang Signed-off-by: Kevin Tang Reviewed-by: Rob Herring --- .../bindings/display/sprd/sprd,sharkl3-dpu.yaml | 83 ++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml diff --git a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml new file mode 100644 index 0000000..a9052e1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dpu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Unisoc Sharkl3 Display Processor Unit (DPU) + +maintainers: + - Kevin Tang + +description: | + DPU (Display Processor Unit) is the Display Controller for the Unisoc SoCs + which transfers the image data from a video memory buffer to an internal + LCD interface. + +properties: + compatible: + const: sprd,sharkl3-dpu + + reg: + maxItems: 1 + description: + Physical base address and length of the DPU registers set + + interrupts: + maxItems: 1 + description: + The interrupt signal from DPU + + clocks: + minItems: 2 + + clock-names: + items: + - const: clk_src_128m + - const: clk_src_384m + + power-domains: + maxItems: 1 + description: A phandle to DPU power domain node. + + iommus: + maxItems: 1 + description: A phandle to DPU iommu node. + + port: + type: object + description: + A port node with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + That port should be the output endpoint, usually output to + the associated DSI. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - port + +additionalProperties: false + +examples: + - | + #include + #include + dpu: dpu@63000000 { + compatible = "sprd,sharkl3-dpu"; + reg = <0x63000000 0x1000>; + interrupts = ; + clock-names = "clk_src_128m", "clk_src_384m"; + + clocks = <&pll CLK_TWPLL_128M>, + <&pll CLK_TWPLL_384M>; + + dpu_port: port { + dpu_out: endpoint { + remote-endpoint = <&dsi_in>; + }; + }; + }; From patchwork Mon Dec 7 14:50:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11957975 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.5 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A4B8CC4361B for ; Tue, 8 Dec 2020 08:33:25 +0000 (UTC) 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 mail.kernel.org (Postfix) with ESMTPS id 6E68E23A7D for ; Tue, 8 Dec 2020 08:33:25 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 6E68E23A7D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=dri-devel-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B01C86E9A7; Tue, 8 Dec 2020 08:32:23 +0000 (UTC) Received: from mail-pj1-x1041.google.com (mail-pj1-x1041.google.com [IPv6:2607:f8b0:4864:20::1041]) by gabe.freedesktop.org (Postfix) with ESMTPS id 7D7976E862 for ; Mon, 7 Dec 2020 14:50:50 +0000 (UTC) Received: by mail-pj1-x1041.google.com with SMTP id j13so7579522pjz.3 for ; Mon, 07 Dec 2020 06:50:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=XxoJ/fBCpTNXMczMA7eAAjHKOEOEU3vuTs7aWQh2Gss=; b=K3NYHtyZ4SIAu/+eNz6uCtYcQkSKvzTCAlY2ImtngxjuGgSmY2YRLbAGYydCqiALYw momaNfLqQqwbJ2QlOLRBJx5h8FtJnc9u8hymQTRw+BexYO0MjNGsQhfKPYoKEr/htZUH iwQAnoiGCGdBErg0ltdGh2gWQfHLMak8AsH1Y67qVSHyeJKTBdPKPUQFqnaKZ4pkk8lb AFwlCpfF+EGnygNtpFs5SJ49G6pozwpwzniJUDWVRTokEZaQRsRVC1QwXBDBwcusmvgV 13r/d7SY9rFTOSKOcPOB4MGFpWSgGftIyEKKhiFqo8npWJgpQX0lEpHiMpXNhDxXahqq j3fQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=XxoJ/fBCpTNXMczMA7eAAjHKOEOEU3vuTs7aWQh2Gss=; b=CxIv85z0PkSzz9yWNJ7eS8qTPO85EC9A0jhfnoOVExmT41kuoUGXmuI1Kxpcj1uZWQ obrbdvn2PPB0lbEMCkR+/AWyEVLXPyityP2TUmFAtn70xvUToyQsvaeL0ZTb3O/Fv5iA JCoicnTiOVmOQxCr2g2fbIJB4C6xAekFyaoJ1fh80oOCSEBW8eq29aF0T3kMXkOG0pvE iVd7cIZ0SeieHa3I9J1+ZrzhtsN5NDzn49XEKdAgjKOjK9RmZVCTEtUZp/IabupWTDtT QrpWRdu8yInXc5cTa9BbqIJX5nSoxkq44DMpV5Z1SiwA4PVdh6xT4IhrwWrLGYCPTnVR FqdQ== X-Gm-Message-State: AOAM531Q45nXHe1VunuJkRwOV9zk9ixZEtUO+Tw8zKd46DLaKITEQuzR AMbGn+aHazED+p9pE2K6Z2E= X-Google-Smtp-Source: ABdhPJxhlRWICdSXvBPLAMagPUnbdJy4FcCai9+srIYc7o8cdhtvg9+imq0Nv7tLY8FpeuRIcMC7fw== X-Received: by 2002:a17:90a:ee8e:: with SMTP id i14mr16791163pjz.190.1607352649872; Mon, 07 Dec 2020 06:50:49 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id u25sm10360249pgl.68.2020.12.07.06.50.46 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 07 Dec 2020 06:50:49 -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 Subject: [PATCH v1 4/6] drm/sprd: add Unisoc's drm display controller driver Date: Mon, 7 Dec 2020 22:50:24 +0800 Message-Id: <1607352626-26088-5-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> References: <1607352626-26088-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Tue, 08 Dec 2020 08:32:06 +0000 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: , Cc: orsonzhai@gmail.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, zhang.lyra@gmail.com MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Adds DPU(Display Processor Unit) support for the Unisoc's display subsystem. It's support multi planes, scaler, rotation, PQ(Picture Quality) and more. Cc: Orson Zhai Cc: Chunyan Zhang Signed-off-by: Kevin Tang --- drivers/gpu/drm/sprd/Kconfig | 1 + drivers/gpu/drm/sprd/Makefile | 6 +- drivers/gpu/drm/sprd/dpu_r2p0.c | 598 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_dpu.c | 457 ++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_dpu.h | 175 ++++++++++++ drivers/gpu/drm/sprd/sprd_drm.c | 1 + drivers/gpu/drm/sprd/sprd_drm.h | 2 + 7 files changed, 1238 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/sprd/dpu_r2p0.c 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 6e80cc9..9b4ef9a 100644 --- a/drivers/gpu/drm/sprd/Kconfig +++ b/drivers/gpu/drm/sprd/Kconfig @@ -3,6 +3,7 @@ config DRM_SPRD depends on ARCH_SPRD || COMPILE_TEST depends on DRM && OF select DRM_KMS_HELPER + select VIDEOMODE_HELPERS select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER select DRM_MIPI_DSI diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile index 86d95d9..0ba48f2 100644 --- a/drivers/gpu/drm/sprd/Makefile +++ b/drivers/gpu/drm/sprd/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -subdir-ccflags-y += -I$(srctree)/$(src) +obj-y := sprd_drm.o \ + sprd_dpu.o + +obj-y += dpu_r2p0.o -obj-y := sprd_drm.o diff --git a/drivers/gpu/drm/sprd/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu_r2p0.c new file mode 100644 index 0000000..46ab8b3 --- /dev/null +++ b/drivers/gpu/drm/sprd/dpu_r2p0.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#include +#include +#include +#include + +#include "sprd_dpu.h" + +/* Global control registers */ +#define REG_DPU_CTRL 0x04 +#define REG_DPU_CFG0 0x08 +#define REG_DPU_CFG1 0x0C +#define REG_DPU_CFG2 0x10 +#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_PALLETE 0x58 +#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_VAOR_ADDR_RD 0x818 +#define REG_MMU_VAOR_ADDR_WR 0x81C +#define REG_MMU_INV_ADDR_RD 0x820 +#define REG_MMU_INV_ADDR_WR 0x824 +#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) +#define BIT_DPU_COEF_NARROW_RANGE BIT(4) +#define BIT_DPU_Y2R_COEF_ITU709_STANDARD BIT(5) + +/* 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) + +/* 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) +#define BIT_DPU_INT_FBC_PLD_ERR BIT(8) +#define BIT_DPU_INT_FBC_HDR_ERR BIT(9) +#define BIT_DPU_INT_MMU_VAOR_RD BIT(16) +#define BIT_DPU_INT_MMU_VAOR_WR BIT(17) +#define BIT_DPU_INT_MMU_INV_RD BIT(18) +#define BIT_DPU_INT_MMU_INV_WR BIT(19) + +/* 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 primary_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, +}; + +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val) +{ + u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD | + BIT_DPU_INT_MMU_VAOR_WR | + BIT_DPU_INT_MMU_INV_RD | + BIT_DPU_INT_MMU_INV_WR; + u32 val = reg_val & mmu_mask; + int i; + + if (val) { + DRM_ERROR("--- iommu interrupt err: 0x%04x ---\n", val); + + if (val & BIT_DPU_INT_MMU_INV_RD) + DRM_ERROR("iommu invalid read error, addr: 0x%08x\n", + readl_relaxed(ctx->base + REG_MMU_INV_ADDR_RD)); + if (val & BIT_DPU_INT_MMU_INV_WR) + DRM_ERROR("iommu invalid write error, addr: 0x%08x\n", + readl_relaxed(ctx->base + REG_MMU_INV_ADDR_WR)); + if (val & BIT_DPU_INT_MMU_VAOR_RD) + DRM_ERROR("iommu va out of range read error, addr: 0x%08x\n", + readl_relaxed(ctx->base + REG_MMU_VAOR_ADDR_RD)); + if (val & BIT_DPU_INT_MMU_VAOR_WR) + DRM_ERROR("iommu va out of range write error, addr: 0x%08x\n", + readl_relaxed(ctx->base + REG_MMU_VAOR_ADDR_WR)); + + for (i = 0; i < 8; i++) { + reg_val = layer_reg_rd(ctx, REG_LAY_CTRL, i); + if (reg_val & 0x1) + DRM_INFO("layer%d: 0x%08x 0x%08x 0x%08x ctrl: 0x%08x\n", i, + layer_reg_rd(ctx, REG_LAY_BASE_ADDR0, i), + layer_reg_rd(ctx, REG_LAY_BASE_ADDR1, i), + layer_reg_rd(ctx, REG_LAY_BASE_ADDR2, i), + layer_reg_rd(ctx, REG_LAY_CTRL, i)); + } + } + + return val; +} + +static void dpu_clean_all(struct dpu_context *ctx) +{ + int i; + + for (i = 0; i < 8; i++) + layer_reg_wr(ctx, REG_LAY_CTRL, 0x00, i); +} + +static int dpu_wait_stop_done(struct dpu_context *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_ERROR("dpu wait for stop done time out!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int dpu_wait_update_done(struct dpu_context *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_ERROR("dpu wait for reg update done time out!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void dpu_layer(struct dpu_context *ctx, struct dpu_layer *hwlayer) +{ + u32 size, offset, ctrl; + int i; + + offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16); + size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16); + + for (i = 0; i < hwlayer->planes; i++) + layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, hwlayer->addr[i], hwlayer->index); + + layer_reg_wr(ctx, REG_LAY_POS, offset, hwlayer->index); + layer_reg_wr(ctx, REG_LAY_SIZE, size, hwlayer->index); + layer_reg_wr(ctx, REG_LAY_CROP_START, + hwlayer->src_y << 16 | hwlayer->src_x, hwlayer->index); + layer_reg_wr(ctx, REG_LAY_ALPHA, hwlayer->alpha, hwlayer->index); + layer_reg_wr(ctx, REG_LAY_PITCH, hwlayer->pitch, hwlayer->index); + + ctrl = hwlayer->format | + hwlayer->blending | + (hwlayer->rotation & 0x7) << 20; + + layer_reg_wr(ctx, REG_LAY_CTRL, ctrl | BIT_DPU_LAY_EN, hwlayer->index); +} + +u32 sprd_dpu_isr(struct dpu_context *ctx) +{ + u32 reg_val, int_mask = 0; + + reg_val = readl_relaxed(ctx->base + REG_DPU_INT_STS); + + /* disable err interrupt */ + if (reg_val & BIT_DPU_INT_ERR) + int_mask |= BIT_DPU_INT_ERR; + + /* 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); + } + + /* dpu ifbc payload error isr */ + if (reg_val & BIT_DPU_INT_FBC_PLD_ERR) { + int_mask |= BIT_DPU_INT_FBC_PLD_ERR; + DRM_ERROR("dpu ifbc payload error\n"); + } + + /* dpu ifbc header error isr */ + if (reg_val & BIT_DPU_INT_FBC_HDR_ERR) { + int_mask |= BIT_DPU_INT_FBC_HDR_ERR; + DRM_ERROR("dpu ifbc header error\n"); + } + + int_mask |= check_mmu_isr(ctx, reg_val); + + writel_relaxed(reg_val, ctx->base + REG_DPU_INT_CLR); + dpu_reg_clr(ctx, REG_DPU_INT_EN, int_mask); + + return reg_val; +} + +int sprd_dpu_img_ctrl(struct dpu_layer *layer, u32 format, u32 angle, u32 blend) +{ + switch (format) { + case DRM_FORMAT_BGRA8888: + /* BGRA8888 -> ARGB8888 */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; + layer->format |= BIT_DPU_LAY_FORMAT_ARGB8888; + break; + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + /* RGBA8888 -> ABGR8888 */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; + /* FALLTHRU */ + case DRM_FORMAT_ABGR8888: + /* RB switch */ + layer->format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + /* FALLTHRU */ + case DRM_FORMAT_ARGB8888: + layer->format |= BIT_DPU_LAY_FORMAT_ARGB8888; + break; + case DRM_FORMAT_XBGR8888: + /* RB switch */ + layer->format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + /* FALLTHRU */ + case DRM_FORMAT_XRGB8888: + layer->format |= BIT_DPU_LAY_FORMAT_ARGB8888; + break; + case DRM_FORMAT_BGR565: + /* RB switch */ + layer->format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + /* FALLTHRU */ + case DRM_FORMAT_RGB565: + layer->format |= BIT_DPU_LAY_FORMAT_RGB565; + break; + case DRM_FORMAT_NV12: + /* 2-Lane: Yuv420 */ + layer->format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE; + /* Y endian */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + layer->format |= BIT_DPU_LAY_NO_SWITCH; + break; + case DRM_FORMAT_NV21: + /* 2-Lane: Yuv420 */ + layer->format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE; + /* Y endian */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + layer->format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + break; + case DRM_FORMAT_NV16: + /* 2-Lane: Yuv422 */ + layer->format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE; + /* Y endian */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0; + /* UV endian */ + layer->format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + break; + case DRM_FORMAT_NV61: + /* 2-Lane: Yuv422 */ + layer->format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE; + /* Y endian */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + layer->format |= BIT_DPU_LAY_NO_SWITCH; + break; + case DRM_FORMAT_YUV420: + layer->format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE; + /* Y endian */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + layer->format |= BIT_DPU_LAY_NO_SWITCH; + break; + case DRM_FORMAT_YVU420: + layer->format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE; + /* Y endian */ + layer->format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3; + /* UV endian */ + layer->format |= BIT_DPU_LAY_RB_OR_UV_SWITCH; + break; + default: + DRM_ERROR("invalid format %c%c%c%c\n", format, + format >> 8, + format >> 16, + format >> 24); + return -EINVAL; + } + + switch (angle) { + case DRM_MODE_ROTATE_0: + layer->rotation = DPU_LAYER_ROTATION_0; + break; + case DRM_MODE_ROTATE_90: + layer->rotation = DPU_LAYER_ROTATION_90; + break; + case DRM_MODE_ROTATE_180: + layer->rotation = DPU_LAYER_ROTATION_180; + break; + case DRM_MODE_ROTATE_270: + layer->rotation = DPU_LAYER_ROTATION_270; + break; + case DRM_MODE_REFLECT_Y: + layer->rotation = DPU_LAYER_ROTATION_180_M; + break; + case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90): + layer->rotation = DPU_LAYER_ROTATION_90_M; + break; + case DRM_MODE_REFLECT_X: + layer->rotation = DPU_LAYER_ROTATION_0_M; + break; + case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90): + layer->rotation = DPU_LAYER_ROTATION_270_M; + break; + default: + DRM_ERROR("rotation convert unsupport angle (drm)= 0x%x\n", angle); + return -EINVAL; + } + + switch (blend) { + case DRM_MODE_BLEND_COVERAGE: + /* alpha mode select - combo alpha */ + layer->blending |= BIT_DPU_LAY_COMBO_ALPHA; + /* Normal mode */ + layer->blending |= BIT_DPU_LAY_MODE_BLEND_NORMAL; + break; + case DRM_MODE_BLEND_PREMULTI: + /* alpha mode select - combo alpha */ + layer->blending |= BIT_DPU_LAY_COMBO_ALPHA; + /* Pre-mult mode */ + layer->blending |= 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 */ + layer->blending |= BIT_DPU_LAY_LAYER_ALPHA; + break; + } + + return 0; +} + +void sprd_dpu_flip(struct dpu_context *ctx, + struct dpu_layer layers[], u8 count) +{ + int i; + u32 reg_val; + + /* + * Make sure the dpu is in stop status. DPU_R2P0 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(ctx); + + /* reset the bgcolor to black */ + writel_relaxed(0x00, ctx->base + REG_BG_COLOR); + + /* disable all the layers */ + dpu_clean_all(ctx); + + /* start configure dpu layers */ + for (i = 0; i < count; i++) + dpu_layer(ctx, &layers[i]); + + /* 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(ctx); + } + + 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; + } + + /* + * If the following interrupt was disabled in isr, + * re-enable it. + */ + reg_val = BIT_DPU_INT_FBC_PLD_ERR | + BIT_DPU_INT_FBC_HDR_ERR | + BIT_DPU_INT_MMU_VAOR_RD | + BIT_DPU_INT_MMU_VAOR_WR | + BIT_DPU_INT_MMU_INV_RD | + BIT_DPU_INT_MMU_INV_WR; + dpu_reg_set(ctx, REG_DPU_INT_EN, reg_val); +} + +void sprd_dpu_init(struct dpu_context *ctx) +{ + u32 reg_val, size; + + writel_relaxed(0x00, ctx->base + REG_BG_COLOR); + + size = (ctx->vm.vactive << 16) | ctx->vm.hactive; + + writel_relaxed(size, ctx->base + REG_PANEL_SIZE); + writel_relaxed(size, ctx->base + REG_BLEND_SIZE); + + reg_val = BIT_DPU_COEF_NARROW_RANGE | BIT_DPU_Y2R_COEF_ITU709_STANDARD; + writel_relaxed(reg_val, ctx->base + REG_DPU_CFG0); + writel_relaxed(0x004466da, ctx->base + REG_DPU_CFG1); + writel_relaxed(0x00, ctx->base + REG_DPU_CFG2); + + if (ctx->stopped) + dpu_clean_all(ctx); + + writel_relaxed(0x00, ctx->base + REG_MMU_EN); + writel_relaxed(0x00, ctx->base + REG_MMU_PPN1); + writel_relaxed(0xffff, ctx->base + REG_MMU_RANGE1); + writel_relaxed(0x00, ctx->base + REG_MMU_PPN2); + writel_relaxed(0xffff, ctx->base + REG_MMU_RANGE2); + writel_relaxed(0x1ffff, ctx->base + REG_MMU_VPN_RANGE); + + writel_relaxed(0xffff, ctx->base + REG_DPU_INT_CLR); +} + +void sprd_dpu_fini(struct dpu_context *ctx) +{ + writel_relaxed(0x00, ctx->base + REG_DPU_INT_EN); + writel_relaxed(0xff, ctx->base + REG_DPU_INT_CLR); +} + +void sprd_dpi_init(struct dpu_context *ctx) +{ + u32 int_mask = 0; + u32 reg_val; + + 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); + + /* set dpi timing */ + reg_val = ctx->vm.hsync_len << 0 | + ctx->vm.hback_porch << 8 | + ctx->vm.hfront_porch << 20; + writel_relaxed(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_relaxed(reg_val, ctx->base + REG_DPI_V_TIMING); + + if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32) + DRM_WARN("Warning: (vsync + vbp) < 32, " + "underflow risk!\n"); + + /* 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; + } + + /* enable ifbc payload error INT */ + int_mask |= BIT_DPU_INT_FBC_PLD_ERR; + /* enable ifbc header error INT */ + int_mask |= BIT_DPU_INT_FBC_HDR_ERR; + /* enable iommu va out of range read error INT */ + int_mask |= BIT_DPU_INT_MMU_VAOR_RD; + /* enable iommu va out of range write error INT */ + int_mask |= BIT_DPU_INT_MMU_VAOR_WR; + /* enable iommu invalid read error INT */ + int_mask |= BIT_DPU_INT_MMU_INV_RD; + /* enable iommu invalid write error INT */ + int_mask |= BIT_DPU_INT_MMU_INV_WR; + + writel_relaxed(int_mask, ctx->base + REG_DPU_INT_EN); +} + +void sprd_dpu_run(struct dpu_context *ctx) +{ + dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN); + + ctx->stopped = false; +} + +void sprd_dpu_stop(struct dpu_context *ctx) +{ + if (ctx->if_type == SPRD_DPU_IF_DPI) + dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP); + + dpu_wait_stop_done(ctx); +} + +void sprd_dpu_enable_vsync(struct dpu_context *ctx) +{ + dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC); +} + +void sprd_dpu_disable_vsync(struct dpu_context *ctx) +{ + dpu_reg_clr(ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC); +} + +void sprd_dpu_capability(struct dpu_context *ctx, struct dpu_capability *cap) +{ + cap->max_layers = 6; + cap->fmts_ptr = primary_fmts; + cap->fmts_cnt = ARRAY_SIZE(primary_fmts); +} diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c new file mode 100644 index 0000000..1aa2a34 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_dpu.c @@ -0,0 +1,457 @@ +// 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 "sprd_drm.h" +#include "sprd_dpu.h" + +struct sprd_plane { + struct drm_plane plane; + u32 index; +}; + +static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane) +{ + return container_of(plane, struct sprd_plane, plane); +} + +static int sprd_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct sprd_plane *p = to_sprd_plane(plane); + struct drm_framebuffer *fb = state->fb; + struct drm_crtc_state *crtc_state; + struct drm_gem_cma_object *cma_obj; + const struct drm_format_info *info; + struct sprd_dpu *dpu; + struct dpu_layer *layer; + int i, ret; + u32 addr; + + if (!fb || !state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + dpu = to_sprd_crtc(state->crtc); + layer = &dpu->layers[p->index]; + + ret = sprd_dpu_img_ctrl(layer, fb->format->format, + state->rotation, state->pixel_blend_mode); + if (ret) + return ret; + + layer->index = p->index; + layer->src_x = state->src_x >> 16; + layer->src_y = state->src_y >> 16; + layer->src_w = state->src_w >> 16; + layer->src_h = state->src_h >> 16; + layer->dst_x = state->crtc_x; + layer->dst_y = state->crtc_y; + layer->alpha = state->alpha; + layer->planes = fb->format->num_planes; + + 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 (addr % 16) { + DRM_ERROR("layer addr[%d] is not 16 bytes align, it's 0x%08x\n", + i, addr); + return -EFAULT; + } + + layer->addr[i] = addr; + } + + info = drm_format_info(fb->format->format); + if (fb->format->num_planes == 3) { + /* UV pitch is 1/2 of Y pitch */ + layer->pitch = (fb->pitches[0] / info->cpp[0]) | + (fb->pitches[0] / info->cpp[0] << 15); + } else { + layer->pitch = fb->pitches[0] / info->cpp[0]; + } + + dpu->pending_planes++; + + return 0; +} + +/* plane doesn't represent a real HW, so there is no HW update for plane. + * dpu handles all the HW update in crtc->atomic_flush + */ +static void sprd_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + DRM_DEBUG("%s()\n", __func__); +} + +static void sprd_plane_create_properties(struct sprd_plane *p, 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(&p->plane, + DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_MASK | + DRM_MODE_REFLECT_MASK); + + /* create alpha property */ + drm_plane_create_alpha_property(&p->plane); + + /* create blend mode property */ + drm_plane_create_blend_mode_property(&p->plane, supported_modes); + + /* create zpos property */ + drm_plane_create_zpos_immutable_property(&p->plane, index); +} + +static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = { + .atomic_check = sprd_plane_atomic_check, + .atomic_update = sprd_plane_atomic_update, +}; + +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 drm_plane *sprd_plane_init(struct drm_device *drm, + struct sprd_dpu *dpu) +{ + struct drm_plane *primary = NULL; + struct sprd_plane *p = NULL; + struct dpu_capability cap = {}; + int ret, i; + + sprd_dpu_capability(&dpu->ctx, &cap); + + dpu->layers = devm_kcalloc(drm->dev, cap.max_layers, + sizeof(struct dpu_layer), GFP_KERNEL); + if (!dpu->layers) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < cap.max_layers; i++) { + + p = devm_kzalloc(drm->dev, sizeof(*p), GFP_KERNEL); + if (!p) + return ERR_PTR(-ENOMEM); + + ret = drm_universal_plane_init(drm, &p->plane, 1, + &sprd_plane_funcs, cap.fmts_ptr, + cap.fmts_cnt, NULL, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("fail to init primary plane\n"); + return ERR_PTR(ret); + } + + drm_plane_helper_add(&p->plane, &sprd_plane_helper_funcs); + + sprd_plane_create_properties(p, i); + + p->index = i; + if (i == 0) + primary = &p->plane; + } + + return primary; +} + +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + + DRM_DEBUG("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode)); + + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + drm_display_mode_to_videomode(mode, &dpu->ctx.vm); + + if ((mode->hdisplay == mode->htotal) || + (mode->vdisplay == mode->vtotal)) + dpu->ctx.if_type = SPRD_DPU_IF_EDPI; + else + dpu->ctx.if_type = SPRD_DPU_IF_DPI; + } + + return MODE_OK; +} + +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + + sprd_dpu_init(&dpu->ctx); + + sprd_dpi_init(&dpu->ctx); + + enable_irq(dpu->ctx.irq); + + drm_crtc_vblank_on(&dpu->crtc); +} + +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + struct drm_device *drm = dpu->crtc.dev; + + drm_crtc_vblank_off(&dpu->crtc); + + disable_irq(dpu->ctx.irq); + + sprd_dpu_fini(&dpu->ctx); + + 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_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + DRM_DEBUG("%s()\n", __func__); + + return 0; +} + +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) + +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + struct drm_device *drm = dpu->crtc.dev; + + if (dpu->pending_planes) + sprd_dpu_flip(&dpu->ctx, dpu->layers, dpu->pending_planes); + + 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); + + dpu->pending_planes = 0; +} + +static int sprd_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + + sprd_dpu_enable_vsync(&dpu->ctx); + + return 0; +} + +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = to_sprd_crtc(crtc); + + sprd_dpu_disable_vsync(&dpu->ctx); +} + +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = { + .mode_valid = sprd_crtc_mode_valid, + .atomic_check = sprd_crtc_atomic_check, + .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 int sprd_crtc_init(struct drm_device *drm, struct drm_crtc *crtc, + struct drm_plane *primary) +{ + struct device_node *port; + int ret; + + /* + * set crtc port so that drm_of_find_possible_crtcs call works + */ + port = of_parse_phandle(drm->dev->of_node, "ports", 0); + if (!port) { + DRM_ERROR("find 'ports' phandle of %s failed\n", + drm->dev->of_node->full_name); + return -EINVAL; + } + of_node_put(port); + crtc->port = port; + + ret = drm_crtc_init_with_planes(drm, crtc, primary, NULL, + &sprd_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("failed to init crtc.\n"); + return ret; + } + + drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs); + + return 0; +} + +static irqreturn_t sprd_dpu_isr_handler(int irq, void *data) +{ + struct sprd_dpu *dpu = data; + struct dpu_context *ctx = &dpu->ctx; + u32 int_mask = 0; + + int_mask = sprd_dpu_isr(ctx); + + if (int_mask & BIT_DPU_INT_ERR) + DRM_WARN("Warning: dpu underflow!\n"); + + if (int_mask & BIT_DPU_INT_VSYNC) + drm_crtc_handle_vblank(&dpu->crtc); + + return IRQ_HANDLED; +} + +static int sprd_dpu_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm = data; + struct sprd_dpu *dpu = dev_get_drvdata(dev); + struct drm_plane *plane; + int ret; + + plane = sprd_plane_init(drm, dpu); + if (IS_ERR_OR_NULL(plane)) { + ret = PTR_ERR(plane); + return ret; + } + + ret = sprd_crtc_init(drm, &dpu->crtc, plane); + if (ret) + return ret; + + return 0; +} + +static void sprd_dpu_unbind(struct device *dev, struct device *master, + void *data) +{ + struct sprd_dpu *dpu = dev_get_drvdata(dev); + + drm_crtc_cleanup(&dpu->crtc); +} + +static const struct component_ops dpu_component_ops = { + .bind = sprd_dpu_bind, + .unbind = sprd_dpu_unbind, +}; + +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; + } + + irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN); + ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr_handler, + 0, "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 const struct of_device_id dpu_match_table[] = { + { .compatible = "sprd,sharkl3-dpu" }, + { /* sentinel */ }, +}; + +static int sprd_dpu_probe(struct platform_device *pdev) +{ + struct sprd_dpu *dpu; + int ret; + + dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL); + if (!dpu) + return -ENOMEM; + + ret = sprd_dpu_context_init(dpu, &pdev->dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, dpu); + + 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 0000000..3f64f4c --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_dpu.h @@ -0,0 +1,175 @@ +/* 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