From patchwork Fri Feb 21 07:48:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11396263 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1149614BC for ; Fri, 21 Feb 2020 11:15:38 +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 E3D54207FD for ; Fri, 21 Feb 2020 11:15:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cH/zAuRy" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E3D54207FD 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 BDB106F417; Fri, 21 Feb 2020 11:14:30 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-pf1-x441.google.com (mail-pf1-x441.google.com [IPv6:2607:f8b0:4864:20::441]) by gabe.freedesktop.org (Postfix) with ESMTPS id EA66A6EEB2 for ; Fri, 21 Feb 2020 07:49:06 +0000 (UTC) Received: by mail-pf1-x441.google.com with SMTP id 4so759402pfz.9 for ; Thu, 20 Feb 2020 23:49:06 -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=InvFUNRiPxXOxYMHgJ0FZcGWdDLWenQO6uankp4X5co=; b=cH/zAuRygLTMG5vrXLh0wRpvYslN0/4eyqLnEeEXbOuFqjKLYJZ4wXZDafrsK1t2nD NJPOaztBE/WnSneu7MnJ/S+IOwRQyQQr8k4Ysq/Fe445TcDqi8e4KnNr61GGsd3Lmaeo tD4eRVCtWw8s5Pwg4fJWwjXl2ERLdyccnuofZ7HliCVa73JDx1Se5x16ep5dxSj2g/AG xKdwewHFN4V61X4jt/SiKOpjN7xyDxk6tMK9LJO31uwxfiR4ufK3S45/prJwzfdlMY8l qdJWX8NRes7TGzzfZtTDFHAm6C5kTlrVofd9xCXLx+wlOXQK20RRzEkyBKSPixaFxxuH FDWQ== 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=InvFUNRiPxXOxYMHgJ0FZcGWdDLWenQO6uankp4X5co=; b=ldJM759UZirfiqk90BVw788dTWI7vPxdCJ0vfmTKpxGmT090pEKFXvHvtkHeUrpQye /fBa7ENamFZ5PWzzPrYI1EsvsrKu+UPr62IptL1LZIYXva6mnT03MKj71tppHTX7bKN3 Hm4gmBtGFVRCWMhhYFCJfd8IsoNpkgjqW5HEQMKGeVWcFunRRs9e6oyTWM64EzsS0bLu kZ65Fhd6f21p3AbI9naLrP7nTn277ufxH/ryVcZ6l1OYgXQmpoa38c7aAAoUcsHjpDXO Up59UwakuIV3KtbaU00+tJMSTakJ6pA0fxUnH7kZgP7Iaw6OL8vkwsk5feDT7nydhvVv QMkA== X-Gm-Message-State: APjAAAWhiMw0m9vYdFvNeWZIpIlFoZrWt+Tl2LBNGAjWfRmyyFFzf7k2 12TzpIP4859S03DG+k3D5Sg= X-Google-Smtp-Source: APXvYqzrktlCrULOant8+rekVokatvRDsgDVLBnEil8PQxbkujjyDkYWFqsKwTCWKS7ERFp5nwkHUQ== X-Received: by 2002:a63:42c2:: with SMTP id p185mr39122753pga.268.1582271346641; Thu, 20 Feb 2020 23:49:06 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id d1sm1444653pgj.79.2020.02.20.23.49.03 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 20 Feb 2020 23:49:06 -0800 (PST) From: Kevin Tang To: airlied@linux.ie, daniel@ffwll.ch, robh+dt@kernel.org, mark.rutland@arm.com, kevin3.tang@gmail.com Subject: [PATCH RFC v3 1/6] dt-bindings: display: add Unisoc's drm master bindings Date: Fri, 21 Feb 2020 15:48:51 +0800 Message-Id: <1582271336-3708-2-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> References: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Fri, 21 Feb 2020 11:14:23 +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, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, zhang.lyra@gmail.com, baolin.wang@linaro.org 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 Cc: Orson Zhai Cc: Baolin Wang Cc: Chunyan Zhang Signed-off-by: Kevin Tang --- .../devicetree/bindings/display/sprd/drm.yaml | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/sprd/drm.yaml diff --git a/Documentation/devicetree/bindings/display/sprd/drm.yaml b/Documentation/devicetree/bindings/display/sprd/drm.yaml new file mode 100644 index 0000000..1614ca6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/sprd/drm.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/sprd/drm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Unisoc DRM master device + +maintainers: + - David Airlie + - Daniel Vetter + - Rob Herring + - Mark Rutland + +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. + +properties: + compatible: + const: sprd,display-subsystem + + ports: + description: + Should contain a list of phandles pointing to display interface port + of DPU devices. + +required: + - compatible + - ports + +examples: + - | + display-subsystem { + compatible = "sprd,display-subsystem"; + ports = <&dpu_out>; + }; \ No newline at end of file From patchwork Fri Feb 21 07:48:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11396267 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DAA7A14E3 for ; Fri, 21 Feb 2020 11:15:44 +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 B925C207FD for ; Fri, 21 Feb 2020 11:15:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ln1nOVrN" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B925C207FD 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 C73126F410; Fri, 21 Feb 2020 11:15:24 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-pl1-x641.google.com (mail-pl1-x641.google.com [IPv6:2607:f8b0:4864:20::641]) by gabe.freedesktop.org (Postfix) with ESMTPS id 790EC6EEB2 for ; Fri, 21 Feb 2020 07:49:11 +0000 (UTC) Received: by mail-pl1-x641.google.com with SMTP id a6so504981plm.3 for ; Thu, 20 Feb 2020 23:49:11 -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=dD45bZgMHI429x1lGSzhYtYWYGFGNq9cj3RiULU4S5E=; b=ln1nOVrNcJkoyPW2BryBJmlC0HJdJT0es/jFVMLqA/txZZOHS1SV8iLTeNwlgwtCBa g4ywya0iqdxp2zDuIdI6YapNjVexec9gu2RgxUYpLmQfKw/xvuSYaVl5zwFu015W5FQS o/EcEH8g0X/9oaWXgyJGztZlYmcrDpPqn9q4rAVSh0tnl2UZ+QHOSe9CfIOddxDrp9lj B6Gr5kWMSqner8Hh4ug0COmGn9ZktxDzRxZPvIE7EE5TXIupFmCvEVa6Wju2h3l1zOSc Bngu5WAl1XzvWBJlEEgFaaFUmHYpKHQZ9a4Yc0VESlnhA34Ycb60/kRXM6HpiwluQPVM REVg== 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=dD45bZgMHI429x1lGSzhYtYWYGFGNq9cj3RiULU4S5E=; b=bbqs2rKfnbiPsut4VMZRK/GwyJgd5t+GdDRueug85bk7Wr/wfNNdI1qVSjng0CwUcf f+3QEC2JcJBE5aXV+YtCRTIrjSAob9JC9kpMOtD0rDknhu7ZU3L6OApZhHc3oveJ9POa tfSTOLyCOBnSw2uSn211s27W182ZrLIEbu+MJrUJl3zMmZioshI4S9dOjW8RUCAVQviL GmpOXW5WVb0PkafXoxTytI7gC/PSLUlIl6kH2EtIGYKmmUX9juq3N2F0OLURsQI4cqsE zQuByheRw0Bxgj2xTSbEk3yBma78CfV83GppsVmrrYj+Lj7wyQ3w5l3PjZqbYWCQzX11 MP3Q== X-Gm-Message-State: APjAAAVR1wnH+Zvw04YPR0YwMpoWcCIh13HtXIrWyKrlG1fDmETQDdrI EqdmvvHkQ07zmXCjbNNDXpA= X-Google-Smtp-Source: APXvYqxRSWB2+uzILLKzFy8/DuQMduAXucDkKIE3juDXWyRyZbeTu9GSqrAUrUYtc6K7Io2Zyahsdg== X-Received: by 2002:a17:902:8604:: with SMTP id f4mr36166983plo.278.1582271351066; Thu, 20 Feb 2020 23:49:11 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id d1sm1444653pgj.79.2020.02.20.23.49.07 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 20 Feb 2020 23:49:09 -0800 (PST) From: Kevin Tang To: airlied@linux.ie, daniel@ffwll.ch, robh+dt@kernel.org, mark.rutland@arm.com, kevin3.tang@gmail.com Subject: [PATCH RFC v3 2/6] drm/sprd: add Unisoc's drm kms master Date: Fri, 21 Feb 2020 15:48:52 +0800 Message-Id: <1582271336-3708-3-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> References: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Fri, 21 Feb 2020 11:14:23 +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, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, zhang.lyra@gmail.com, baolin.wang@linaro.org MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Kevin Tang Adds drm support for the Unisoc's display subsystem. This is drm device and gem driver. This driver provides support for the Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. Cc: Orson Zhai Cc: Baolin Wang Cc: Chunyan Zhang Signed-off-by: Kevin Tang Reviewed-by: Emil Velikov --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/sprd/Kconfig | 14 ++ drivers/gpu/drm/sprd/Makefile | 7 + drivers/gpu/drm/sprd/sprd_drm.c | 292 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_drm.h | 16 +++ 6 files changed, 332 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 bfdadc3..cead12c 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -387,6 +387,8 @@ source "drivers/gpu/drm/aspeed/Kconfig" source "drivers/gpu/drm/mcde/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 9f1c7c4..85ca211 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -122,3 +122,4 @@ obj-$(CONFIG_DRM_LIMA) += lima/ obj-$(CONFIG_DRM_PANFROST) += panfrost/ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ obj-$(CONFIG_DRM_MCDE) += mcde/ +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..79f286b --- /dev/null +++ b/drivers/gpu/drm/sprd/Kconfig @@ -0,0 +1,14 @@ +config DRM_SPRD + tristate "DRM Support for Unisoc SoCs Platform" + depends on ARCH_SPRD + depends on DRM && OF + select DRM_KMS_HELPER + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select DRM_MIPI_DSI + select DRM_PANEL + select VIDEOMODE_HELPERS + select BACKLIGHT_CLASS_DEVICE + help + Choose this option if you have a Unisoc chipsets. + If M is selected the module will be called sprd-drm. \ No newline at end of file diff --git a/drivers/gpu/drm/sprd/Makefile b/drivers/gpu/drm/sprd/Makefile new file mode 100644 index 0000000..63b8751 --- /dev/null +++ b/drivers/gpu/drm/sprd/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y += -Iinclude/drm + +subdir-ccflags-y += -I$(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..7cac098 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_drm.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Unisoc Inc. + */ + +#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 "20191101" +#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_init(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_VMAP_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; + struct sprd_drm *sprd; + int err; + + drm = drm_dev_alloc(&sprd_drm_drv, dev); + if (IS_ERR(drm)) + return PTR_ERR(drm); + + dev_set_drvdata(dev, drm); + + sprd = devm_kzalloc(drm->dev, sizeof(*sprd), GFP_KERNEL); + if (!sprd) { + err = -ENOMEM; + goto err_free_drm; + } + drm->dev_private = sprd; + + sprd_drm_mode_config_init(drm); + + /* bind and init sub drivers */ + err = component_bind_all(drm->dev, drm); + if (err) { + DRM_ERROR("failed to bind all component.\n"); + goto err_dc_cleanup; + } + + /* vblank init */ + err = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (err) { + DRM_ERROR("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); + + err = drm_dev_register(drm, 0); + if (err < 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); +err_free_drm: + drm_dev_put(drm); + return err; +} + +static void sprd_drm_unbind(struct device *dev) +{ + drm_put_dev(dev_get_drvdata(dev)); +} + +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) +{ + struct device_node *np = data; + + DRM_DEBUG("compare %s\n", np->full_name); + + return dev->of_node == np; +} + +static int sprd_drm_component_probe(struct device *dev, + const struct component_master_ops *m_ops) +{ + struct device_node *ep, *port, *remote; + struct component_match *match = NULL; + int i; + + if (!dev->of_node) + return -EINVAL; + + /* + * Bind the crtc's ports first, so that drm_of_find_possible_crtcs() + * called from encoder's .bind callbacks works as expected + */ + for (i = 0; ; i++) { + port = of_parse_phandle(dev->of_node, "ports", i); + if (!port) + break; + + if (!of_device_is_available(port->parent)) { + of_node_put(port); + continue; + } + + component_match_add(dev, &match, compare_of, port->parent); + of_node_put(port); + } + + if (i == 0) { + dev_err(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + if (!match) { + dev_err(dev, "no available port\n"); + return -ENODEV; + } + + /* + * For bound crtcs, bind the encoders attached to their remote endpoint + */ + for (i = 0; ; i++) { + port = of_parse_phandle(dev->of_node, "ports", i); + if (!port) + break; + + if (!of_device_is_available(port->parent)) { + of_node_put(port); + continue; + } + + for_each_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote)) { + of_node_put(remote); + continue; + } else if (!of_device_is_available(remote->parent)) { + dev_warn(dev, "parent device of %s is not available\n", + remote->full_name); + of_node_put(remote); + continue; + } + + component_match_add(dev, &match, compare_of, remote); + of_node_put(remote); + } + of_node_put(port); + } + + return component_master_add_with_match(dev, m_ops, match); +} + +static int sprd_drm_probe(struct platform_device *pdev) +{ + int ret; + + ret = dma_set_mask_and_coherent(&pdev->dev, ~0); + if (ret) { + DRM_ERROR("dma_set_mask_and_coherent failed (%d)\n", ret); + return ret; + } + + return sprd_drm_component_probe(&pdev->dev, &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 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", }, + {}, +}; +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..137cb27 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_drm.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 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 Fri Feb 21 07:48:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11396271 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C493E14BC for ; Fri, 21 Feb 2020 11:15:47 +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 A264E2073A for ; Fri, 21 Feb 2020 11:15:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FYeq5fRl" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org A264E2073A 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 A7B656F3FD; Fri, 21 Feb 2020 11:15:23 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-pg1-x544.google.com (mail-pg1-x544.google.com [IPv6:2607:f8b0:4864:20::544]) by gabe.freedesktop.org (Postfix) with ESMTPS id EF3CD6EEB4 for ; Fri, 21 Feb 2020 07:49:14 +0000 (UTC) Received: by mail-pg1-x544.google.com with SMTP id 6so581952pgk.0 for ; Thu, 20 Feb 2020 23:49:14 -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=9s/89tFhK8CsKwuJAyzex+lyypM4LteBbs7SvUHMAY4=; b=FYeq5fRl/qH/XEUZDrffiETx7mSYEoysXSDKfoCD8vugyXXzbaSVd1+NbJutdsIDFJ 6TRvVT4c3trRe5JdG9v2FnfceZrgA1E+fgzWlQaKKLydgSE2Jxq0jRZxbc5pSswPrl0q kqI5ccN7TYTfChx6xQXGx7dNPwzqD1X1fPFblfem1I5UAxtmYALsjsYpkwqXUVuazDvq P2DEtqOVy7pNW4fVc3dBNTO+WxPswVMzQiHebeysHi2iHu8qiadOc6A1s8r4RANg3yF8 nuf2jqREWcFkLO9xpoFj5e3EN6g0huypZW3dq6HxbjEttngfOCGDyh/ChEfHAGXuGcvF d5ew== 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=9s/89tFhK8CsKwuJAyzex+lyypM4LteBbs7SvUHMAY4=; b=o/MWR7UZhcz2lJkanNjq4qk7r3Xs+W3FUPDHrNh07d02w7fMilA1YjPXp9f+GAlbYx b2Vyx1K4r4Ai0sIfozI3NObRO1huvB+la+YET/S/rEiZGRQ52qY6oart7pSCaMCmqxzP xXVJOHUJ8KIQgX+xYOVFe9YDi20PGbma1Cqh4jLpNdiiwPgF6nfczA0qW2LygYZ7TgwS 7gDWaoCpBZrduM0DUMq9o3sx0u5TtBulFwzpm51RvQPdJB1ptte7qy/8DDZks3IPkMvU D1JonSLq/8tZTu7GgSHVXr7HgpZq3tEXuBRQ3i5CEv1V2d0lfxSQuyxabwBG0Q3w8Ulx 9VsQ== X-Gm-Message-State: APjAAAWaZY9FgOjgfBMn0fkZZgS49ZVNmi9m29yduKygh6kwoOYza97/ xflOd1RPAqBrG7cK6AfIF69Q2H4Z X-Google-Smtp-Source: APXvYqxKqdVRyRdtOGfIJxeRmGLjwNsFH6v00he3QSjaziEmGGmxu/92M0N39l7riAZg/3ddqVQ/GQ== X-Received: by 2002:a62:18c9:: with SMTP id 192mr35752390pfy.117.1582271354610; Thu, 20 Feb 2020 23:49:14 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id d1sm1444653pgj.79.2020.02.20.23.49.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 20 Feb 2020 23:49:14 -0800 (PST) From: Kevin Tang To: airlied@linux.ie, daniel@ffwll.ch, robh+dt@kernel.org, mark.rutland@arm.com, kevin3.tang@gmail.com Subject: [PATCH RFC v3 3/6] dt-bindings: display: add Unisoc's dpu bindings Date: Fri, 21 Feb 2020 15:48:53 +0800 Message-Id: <1582271336-3708-4-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> References: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Fri, 21 Feb 2020 11:14:23 +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, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, zhang.lyra@gmail.com, baolin.wang@linaro.org 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: Baolin Wang Cc: Chunyan Zhang Signed-off-by: Kevin Tang --- .../devicetree/bindings/display/sprd/dpu.yaml | 85 ++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/sprd/dpu.yaml diff --git a/Documentation/devicetree/bindings/display/sprd/dpu.yaml b/Documentation/devicetree/bindings/display/sprd/dpu.yaml new file mode 100644 index 0000000..7695d94 --- /dev/null +++ b/Documentation/devicetree/bindings/display/sprd/dpu.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/sprd/dpu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Unisoc SoC Display Processor Unit (DPU) + +maintainers: + - David Airlie + - Daniel Vetter + - Rob Herring + - Mark Rutland + +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: + description: A phandle to DPU power domain node. + + iommus: + 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@0x63000000 { + compatible = "sprd,sharkl3-dpu"; + reg = <0x0 0x63000000 0x0 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 Fri Feb 21 07:48:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Tang X-Patchwork-Id: 11396275 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 55F1C14E3 for ; Fri, 21 Feb 2020 11:15:52 +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 341D02073A for ; Fri, 21 Feb 2020 11:15:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GL+r6qeY" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 341D02073A 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 63F416F40E; Fri, 21 Feb 2020 11:15:24 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-pf1-x441.google.com (mail-pf1-x441.google.com [IPv6:2607:f8b0:4864:20::441]) by gabe.freedesktop.org (Postfix) with ESMTPS id EB7936EEB5 for ; Fri, 21 Feb 2020 07:49:18 +0000 (UTC) Received: by mail-pf1-x441.google.com with SMTP id 4so759627pfz.9 for ; Thu, 20 Feb 2020 23:49:18 -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=3ly3QqudElxaLKhmv4LOZ+B2cTL+Sb73BbAuzZrAgLI=; b=GL+r6qeYmuUQmE6yVVRa5385m/jR/qDucUwNp6BA0zDqD0iNqCRLpKiONvRfpYf2iJ zTWzNJX7TBsTq7w9A9CmV/zcs2AAPNW0uMY8eSaVt+RDYnNNqcVMeomm68XoewSyysv9 dPRTzWC/6XCfBvriT/tVE7LONvwwDTBj8CJkZzOsRrP4z5l/z0Q6aikzys3ZbrhQEnOS 5mz58Ie1962y0lEElvtETitVNMWbZnB91knx2luPuW6IHT+yGXn3NqtTc92ZKAJ0FzZQ 7vYltrwuIGpnQnI2qGZOqsz0pKVGVlu9rTv6z7Do1nMVuepdNEe2juo7eD7FZ9KJw2X2 Pm4g== 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=3ly3QqudElxaLKhmv4LOZ+B2cTL+Sb73BbAuzZrAgLI=; b=Oicxdd8wqiSLKoMtV6sa82XlmmXDJeGH0IMq8B8nAQUNSss8pmXJJMD8nndMcFXMRX d/NbSRdz99VHM//KUsbCtqfs/gZ2j7HzoldIuJ+yDitv010Ym+2H9yJy2t8Np+rhe0Uk Oe8+ldMlREBdSmeZzypu6EdX108wcM/acdCDA6nA1Gg6YBgfU/HP3nYeGs8g2Tj4P9JX JkoZR8g7x5uttOHytfouY0eA33pogAz/AIPCNiRqouGuSePfFjOCRy84qcfgHQE67y22 CV2bvBF269dHRi2atJi8j3hio4QKT1Qo4iadvrl+H1DgVhMSAt6I7k7lPxNKqOsi6Db6 WuIw== X-Gm-Message-State: APjAAAWBs1TPNz4lrzxicpulou61Nz3Xpoqn1QNyGhdNePdNlZSpXwRr ScIFwrfIzoem6yWBOrBdd4Y= X-Google-Smtp-Source: APXvYqzD8QxX594Ohg8ThEGEpd7dcIqcTHVjDM99YDhmtv8DyKFQwc3Zr8/afqESsO3PeDZ/M1ECQg== X-Received: by 2002:aa7:9510:: with SMTP id b16mr35782523pfp.65.1582271358313; Thu, 20 Feb 2020 23:49:18 -0800 (PST) Received: from nj08008nbu.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id d1sm1444653pgj.79.2020.02.20.23.49.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 20 Feb 2020 23:49:17 -0800 (PST) From: Kevin Tang To: airlied@linux.ie, daniel@ffwll.ch, robh+dt@kernel.org, mark.rutland@arm.com, kevin3.tang@gmail.com Subject: [PATCH RFC v3 4/6] drm/sprd: add Unisoc's drm display controller driver Date: Fri, 21 Feb 2020 15:48:54 +0800 Message-Id: <1582271336-3708-5-git-send-email-kevin3.tang@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> References: <1582271336-3708-1-git-send-email-kevin3.tang@gmail.com> X-Mailman-Approved-At: Fri, 21 Feb 2020 11:14:23 +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, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, zhang.lyra@gmail.com, baolin.wang@linaro.org 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: Baolin Wang Cc: Chunyan Zhang Signed-off-by: Kevin Tang --- drivers/gpu/drm/sprd/Makefile | 6 +- drivers/gpu/drm/sprd/disp_lib.c | 59 +++ drivers/gpu/drm/sprd/disp_lib.h | 21 + drivers/gpu/drm/sprd/dpu/Makefile | 7 + drivers/gpu/drm/sprd/dpu/dpu_r2p0.c | 787 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_dpu.c | 678 +++++++++++++++++++++++++++++++ drivers/gpu/drm/sprd/sprd_dpu.h | 130 ++++++ drivers/gpu/drm/sprd/sprd_drm.c | 1 + drivers/gpu/drm/sprd/sprd_drm.h | 2 + 9 files changed, 1690 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/sprd/disp_lib.c create mode 100644 drivers/gpu/drm/sprd/disp_lib.h create mode 100644 drivers/gpu/drm/sprd/dpu/Makefile create mode 100644 drivers/gpu/drm/sprd/dpu/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/Makefile b/drivers/gpu/drm/sprd/Makefile index 63b8751..c94c8ac 100644 --- a/drivers/gpu/drm/sprd/Makefile +++ b/drivers/gpu/drm/sprd/Makefile @@ -4,4 +4,8 @@ ccflags-y += -Iinclude/drm subdir-ccflags-y += -I$(src) -obj-y := sprd_drm.o +obj-y := sprd_drm.o \ + sprd_dpu.o + +obj-y += disp_lib.o +obj-y += dpu/ diff --git a/drivers/gpu/drm/sprd/disp_lib.c b/drivers/gpu/drm/sprd/disp_lib.c new file mode 100644 index 0000000..c887822 --- /dev/null +++ b/drivers/gpu/drm/sprd/disp_lib.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Unisoc Inc. + */ + +#define pr_fmt(__fmt) "[drm][%20s] "__fmt, __func__ + +#include +#include +#include +#include +#include + +#include "disp_lib.h" + +struct device *sprd_disp_pipe_get_by_port(struct device *dev, int port) +{ + struct device_node *np = dev->of_node; + struct device_node *endpoint; + struct device_node *remote_node; + struct platform_device *remote_pdev; + + endpoint = of_graph_get_endpoint_by_regs(np, port, 0); + if (!endpoint) { + DRM_ERROR("%s/port%d/endpoint0 was not found\n", + np->full_name, port); + return NULL; + } + + remote_node = of_graph_get_remote_port_parent(endpoint); + if (!remote_node) { + DRM_ERROR("device node was not found by endpoint0\n"); + return NULL; + } + + remote_pdev = of_find_device_by_node(remote_node); + if (remote_pdev == NULL) { + DRM_ERROR("find %s platform device failed\n", + remote_node->full_name); + return NULL; + } + + return &remote_pdev->dev; +} + +struct device *sprd_disp_pipe_get_input(struct device *dev) +{ + return sprd_disp_pipe_get_by_port(dev, 1); +} + +struct device *sprd_disp_pipe_get_output(struct device *dev) +{ + return sprd_disp_pipe_get_by_port(dev, 0); +} + +MODULE_AUTHOR("Leon He "); +MODULE_AUTHOR("Kevin Tang "); +MODULE_DESCRIPTION("Unisoc display common API library"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/sprd/disp_lib.h b/drivers/gpu/drm/sprd/disp_lib.h new file mode 100644 index 0000000..2c9abd7 --- /dev/null +++ b/drivers/gpu/drm/sprd/disp_lib.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Unisoc Inc. + */ + +#ifndef _DISP_LIB_H_ +#define _DISP_LIB_H_ + +#include +#include + +#ifdef pr_fmt +#undef pr_fmt +#define pr_fmt(__fmt) "[drm][%20s] "__fmt, __func__ +#endif + +struct device *sprd_disp_pipe_get_by_port(struct device *dev, int port); +struct device *sprd_disp_pipe_get_input(struct device *dev); +struct device *sprd_disp_pipe_get_output(struct device *dev); + +#endif diff --git a/drivers/gpu/drm/sprd/dpu/Makefile b/drivers/gpu/drm/sprd/dpu/Makefile new file mode 100644 index 0000000..73bd497 --- /dev/null +++ b/drivers/gpu/drm/sprd/dpu/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +ifdef CONFIG_ARM64 +KBUILD_CFLAGS += -mstrict-align +endif + +obj-y += dpu_r2p0.o diff --git a/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c new file mode 100644 index 0000000..b96e2e2 --- /dev/null +++ b/drivers/gpu/drm/sprd/dpu/dpu_r2p0.c @@ -0,0 +1,787 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Unisoc Inc. + */ + +#include +#include +#include +#include "sprd_dpu.h" + +#define DISPC_INT_FBC_PLD_ERR_MASK BIT(8) +#define DISPC_INT_FBC_HDR_ERR_MASK BIT(9) + +#define DISPC_INT_MMU_INV_WR_MASK BIT(19) +#define DISPC_INT_MMU_INV_RD_MASK BIT(18) +#define DISPC_INT_MMU_VAOR_WR_MASK BIT(17) +#define DISPC_INT_MMU_VAOR_RD_MASK BIT(16) + +struct layer_reg { + u32 addr[4]; + u32 ctrl; + u32 size; + u32 pitch; + u32 pos; + u32 alpha; + u32 ck; + u32 pallete; + u32 crop_start; +}; + +struct wb_region_reg { + u32 pos; + u32 size; +}; + +struct dpu_reg { + u32 dpu_version; + u32 dpu_ctrl; + u32 dpu_cfg0; + u32 dpu_cfg1; + u32 dpu_cfg2; + u32 dpu_secure; + u32 reserved_0x0018_0x001C[2]; + u32 panel_size; + u32 blend_size; + u32 reserved_0x0028; + u32 bg_color; + struct layer_reg layers[8]; + u32 wb_base_addr; + u32 wb_ctrl; + u32 wb_cfg; + u32 wb_pitch; + struct wb_region_reg region[3]; + u32 reserved_0x01D8_0x01DC[2]; + u32 dpu_int_en; + u32 dpu_int_clr; + u32 dpu_int_sts; + u32 dpu_int_raw; + u32 dpi_ctrl; + u32 dpi_h_timing; + u32 dpi_v_timing; + u32 reserved_0x01FC; + u32 dpu_enhance_cfg; + u32 reserved_0x0204_0x020C[3]; + u32 epf_epsilon; + u32 epf_gain0_3; + u32 epf_gain4_7; + u32 epf_diff; + u32 reserved_0x0220_0x023C[8]; + u32 hsv_lut_addr; + u32 hsv_lut_wdata; + u32 hsv_lut_rdata; + u32 reserved_0x024C_0x027C[13]; + u32 cm_coef01_00; + u32 cm_coef03_02; + u32 cm_coef11_10; + u32 cm_coef13_12; + u32 cm_coef21_20; + u32 cm_coef23_22; + u32 reserved_0x0298_0x02BC[10]; + u32 slp_cfg0; + u32 slp_cfg1; + u32 reserved_0x02C8_0x02FC[14]; + u32 gamma_lut_addr; + u32 gamma_lut_wdata; + u32 gamma_lut_rdata; + u32 reserved_0x030C_0x033C[13]; + u32 checksum_en; + u32 checksum0_start_pos; + u32 checksum0_end_pos; + u32 checksum1_start_pos; + u32 checksum1_end_pos; + u32 checksum0_result; + u32 checksum1_result; + u32 reserved_0x035C; + u32 dpu_sts[18]; + u32 reserved_0x03A8_0x03AC[2]; + u32 dpu_fbc_cfg0; + u32 dpu_fbc_cfg1; + u32 reserved_0x03B8_0x03EC[14]; + u32 rf_ram_addr; + u32 rf_ram_rdata_low; + u32 rf_ram_rdata_high; + u32 reserved_0x03FC_0x07FC[257]; + u32 mmu_en; + u32 mmu_update; + u32 mmu_min_vpn; + u32 mmu_vpn_range; + u32 mmu_pt_addr; + u32 mmu_default_page; + u32 mmu_vaor_addr_rd; + u32 mmu_vaor_addr_wr; + u32 mmu_inv_addr_rd; + u32 mmu_inv_addr_wr; + u32 mmu_uns_addr_rd; + u32 mmu_uns_addr_wr; + u32 mmu_miss_cnt; + u32 mmu_pt_update_qos; + u32 mmu_version; + u32 mmu_min_ppn1; + u32 mmu_ppn_range1; + u32 mmu_min_ppn2; + u32 mmu_ppn_range2; + u32 mmu_vpn_paor_rd; + u32 mmu_vpn_paor_wr; + u32 mmu_ppn_paor_rd; + u32 mmu_ppn_paor_wr; + u32 mmu_reg_au_manage; + u32 mmu_page_rd_ch; + u32 mmu_page_wr_ch; + u32 mmu_read_page_cmd_cnt; + u32 mmu_read_page_latency_cnt; + u32 mmu_page_max_latency; +}; + +static DECLARE_WAIT_QUEUE_HEAD(wait_queue); +static bool panel_ready = true; +static bool evt_update; +static bool evt_stop; + +static u32 dpu_get_version(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + return reg->dpu_version; +} + +static void dpu_dump(struct dpu_context *ctx) +{ + u32 *reg = (u32 *)ctx->base; + int i; + + pr_info(" 0 4 8 C\n"); + for (i = 0; i < 256; i += 4) { + pr_info("%04x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + i * 4, reg[i], reg[i + 1], reg[i + 2], reg[i + 3]); + } +} + +static u32 check_mmu_isr(struct dpu_context *ctx, u32 reg_val) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + u32 mmu_mask = DISPC_INT_MMU_VAOR_RD_MASK | + DISPC_INT_MMU_VAOR_WR_MASK | + DISPC_INT_MMU_INV_RD_MASK | + DISPC_INT_MMU_INV_WR_MASK; + u32 val = reg_val & mmu_mask; + + if (val) { + pr_err("--- iommu interrupt err: 0x%04x ---\n", val); + + pr_err("iommu invalid read error, addr: 0x%08x\n", + reg->mmu_inv_addr_rd); + pr_err("iommu invalid write error, addr: 0x%08x\n", + reg->mmu_inv_addr_wr); + pr_err("iommu va out of range read error, addr: 0x%08x\n", + reg->mmu_vaor_addr_rd); + pr_err("iommu va out of range write error, addr: 0x%08x\n", + reg->mmu_vaor_addr_wr); + pr_err("BUG: iommu failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + + dpu_dump(ctx); + } + + return val; +} + +static void dpu_clean_all(struct dpu_context *ctx) +{ + int i; + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + for (i = 0; i < 8; i++) + reg->layers[i].ctrl = 0; +} + +static u32 dpu_isr(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + u32 reg_val, int_mask = 0; + + reg_val = reg->dpu_int_sts; + + /* disable err interrupt */ + if (reg_val & DISPC_INT_ERR_MASK) + int_mask |= DISPC_INT_ERR_MASK; + + /* dpu update done isr */ + if (reg_val & DISPC_INT_UPDATE_DONE_MASK) { + evt_update = true; + wake_up_interruptible_all(&wait_queue); + } + + /* dpu stop done isr */ + if (reg_val & DISPC_INT_DONE_MASK) { + evt_stop = true; + wake_up_interruptible_all(&wait_queue); + } + + /* dpu ifbc payload error isr */ + if (reg_val & DISPC_INT_FBC_PLD_ERR_MASK) { + int_mask |= DISPC_INT_FBC_PLD_ERR_MASK; + pr_err("dpu ifbc payload error\n"); + } + + /* dpu ifbc header error isr */ + if (reg_val & DISPC_INT_FBC_HDR_ERR_MASK) { + int_mask |= DISPC_INT_FBC_HDR_ERR_MASK; + pr_err("dpu ifbc header error\n"); + } + + int_mask |= check_mmu_isr(ctx, reg_val); + + reg->dpu_int_clr = reg_val; + reg->dpu_int_en &= ~int_mask; + + return reg_val; +} + +static int dpu_wait_stop_done(struct dpu_context *ctx) +{ + int rc; + + if (ctx->is_stopped) + return 0; + + rc = wait_event_interruptible_timeout(wait_queue, evt_stop, + msecs_to_jiffies(500)); + evt_stop = false; + + ctx->is_stopped = true; + + if (!rc) { + pr_err("dpu wait for stop done time out!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int dpu_wait_update_done(struct dpu_context *ctx) +{ + int rc; + + evt_update = false; + + rc = wait_event_interruptible_timeout(wait_queue, evt_update, + msecs_to_jiffies(500)); + + if (!rc) { + pr_err("dpu wait for reg update done time out!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void dpu_stop(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + if (ctx->if_type == SPRD_DISPC_IF_DPI) + reg->dpu_ctrl |= BIT(1); + + dpu_wait_stop_done(ctx); + + pr_info("dpu stop\n"); +} + +static void dpu_run(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + reg->dpu_ctrl |= BIT(0); + + ctx->is_stopped = false; + + pr_info("dpu run\n"); + + if (ctx->if_type == SPRD_DISPC_IF_EDPI) { + /* + * If the panel read GRAM speed faster than + * DSI write GRAM speed, it will display some + * mass on screen when backlight on. So wait + * a TE period after flush the GRAM. + */ + if (!panel_ready) { + dpu_wait_stop_done(ctx); + /* wait for TE again */ + mdelay(20); + panel_ready = true; + } + } +} + +static int dpu_init(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + u32 size; + + reg->bg_color = 0; + + size = (ctx->vm.vactive << 16) | ctx->vm.hactive; + reg->panel_size = size; + reg->blend_size = size; + + reg->dpu_cfg0 = BIT(4) | BIT(5); + + reg->dpu_cfg1 = 0x004466da; + reg->dpu_cfg2 = 0; + + if (ctx->is_stopped) + dpu_clean_all(ctx); + + reg->mmu_en = 0; + reg->mmu_min_ppn1 = 0; + reg->mmu_ppn_range1 = 0xffff; + reg->mmu_min_ppn2 = 0; + reg->mmu_ppn_range2 = 0xffff; + reg->mmu_vpn_range = 0x1ffff; + + reg->dpu_int_clr = 0xffff; + + return 0; +} + +static void dpu_uninit(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + reg->dpu_int_en = 0; + reg->dpu_int_clr = 0xff; + + panel_ready = false; +} + +enum { + DPU_LAYER_FORMAT_YUV422_2PLANE, + DPU_LAYER_FORMAT_YUV420_2PLANE, + DPU_LAYER_FORMAT_YUV420_3PLANE, + DPU_LAYER_FORMAT_ARGB8888, + DPU_LAYER_FORMAT_RGB565, + DPU_LAYER_FORMAT_XFBC_ARGB8888 = 8, + DPU_LAYER_FORMAT_XFBC_RGB565, + DPU_LAYER_FORMAT_MAX_TYPES, +}; + +enum { + DPU_LAYER_ROTATION_0, + DPU_LAYER_ROTATION_90, + DPU_LAYER_ROTATION_180, + DPU_LAYER_ROTATION_270, + DPU_LAYER_ROTATION_0_M, + DPU_LAYER_ROTATION_90_M, + DPU_LAYER_ROTATION_180_M, + DPU_LAYER_ROTATION_270_M, +}; + +static u32 to_dpu_rotation(u32 angle) +{ + u32 rot = DPU_LAYER_ROTATION_0; + + switch (angle) { + case 0: + case DRM_MODE_ROTATE_0: + rot = DPU_LAYER_ROTATION_0; + break; + case DRM_MODE_ROTATE_90: + rot = DPU_LAYER_ROTATION_90; + break; + case DRM_MODE_ROTATE_180: + rot = DPU_LAYER_ROTATION_180; + break; + case DRM_MODE_ROTATE_270: + rot = DPU_LAYER_ROTATION_270; + break; + case DRM_MODE_REFLECT_Y: + rot = DPU_LAYER_ROTATION_180_M; + break; + case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90): + rot = DPU_LAYER_ROTATION_90_M; + break; + case DRM_MODE_REFLECT_X: + rot = DPU_LAYER_ROTATION_0_M; + break; + case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90): + rot = DPU_LAYER_ROTATION_270_M; + break; + default: + pr_err("rotation convert unsupport angle (drm)= 0x%x\n", angle); + break; + } + + return rot; +} + +static u32 dpu_img_ctrl(u32 format, u32 blending, u32 rotation) +{ + int reg_val = 0; + + /* layer enable */ + reg_val |= BIT(0); + + switch (format) { + case DRM_FORMAT_BGRA8888: + /* BGRA8888 -> ARGB8888 */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B3B2B1B0 << 8; + reg_val |= (DPU_LAYER_FORMAT_ARGB8888 << 4); + break; + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + /* RGBA8888 -> ABGR8888 */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B3B2B1B0 << 8; + case DRM_FORMAT_ABGR8888: + /* rb switch */ + reg_val |= BIT(10); + case DRM_FORMAT_ARGB8888: + reg_val |= (DPU_LAYER_FORMAT_ARGB8888 << 4); + break; + case DRM_FORMAT_XBGR8888: + /* rb switch */ + reg_val |= BIT(10); + case DRM_FORMAT_XRGB8888: + reg_val |= (DPU_LAYER_FORMAT_ARGB8888 << 4); + break; + case DRM_FORMAT_BGR565: + /* rb switch */ + reg_val |= BIT(10); + case DRM_FORMAT_RGB565: + reg_val |= (DPU_LAYER_FORMAT_RGB565 << 4); + break; + case DRM_FORMAT_NV12: + /* 2-Lane: Yuv420 */ + reg_val |= DPU_LAYER_FORMAT_YUV420_2PLANE << 4; + /* Y endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 8; + /* UV endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 10; + break; + case DRM_FORMAT_NV21: + /* 2-Lane: Yuv420 */ + reg_val |= DPU_LAYER_FORMAT_YUV420_2PLANE << 4; + /* Y endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 8; + /* UV endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B3B2B1B0 << 10; + break; + case DRM_FORMAT_NV16: + /* 2-Lane: Yuv422 */ + reg_val |= DPU_LAYER_FORMAT_YUV422_2PLANE << 4; + /* Y endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B3B2B1B0 << 8; + /* UV endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B3B2B1B0 << 10; + break; + case DRM_FORMAT_NV61: + /* 2-Lane: Yuv422 */ + reg_val |= DPU_LAYER_FORMAT_YUV422_2PLANE << 4; + /* Y endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 8; + /* UV endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 10; + break; + case DRM_FORMAT_YUV420: + reg_val |= DPU_LAYER_FORMAT_YUV420_3PLANE << 4; + /* Y endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 8; + /* UV endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 10; + break; + case DRM_FORMAT_YVU420: + reg_val |= DPU_LAYER_FORMAT_YUV420_3PLANE << 4; + /* Y endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B0B1B2B3 << 8; + /* UV endian */ + reg_val |= SPRD_IMG_DATA_ENDIAN_B3B2B1B0 << 10; + break; + default: + pr_err("error: invalid format %c%c%c%c\n", format, + format >> 8, + format >> 16, + format >> 24); + break; + } + + switch (blending) { + case DRM_MODE_BLEND_PIXEL_NONE: + /* don't do blending, maybe RGBX */ + /* alpha mode select - layer alpha */ + reg_val |= BIT(2); + break; + case DRM_MODE_BLEND_COVERAGE: + /* alpha mode select - combo alpha */ + reg_val |= BIT(3); + /*Normal mode*/ + reg_val &= (~BIT(16)); + break; + case DRM_MODE_BLEND_PREMULTI: + /* alpha mode select - combo alpha */ + reg_val |= BIT(3); + /*Pre-mult mode*/ + reg_val |= BIT(16); + break; + default: + /* alpha mode select - layer alpha */ + reg_val |= BIT(2); + break; + } + + rotation = to_dpu_rotation(rotation); + reg_val |= (rotation & 0x7) << 20; + + return reg_val; +} + +static void dpu_bgcolor(struct dpu_context *ctx, u32 color) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + if (ctx->if_type == SPRD_DISPC_IF_EDPI) + dpu_wait_stop_done(ctx); + + reg->bg_color = color; + + dpu_clean_all(ctx); + + if ((ctx->if_type == SPRD_DISPC_IF_DPI) && !ctx->is_stopped) { + reg->dpu_ctrl |= BIT(2); + dpu_wait_update_done(ctx); + } else if (ctx->if_type == SPRD_DISPC_IF_EDPI) { + reg->dpu_ctrl |= BIT(0); + ctx->is_stopped = false; + } +} + +static void dpu_layer(struct dpu_context *ctx, + struct sprd_dpu_layer *hwlayer) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + const struct drm_format_info *info; + struct layer_reg *layer; + u32 addr, size, offset; + int i; + + layer = ®->layers[hwlayer->index]; + offset = (hwlayer->dst_x & 0xffff) | ((hwlayer->dst_y) << 16); + + if (hwlayer->src_w && hwlayer->src_h) + size = (hwlayer->src_w & 0xffff) | ((hwlayer->src_h) << 16); + else + size = (hwlayer->dst_w & 0xffff) | ((hwlayer->dst_h) << 16); + + for (i = 0; i < hwlayer->planes; i++) { + addr = hwlayer->addr[i]; + + if (addr % 16) + pr_err("layer addr[%d] is not 16 bytes align, it's 0x%08x\n", + i, addr); + layer->addr[i] = addr; + } + + layer->pos = offset; + layer->size = size; + layer->crop_start = (hwlayer->src_y << 16) | hwlayer->src_x; + layer->alpha = hwlayer->alpha; + + info = drm_format_info(hwlayer->format); + if (info->cpp[0] == 0) { + pr_err("layer[%d] bytes per pixel is invalid\n", hwlayer->index); + return; + } + + if (hwlayer->planes == 3) + /* UV pitch is 1/2 of Y pitch*/ + layer->pitch = (hwlayer->pitch[0] / info->cpp[0]) | + (hwlayer->pitch[0] / info->cpp[0] << 15); + else + layer->pitch = hwlayer->pitch[0] / info->cpp[0]; + + layer->ctrl = dpu_img_ctrl(hwlayer->format, hwlayer->blending, + hwlayer->rotation); + + pr_debug("dst_x = %d, dst_y = %d, dst_w = %d, dst_h = %d\n", + hwlayer->dst_x, hwlayer->dst_y, + hwlayer->dst_w, hwlayer->dst_h); + pr_debug("start_x = %d, start_y = %d, start_w = %d, start_h = %d\n", + hwlayer->src_x, hwlayer->src_y, + hwlayer->src_w, hwlayer->src_h); +} + +static void dpu_flip(struct dpu_context *ctx, + struct sprd_dpu_layer layers[], u8 count) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + int i; + + /* + * 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_DISPC_IF_EDPI) + dpu_wait_stop_done(ctx); + + /* reset the bgcolor to black */ + reg->bg_color = 0; + + /* 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_DISPC_IF_DPI) { + if (!ctx->is_stopped) { + reg->dpu_ctrl |= BIT(2); + dpu_wait_update_done(ctx); + } + + reg->dpu_int_en |= DISPC_INT_ERR_MASK; + + } else if (ctx->if_type == SPRD_DISPC_IF_EDPI) { + reg->dpu_ctrl |= BIT(0); + + ctx->is_stopped = false; + } + + /* + * If the following interrupt was disabled in isr, + * re-enable it. + */ + reg->dpu_int_en |= DISPC_INT_FBC_PLD_ERR_MASK | + DISPC_INT_FBC_HDR_ERR_MASK | + DISPC_INT_MMU_VAOR_RD_MASK | + DISPC_INT_MMU_VAOR_WR_MASK | + DISPC_INT_MMU_INV_RD_MASK | + DISPC_INT_MMU_INV_WR_MASK; +} + +static void dpu_dpi_init(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + u32 int_mask = 0; + + if (ctx->if_type == SPRD_DISPC_IF_DPI) { + /* use dpi as interface */ + reg->dpu_cfg0 &= ~BIT(0); + + /* disable Halt function for SPRD DSI */ + reg->dpi_ctrl &= ~BIT(16); + + /* select te from external pad */ + reg->dpi_ctrl |= BIT(10); + + /* set dpi timing */ + reg->dpi_h_timing = (ctx->vm.hsync_len << 0) | + (ctx->vm.hback_porch << 8) | + (ctx->vm.hfront_porch << 20); + reg->dpi_v_timing = (ctx->vm.vsync_len << 0) | + (ctx->vm.vback_porch << 8) | + (ctx->vm.vfront_porch << 20); + if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32) + pr_warn("Warning: (vsync + vbp) < 32, " + "underflow risk!\n"); + + /* enable dpu update done INT */ + int_mask |= DISPC_INT_UPDATE_DONE_MASK; + /* enable dpu DONE INT */ + int_mask |= DISPC_INT_DONE_MASK; + /* enable dpu dpi vsync */ + int_mask |= DISPC_INT_DPI_VSYNC_MASK; + /* enable dpu TE INT */ + int_mask |= DISPC_INT_TE_MASK; + /* enable underflow err INT */ + int_mask |= DISPC_INT_ERR_MASK; + /* enable write back done INT */ + int_mask |= DISPC_INT_WB_DONE_MASK; + /* enable write back fail INT */ + int_mask |= DISPC_INT_WB_FAIL_MASK; + + } else if (ctx->if_type == SPRD_DISPC_IF_EDPI) { + /* use edpi as interface */ + reg->dpu_cfg0 |= BIT(0); + + /* use external te */ + reg->dpi_ctrl |= BIT(10); + + /* enable te */ + reg->dpi_ctrl |= BIT(8); + + /* enable stop DONE INT */ + int_mask |= DISPC_INT_DONE_MASK; + /* enable TE INT */ + int_mask |= DISPC_INT_TE_MASK; + } + + /* enable ifbc payload error INT */ + int_mask |= DISPC_INT_FBC_PLD_ERR_MASK; + /* enable ifbc header error INT */ + int_mask |= DISPC_INT_FBC_HDR_ERR_MASK; + /* enable iommu va out of range read error INT */ + int_mask |= DISPC_INT_MMU_VAOR_RD_MASK; + /* enable iommu va out of range write error INT */ + int_mask |= DISPC_INT_MMU_VAOR_WR_MASK; + /* enable iommu invalid read error INT */ + int_mask |= DISPC_INT_MMU_INV_RD_MASK; + /* enable iommu invalid write error INT */ + int_mask |= DISPC_INT_MMU_INV_WR_MASK; + + reg->dpu_int_en = int_mask; +} + +static void enable_vsync(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + reg->dpu_int_en |= DISPC_INT_DPI_VSYNC_MASK; +} + +static void disable_vsync(struct dpu_context *ctx) +{ + struct dpu_reg *reg = (struct dpu_reg *)ctx->base; + + reg->dpu_int_en &= ~DISPC_INT_DPI_VSYNC_MASK; +} + +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_BGRX8888, + 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 int dpu_capability(struct dpu_context *ctx, + struct dpu_capability *cap) +{ + if (!cap) + return -EINVAL; + + cap->max_layers = 6; + cap->fmts_ptr = primary_fmts; + cap->fmts_cnt = ARRAY_SIZE(primary_fmts); + + return 0; +} + +struct dpu_core_ops sharkl3_dpu_core_ops = { + .version = dpu_get_version, + .init = dpu_init, + .uninit = dpu_uninit, + .run = dpu_run, + .stop = dpu_stop, + .isr = dpu_isr, + .ifconfig = dpu_dpi_init, + .capability = dpu_capability, + .flip = dpu_flip, + .bg_color = dpu_bgcolor, + .enable_vsync = enable_vsync, + .disable_vsync = disable_vsync, +}; diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c new file mode 100644 index 0000000..331f236 --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_dpu.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Unisoc Inc. + */ + +#include +#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 int sprd_dpu_init(struct sprd_dpu *dpu); +static int sprd_dpu_uninit(struct sprd_dpu *dpu); + +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) +{ + DRM_DEBUG("%s()\n", __func__); + + return 0; +} + +static void sprd_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = plane->state->fb; + struct drm_gem_cma_object *cma_obj; + struct sprd_plane *p = to_sprd_plane(plane); + struct sprd_dpu *dpu = crtc_to_dpu(plane->state->crtc); + struct sprd_dpu_layer *layer = &dpu->layers[p->index]; + int i; + + if (plane->state->crtc->state->active_changed) { + DRM_DEBUG("resume or suspend, no need to update plane\n"); + return; + } + + 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->dst_w = state->crtc_w; + layer->dst_h = state->crtc_h; + layer->alpha = state->alpha; + layer->blending = state->pixel_blend_mode; + layer->rotation = state->rotation; + layer->planes = fb->format->num_planes; + layer->format = fb->format->format; + + DRM_DEBUG("%s() alpha = %u, blending = %u, rotation = %u\n", + __func__, layer->alpha, layer->blending, layer->rotation); + + for (i = 0; i < layer->planes; i++) { + cma_obj = drm_fb_cma_get_gem_obj(fb, i); + layer->addr[i] = cma_obj->paddr + fb->offsets[i]; + layer->pitch[i] = fb->pitches[i]; + } + + dpu->pending_planes++; +} + +static void sprd_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct sprd_plane *p = to_sprd_plane(plane); + + /* + * NOTE: + * The dpu->core->flip() will disable all the planes each time. + * So there is no need to impliment the atomic_disable() function. + * But this function can not be removed, because it will change + * to call atomic_update() callback instead. Which will cause + * kernel panic in sprd_plane_atomic_update(). + * + * We do nothing here but just print a debug log. + */ + DRM_DEBUG("%s() layer_id = %u\n", __func__, p->index); +} + +static int 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); + + return 0; +} + +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 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 err, i; + + if (dpu->core && dpu->core->capability) + dpu->core->capability(&dpu->ctx, &cap); + + dpu->layers = devm_kcalloc(drm->dev, cap.max_layers, + sizeof(struct sprd_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); + + err = drm_universal_plane_init(drm, &p->plane, 1, + &sprd_plane_funcs, cap.fmts_ptr, + cap.fmts_cnt, NULL, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (err) { + DRM_ERROR("fail to init primary plane\n"); + return ERR_PTR(err); + } + + 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; + } + + if (p) + DRM_INFO("dpu plane init ok\n"); + + return primary; +} + +static void sprd_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + + if ((dpu->mode->hdisplay == dpu->mode->htotal) || + (dpu->mode->vdisplay == dpu->mode->vtotal)) + dpu->ctx.if_type = SPRD_DISPC_IF_EDPI; + else + dpu->ctx.if_type = SPRD_DISPC_IF_DPI; +} + +static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + + DRM_INFO("%s() mode: "DRM_MODE_FMT"\n", __func__, DRM_MODE_ARG(mode)); + + if (mode->type & DRM_MODE_TYPE_DEFAULT) + dpu->mode = (struct drm_display_mode *)mode; + + if (mode->type & DRM_MODE_TYPE_PREFERRED) { + dpu->mode = (struct drm_display_mode *)mode; + drm_display_mode_to_videomode(dpu->mode, &dpu->ctx.vm); + } + + return MODE_OK; +} + +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + static bool is_enabled = true; + + DRM_INFO("%s()\n", __func__); + + if (is_enabled) + is_enabled = false; + else + pm_runtime_get_sync(dpu->dev.parent); + + sprd_dpu_init(dpu); + + enable_irq(dpu->ctx.irq); +} + +static void sprd_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + struct drm_device *drm = dpu->crtc.dev; + + DRM_INFO("%s()\n", __func__); + + disable_irq(dpu->ctx.irq); + + sprd_dpu_uninit(dpu); + + pm_runtime_put(dpu->dev.parent); + + 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_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + + DRM_DEBUG("%s()\n", __func__); + + down(&dpu->ctx.refresh_lock); + + memset(dpu->layers, 0, sizeof(*dpu->layers) * dpu->pending_planes); + + dpu->pending_planes = 0; +} + +static void sprd_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) + +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + struct drm_device *drm = dpu->crtc.dev; + + DRM_DEBUG("%s()\n", __func__); + + if (dpu->core && dpu->core->flip && dpu->pending_planes) + dpu->core->flip(&dpu->ctx, dpu->layers, dpu->pending_planes); + + up(&dpu->ctx.refresh_lock); + + 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 = crtc_to_dpu(crtc); + + DRM_DEBUG("%s()\n", __func__); + + if (dpu->core && dpu->core->enable_vsync) + dpu->core->enable_vsync(&dpu->ctx); + + return 0; +} + +static void sprd_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + + DRM_DEBUG("%s()\n", __func__); + + if (dpu->core && dpu->core->disable_vsync) + dpu->core->disable_vsync(&dpu->ctx); +} + +static int sprd_crtc_create_properties(struct drm_crtc *crtc) +{ + struct sprd_dpu *dpu = crtc_to_dpu(crtc); + struct drm_device *drm = dpu->crtc.dev; + struct drm_property *prop; + struct drm_property_blob *blob; + size_t blob_size; + + blob_size = strlen(dpu->ctx.version) + 1; + + blob = drm_property_create_blob(dpu->crtc.dev, blob_size, + dpu->ctx.version); + if (IS_ERR(blob)) { + DRM_ERROR("drm_property_create_blob dpu version failed\n"); + return PTR_ERR(blob); + } + + /* create dpu version property */ + prop = drm_property_create(drm, + DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB, + "dpu version", 0); + if (!prop) { + DRM_ERROR("drm_property_create dpu version failed\n"); + return -ENOMEM; + } + drm_object_attach_property(&crtc->base, prop, blob->base.id); + + return 0; +} + +static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = { + .mode_set_nofb = sprd_crtc_mode_set_nofb, + .mode_valid = sprd_crtc_mode_valid, + .atomic_check = sprd_crtc_atomic_check, + .atomic_begin = sprd_crtc_atomic_begin, + .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 err; + + /* + * 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; + + err = drm_crtc_init_with_planes(drm, crtc, primary, NULL, + &sprd_crtc_funcs, NULL); + if (err) { + DRM_ERROR("failed to init crtc.\n"); + return err; + } + + drm_mode_crtc_set_gamma_size(crtc, 256); + + drm_crtc_helper_add(crtc, &sprd_crtc_helper_funcs); + + sprd_crtc_create_properties(crtc); + + DRM_INFO("%s() ok\n", __func__); + return 0; +} + +int sprd_dpu_run(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + down(&ctx->refresh_lock); + + if (!ctx->is_inited) { + DRM_ERROR("dpu is not initialized\n"); + up(&ctx->refresh_lock); + return -EINVAL; + } + + if (!ctx->is_stopped) { + up(&ctx->refresh_lock); + return 0; + } + + if (dpu->core && dpu->core->run) + dpu->core->run(ctx); + + up(&ctx->refresh_lock); + + drm_crtc_vblank_on(&dpu->crtc); + + return 0; +} + +int sprd_dpu_stop(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + down(&ctx->refresh_lock); + + if (!ctx->is_inited) { + DRM_ERROR("dpu is not initialized\n"); + up(&ctx->refresh_lock); + return -EINVAL; + } + + if (ctx->is_stopped) { + up(&ctx->refresh_lock); + return 0; + } + + if (dpu->core && dpu->core->stop) + dpu->core->stop(ctx); + + up(&ctx->refresh_lock); + + drm_crtc_handle_vblank(&dpu->crtc); + drm_crtc_vblank_off(&dpu->crtc); + + return 0; +} + +static int sprd_dpu_init(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + down(&ctx->refresh_lock); + + if (dpu->ctx.is_inited) { + up(&ctx->refresh_lock); + return 0; + } + + if (dpu->core && dpu->core->init) + dpu->core->init(ctx); + if (dpu->core && dpu->core->ifconfig) + dpu->core->ifconfig(ctx); + + ctx->is_inited = true; + + up(&ctx->refresh_lock); + + return 0; +} + +static int sprd_dpu_uninit(struct sprd_dpu *dpu) +{ + struct dpu_context *ctx = &dpu->ctx; + + down(&ctx->refresh_lock); + + if (!dpu->ctx.is_inited) { + up(&ctx->refresh_lock); + return 0; + } + + if (dpu->core && dpu->core->uninit) + dpu->core->uninit(ctx); + + ctx->is_inited = false; + + up(&ctx->refresh_lock); + + return 0; +} + +static irqreturn_t sprd_dpu_isr(int irq, void *data) +{ + struct sprd_dpu *dpu = data; + struct dpu_context *ctx = &dpu->ctx; + u32 int_mask = 0; + + if (dpu->core && dpu->core->isr) + int_mask = dpu->core->isr(ctx); + + if (int_mask & DISPC_INT_ERR_MASK) + DRM_WARN("Warning: dpu underflow!\n"); + + if ((int_mask & DISPC_INT_DPI_VSYNC_MASK) && ctx->is_inited) + drm_crtc_handle_vblank(&dpu->crtc); + + return IRQ_HANDLED; +} + +static int sprd_dpu_irq_request(struct sprd_dpu *dpu) +{ + int err; + int irq_num; + + irq_num = irq_of_parse_and_map(dpu->dev.of_node, 0); + if (!irq_num) { + DRM_ERROR("error: dpu parse irq num failed\n"); + return -EINVAL; + } + DRM_INFO("dpu irq_num = %d\n", irq_num); + + irq_set_status_flags(irq_num, IRQ_NOAUTOEN); + err = devm_request_irq(&dpu->dev, irq_num, sprd_dpu_isr, + 0, "DISPC", dpu); + if (err) { + DRM_ERROR("error: dpu request irq failed\n"); + return -EINVAL; + } + dpu->ctx.irq = irq_num; + + return 0; +} + +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 err; + + DRM_INFO("%s()\n", __func__); + + plane = sprd_plane_init(drm, dpu); + if (IS_ERR_OR_NULL(plane)) { + err = PTR_ERR(plane); + return err; + } + + err = sprd_crtc_init(drm, &dpu->crtc, plane); + if (err) + return err; + + sprd_dpu_irq_request(dpu); + + return 0; +} + +static void sprd_dpu_unbind(struct device *dev, struct device *master, + void *data) +{ + struct sprd_dpu *dpu = dev_get_drvdata(dev); + + DRM_INFO("%s()\n", __func__); + + 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_node *np) +{ + struct resource r; + struct dpu_context *ctx = &dpu->ctx; + + if (of_address_to_resource(np, 0, &r)) { + DRM_ERROR("parse dt base address failed\n"); + return -ENODEV; + } + ctx->base = (unsigned long)ioremap_nocache(r.start, + resource_size(&r)); + if (ctx->base == 0) { + DRM_ERROR("ioremap base address failed\n"); + return -EFAULT; + } + + sema_init(&ctx->refresh_lock, 1); + + return 0; +} + +static const struct dpu_ops sharkl3_dpu = { + .core = &sharkl3_dpu_core_ops, +}; + +static const struct of_device_id dpu_match_table[] = { + { .compatible = "sprd,sharkl3-dpu", + .data = &sharkl3_dpu }, + { }, +}; + +static int sprd_dpu_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_node(dpu_match_table, np); + const struct dpu_ops *pdata; + struct sprd_dpu *dpu; + int ret; + + dpu = devm_kzalloc(&pdev->dev, sizeof(*dpu), GFP_KERNEL); + if (!dpu) + return -ENOMEM; + + pdata = of_device_get_match_data(&pdev->dev); + if (pdata) { + dpu->core = pdata->core; + dpu->ctx.version = "dpu-r2p0"; + } else { + DRM_ERROR("Can't get %s ops data\n", of_id->name); + return -EINVAL; + } + + ret = sprd_dpu_context_init(dpu, np); + if (ret) + return ret; + + platform_set_drvdata(pdev, dpu); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + 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..5941baa --- /dev/null +++ b/drivers/gpu/drm/sprd/sprd_dpu.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Unisoc Inc. + */ + +#ifndef __SPRD_DPU_H__ +#define __SPRD_DPU_H__ + +#include +#include +#include +#include +#include +#include +#include