Skip to contentSkip to conversation

Add Unisoc's drm kms module

PATCH Superseded Kevin Tang 5 participants 37 messages Feb 22, 2021 – Apr 21, 2021 Page 1 of 2

Subsystems (4)

v4 of 8 Superseded Latest is v8 →

lore
Share & tools
lore

Kevin Tang

author
#0 lore raw

v4 cover Add Unisoc's drm kms module

ChangeList:
RFC v1:
1. only upstream modeset and atomic at first commit.
2. remove some unused code;
3. use alpha and blend_mode properties;
3. add yaml support;
4. remove auto-adaptive panel driver;
5. bugfix

RFC v2:
1. add sprd crtc and plane module for KMS, preparing for multi crtc&encoder
2. remove gem drivers, use generic CMA handlers
3. remove redundant "module_init", all the sub modules loading by KMS

RFC v3:
1. multi crtc&encoder design have problem, so rollback to v1

RFC v4:
1. update to gcc-linaro-7.5.0
2. update to Linux 5.6-rc3
3. remove pm_runtime support
4. add COMPILE_TEST, remove unused kconfig
5. "drm_dev_put" on drm_unbind
6. fix some naming convention issue
7. remove semaphore lock for crtc flip
8. remove static variables

RFC v5:
1. optimize encoder and connector code implementation
2. use "platform_get_irq" and "platform_get_resource"
3. drop useless function return type, drop unless debug log
4. custom properties should be separate, so drop it
5. use DRM_XXX replase pr_xxx
6. drop dsi&dphy hal callback ops
7. drop unless callback ops checking
8. add comments for sprd dpu structure

RFC v6:
1. Access registers via readl/writel
2. Checking for unsupported KMS properties (format, rotation, blend_mode, etc) on plane_check ops
3. Remove always true checks for dpu core ops

RFC v7:
1. Fix DTC unit name warnings
2. Fix the problem of maintainers
3. Call drmm_mode_config_init to mode config init
4. Embed drm_device in sprd_drm and use devm_drm_dev_alloc
5. Replace DRM_XXX with drm_xxx on KMS module, but not suitable for other subsystems
6. Remove plane_update stuff, dpu handles all the HW update in crtc->atomic_flush
7. Dsi&Dphy Code structure adjustment, all move to "sprd/"

v0:
1. Remove dpu_core_ops stuff layer for sprd drtc driver, but dpu_layer need to keeping. Because all the HW update in crtc->atomic_flush, we need temporary storage all layers for the dpu pageflip of atomic_flush.
2. Add ports subnode with port@X.

v1:
1. Remove dphy and dsi graph binding, merge the dphy driver into the dsi.
2. Add commit messages for Unisoc's virtual nodes.

v2:
1. Use drm_xxx to replace all DRM_XXX.
2. Use kzalloc to replace devm_kzalloc for sprd_dsi/sprd_dpu structure init.
3. Remove dpu_core_ops midlayer.

v3:
1. Remove dpu_layer midlayer and commit layers by aotmic_update

v4:
1. Move the devm_drm_dev_alloc to master_ops->bind function.
2. The managed drmm_mode_config_init() it is no longer necessary for drivers to explicitly call drm_mode_config_cleanup, so delete it.
3. Use drmm_helpers to allocate crtc ,planes and encoder.
4. Move allocate crtc ,planes, encoder to bind funtion.
5. Move rotation enum definitions to crtc layer reg bitfields.

Kevin Tang (6):
dt-bindings: display: add Unisoc's drm master bindings drm/sprd: add Unisoc's drm kms master
dt-bindings: display: add Unisoc's dpu bindings drm/sprd: add Unisoc's drm display controller driver
dt-bindings: display: add Unisoc's mipi dsi controller bindings drm/sprd: add Unisoc's drm mipi dsi&dphy driver

 .../display/sprd/sprd,display-subsystem.yaml  |   64 +
 .../display/sprd/sprd,sharkl3-dpu.yaml        |   77 +
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   |  102 ++
 drivers/gpu/drm/Kconfig                       |    2 +
 drivers/gpu/drm/Makefile                      |    1 +
 drivers/gpu/drm/sprd/Kconfig                  |   13 +
 drivers/gpu/drm/sprd/Makefile                 |    8 +
 drivers/gpu/drm/sprd/dw_dsi_ctrl.c            |  794 +++++++++
 drivers/gpu/drm/sprd/dw_dsi_ctrl.h            | 1475 +++++++++++++++++
 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c        |  157 ++
 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h        |   26 +
 drivers/gpu/drm/sprd/megacores_pll.c          |  317 ++++
 drivers/gpu/drm/sprd/megacores_pll.h          |  146 ++
 drivers/gpu/drm/sprd/sprd_dpu.c               |  964 +++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h               |  109 ++
 drivers/gpu/drm/sprd/sprd_drm.c               |  219 +++
 drivers/gpu/drm/sprd/sprd_drm.h               |   19 +
 drivers/gpu/drm/sprd/sprd_dsi.c               | 1132 +++++++++++++
 drivers/gpu/drm/sprd/sprd_dsi.h               |  104 ++
 19 files changed, 5729 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,display-subsystem.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml
 create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml
 create mode 100644 drivers/gpu/drm/sprd/Kconfig
 create mode 100644 drivers/gpu/drm/sprd/Makefile
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl.c
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl.h
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h
 create mode 100644 drivers/gpu/drm/sprd/megacores_pll.c
 create mode 100644 drivers/gpu/drm/sprd/megacores_pll.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_drm.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
Signature · 6 lines
2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
author
#1 lore raw

PATCH v4 1/6 dt-bindings: display: add Unisoc's drm master bindings

From: Kevin Tang <[email protected]>

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;

---
 .../display/sprd/sprd,display-subsystem.yaml  | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,display-subsystem.yaml
Documentation/devicetree/bindings/display/sprd/sprd,display-subsystem.yaml new file +64 -0
new file mode 100644
index 000000000..3d107e943
@@ -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 <[email protected]>
+
+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>;
+    };
+
Signature · 6 lines
2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
author
#2 lore raw

PATCH v4 2/6 drm/sprd: add Unisoc's drm kms master

In reply to Kevin Tang

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v4:
- Move the devm_drm_dev_alloc to master_ops->bind function.
- The managed drmm_mode_config_init() it is no longer necessary for drivers to explicitly call drm_mode_config_cleanup, so delete it.

---
 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 | 217 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
 6 files changed, 253 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
drivers/gpu/drm/Kconfig +2 -0
index 8bf103de1..9d6ce2867 100644
@@ -382,6 +382,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
drivers/gpu/drm/Makefile +1 -0
index 02c229392..42d211d9c 100644
@@ -126,3 +126,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/
drivers/gpu/drm/sprd/Kconfig new file +12 -0
new file mode 100644
index 000000000..6e80cc9f3
@@ -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.
+
drivers/gpu/drm/sprd/Makefile new file +5 -0
new file mode 100644
index 000000000..86d95d93a
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +217 -0

217 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_drm.h new file +16 -0
new file mode 100644
index 000000000..9781fd591
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DRM_H_
+#define _SPRD_DRM_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_print.h>
+
+struct sprd_drm {
+	struct drm_device drm;
+};
+
+#endif /* _SPRD_DRM_H_ */
Signature · 6 lines
2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
maintainer
#3 lore raw

Re: [PATCH v4 2/6] drm/sprd: add Unisoc's drm kms master

Quoted 102 lines · Kevin Tang

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v4:
- Move the devm_drm_dev_alloc to master_ops->bind function.
- The managed drmm_mode_config_init() it is no longer necessary for drivers to explicitly call drm_mode_config_cleanup, so delete it.

---
 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 | 217 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
 6 files changed, 253 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
drivers/gpu/drm/Kconfig +2 -0
index 8bf103de1..9d6ce2867 100644
@@ -382,6 +382,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
drivers/gpu/drm/Makefile +1 -0
index 02c229392..42d211d9c 100644
@@ -126,3 +126,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/
drivers/gpu/drm/sprd/Kconfig new file +12 -0
new file mode 100644
index 000000000..6e80cc9f3
@@ -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.
+
drivers/gpu/drm/sprd/Makefile new file +5 -0
new file mode 100644
index 000000000..86d95d93a
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +15 -0
new file mode 100644
index 000000000..a1d3ed655
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>

drivers/gpu/drm/sprd/sprd_drm.c line 25

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "sprd_drm.h"
+
+#define DRIVER_NAME    "sprd"
+#define DRIVER_DESC    "Spreadtrum SoCs' DRM Driver"

Prefer to s/Spreadtrum/Unisoc.
Spreadtrum was acquired by Unisoc and never existed any more. But "sprd" as a vendor prefix is kept for historical reasons.

-Orson

Quoted 217 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +192 -0
+#define DRIVER_DATE    "20200201"
+#define DRIVER_MAJOR   1
+#define DRIVER_MINOR   0
+
+static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
+       .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void sprd_drm_mode_config_init(struct drm_device *drm)
+{
+       drm->mode_config.min_width = 0;
+       drm->mode_config.min_height = 0;
+       drm->mode_config.max_width = 8192;
+       drm->mode_config.max_height = 8192;
+       drm->mode_config.allow_fb_modifiers = true;
+
+       drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
+       drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
+
+static struct drm_driver sprd_drm_drv = {
+       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+       .fops                   = &sprd_drm_fops,
+
+       /* GEM Operations */
+       DRM_GEM_CMA_DRIVER_OPS,
+
+       .name                   = DRIVER_NAME,
+       .desc                   = DRIVER_DESC,
+       .date                   = DRIVER_DATE,
+       .major                  = DRIVER_MAJOR,
+       .minor                  = DRIVER_MINOR,
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm;
+       struct sprd_drm *sprd;
+       int ret;
+
+       sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm, drm);
+       if (IS_ERR(sprd))
+               return PTR_ERR(sprd);
+
+       drm = &sprd->drm;
+       platform_set_drvdata(pdev, drm);
+
+       ret = drmm_mode_config_init(drm);
+       if (ret)
+               return ret;
+
+       sprd_drm_mode_config_init(drm);
+
+       /* bind and init sub drivers */
+       ret = component_bind_all(drm->dev, drm);
+       if (ret) {
+               drm_err(drm, "failed to bind all component.\n");
+               return ret;
+       }
+
+       /* vblank init */
+       ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+       if (ret) {
+               drm_err(drm, "failed to initialize vblank.\n");
+               goto err_unbind_all;
+       }
+       /* 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);
+       return ret;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+       struct drm_device *drm = dev_get_drvdata(dev);
+
+       drm_dev_unregister(drm);
+
+       drm_kms_helper_poll_fini(drm);
+
+       component_unbind_all(drm->dev, drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+       .bind = sprd_drm_bind,
+       .unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+       return dev->of_node == data;
+}
+
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       ret = dma_set_mask_and_coherent(dev, ~0UL);
+       if (ret) {
+               dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
+               return ret;
+       }
+
+       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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_drm.h new file +16 -1
new file mode 100644
index 000000000..9781fd591
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DRM_H_
+#define _SPRD_DRM_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_print.h>
+
+struct sprd_drm {
+       struct drm_device drm;
+};
+
+#endif /* _SPRD_DRM_H_ */
--

2.29.0

author
#4 lore raw

Re: [PATCH v4 2/6] drm/sprd: add Unisoc's drm kms master

In reply to Kevin Tang

Hi Sam Ravnborg,
Do you have more comments about my patch?

Looking forward to your reply, thank you.

Quoted 334 lines · Kevin Tang <[email protected]> 于2021年2月22日周一 下午9:28写道:

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v4:
- Move the devm_drm_dev_alloc to master_ops->bind function.
- The managed drmm_mode_config_init() it is no longer necessary for
drivers to explicitly call drm_mode_config_cleanup, so delete it.

---
 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 | 217 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
 6 files changed, 253 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
drivers/gpu/drm/Kconfig +2 -0
index 8bf103de1..9d6ce2867 100644
@@ -382,6 +382,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
drivers/gpu/drm/Makefile +1 -0
index 02c229392..42d211d9c 100644
@@ -126,3 +126,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/
drivers/gpu/drm/sprd/Kconfig new file +12 -0
new file mode 100644
index 000000000..6e80cc9f3
@@ -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.
+
drivers/gpu/drm/sprd/Makefile new file +5 -0
new file mode 100644
index 000000000..86d95d93a
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +30 -0
new file mode 100644
index 000000000..a1d3ed655
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#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 = {

drivers/gpu/drm/sprd/sprd_drm.c new file +25 -0
+       .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,

drivers/gpu/drm/sprd/sprd_drm.c new file +20 -0
+       .fops                   = &sprd_drm_fops,
+
+       /* GEM Operations */
+       DRM_GEM_CMA_DRIVER_OPS,
+
+       .name                   = DRIVER_NAME,
+       .desc                   = DRIVER_DESC,
+       .date                   = DRIVER_DATE,
+       .major                  = DRIVER_MAJOR,
+       .minor                  = DRIVER_MINOR,
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm;
+       struct sprd_drm *sprd;
+       int ret;
+
+       sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm,

drm);

drivers/gpu/drm/sprd/sprd_drm.c new file +76 -0
+       if (IS_ERR(sprd))
+               return PTR_ERR(sprd);
+
+       drm = &sprd->drm;
+       platform_set_drvdata(pdev, drm);
+
+       ret = drmm_mode_config_init(drm);
+       if (ret)
+               return ret;
+
+       sprd_drm_mode_config_init(drm);
+
+       /* bind and init sub drivers */
+       ret = component_bind_all(drm->dev, drm);
+       if (ret) {
+               drm_err(drm, "failed to bind all component.\n");
+               return ret;
+       }
+
+       /* vblank init */
+       ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+       if (ret) {
+               drm_err(drm, "failed to initialize vblank.\n");
+               goto err_unbind_all;
+       }
+       /* 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);
+       return ret;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+       struct drm_device *drm = dev_get_drvdata(dev);
+
+       drm_dev_unregister(drm);
+
+       drm_kms_helper_poll_fini(drm);
+
+       component_unbind_all(drm->dev, drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+       .bind = sprd_drm_bind,
+       .unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+       return dev->of_node == data;
+}
+
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       ret = dma_set_mask_and_coherent(dev, ~0UL);
+       if (ret) {
+               dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n",

ret);

drivers/gpu/drm/sprd/sprd_drm.c new file +18 -0
+               return ret;
+       }
+
+       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");

drivers/gpu/drm/sprd/sprd_drm.c new file +48 -0
+               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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_drm.h new file +16 -1
new file mode 100644
index 000000000..9781fd591
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DRM_H_
+#define _SPRD_DRM_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_print.h>
+
+struct sprd_drm {
+       struct drm_device drm;
+};
+
+#endif /* _SPRD_DRM_H_ */
--
Quoted 3 lines

2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#5 lore raw

Re: [PATCH v4 2/6] drm/sprd: add Unisoc's drm kms master

In reply to Kevin Tang

Hi

Quoted 6 lines · Kevin Tang

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

^ internal

Quoted 51 lines

peripherals through libdrm or libkms, it's test ok by modetest (DRM/KMS test tool) and Android HWComposer.

Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>

v4:
- Move the devm_drm_dev_alloc to master_ops->bind function.
- The managed drmm_mode_config_init() it is no longer necessary for drivers to explicitly call drm_mode_config_cleanup, so delete it.

---
 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 | 217 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
 6 files changed, 253 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
drivers/gpu/drm/Kconfig +2 -0
index 8bf103de1..9d6ce2867 100644
@@ -382,6 +382,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
drivers/gpu/drm/Makefile +1 -0
index 02c229392..42d211d9c 100644
@@ -126,3 +126,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/
drivers/gpu/drm/sprd/Kconfig new file +0 -0
new file mode 100644
index 000000000..6e80cc9f3

drivers/gpu/drm/sprd/Kconfig line 8

Quoted 8 lines
drivers/gpu/drm/sprd/Kconfig new file +8 -0
@@ -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

I guess this should rather be moved to your DSI introduction patch?

drivers/gpu/drm/sprd/Makefile line 3

Quoted 13 lines
drivers/gpu/drm/sprd/Kconfig new file +4 -0
+	help
+	  Choose this option if you have a Unisoc chipset.
+	  If M is selected the module will be called sprd_drm.
+
drivers/gpu/drm/sprd/Makefile new file +3 -0
new file mode 100644
index 000000000..86d95d93a
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)

Is it really needed? I'm not seeing any header that aren't in the include path already.

Quoted 150 lines
drivers/gpu/drm/sprd/Makefile new file +1 -0
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +143 -0
new file mode 100644
index 000000000..a1d3ed655
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "sprd_drm.h"
+
+#define DRIVER_NAME	"sprd"
+#define DRIVER_DESC	"Spreadtrum SoCs' DRM Driver"
+#define DRIVER_DATE	"20200201"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void sprd_drm_mode_config_init(struct drm_device *drm)
+{
+	drm->mode_config.min_width = 0;
+	drm->mode_config.min_height = 0;
+	drm->mode_config.max_width = 8192;
+	drm->mode_config.max_height = 8192;
+	drm->mode_config.allow_fb_modifiers = true;
+
+	drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
+	drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
+
+static struct drm_driver sprd_drm_drv = {
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+	.fops			= &sprd_drm_fops,
+
+	/* GEM Operations */
+	DRM_GEM_CMA_DRIVER_OPS,
+
+	.name			= DRIVER_NAME,
+	.desc			= DRIVER_DESC,
+	.date			= DRIVER_DATE,
+	.major			= DRIVER_MAJOR,
+	.minor			= DRIVER_MINOR,
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm;
+	struct sprd_drm *sprd;
+	int ret;
+
+	sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm, drm);
+	if (IS_ERR(sprd))
+		return PTR_ERR(sprd);
+
+	drm = &sprd->drm;
+	platform_set_drvdata(pdev, drm);
+
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	sprd_drm_mode_config_init(drm);
+
+	/* bind and init sub drivers */
+	ret = component_bind_all(drm->dev, drm);
+	if (ret) {
+		drm_err(drm, "failed to bind all component.\n");
+		return ret;
+	}
+
+	/* vblank init */
+	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+	if (ret) {
+		drm_err(drm, "failed to initialize vblank.\n");
+		goto err_unbind_all;
+	}
+	/* 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);
+	return ret;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+
+	drm_dev_unregister(drm);
+
+	drm_kms_helper_poll_fini(drm);
+
+	component_unbind_all(drm->dev, drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+	.bind = sprd_drm_bind,
+	.unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+

drivers/gpu/drm/sprd/sprd_drm.c line 153

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	ret = dma_set_mask_and_coherent(dev, ~0UL);
+	if (ret) {
+		dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
+		return ret;
+	}

It's not really clear to me what it's here for. Could you explain with a comment here?

Thanks
Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#6 lore raw

Re: [PATCH v4 2/6] drm/sprd: add Unisoc's drm kms master

Quoted 66 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午6:53写道:

Hi

Quoted 6 lines · Kevin Tang

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

^ internal

Quoted 10 lines

peripherals through libdrm or libkms, it's test ok by modetest (DRM/KMS test tool) and Android HWComposer.

Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>

v4:
- Move the devm_drm_dev_alloc to master_ops->bind function.
- The managed drmm_mode_config_init() it is no longer necessary for

drivers to explicitly call drm_mode_config_cleanup, so delete it.

Quoted 43 lines
---
 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 | 217 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
 6 files changed, 253 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
drivers/gpu/drm/Kconfig +2 -0
index 8bf103de1..9d6ce2867 100644
@@ -382,6 +382,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
drivers/gpu/drm/Makefile +1 -0
index 02c229392..42d211d9c 100644
@@ -126,3 +126,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/
drivers/gpu/drm/sprd/Kconfig new file +2 -0
new file mode 100644
index 000000000..6e80cc9f3
@@ -0,0 +1,12 @@
+config DRM_SPRD
+     tristate "DRM Support for Unisoc SoCs Platform"
Quoted 9 lines

drivers/gpu/drm/sprd/Kconfig line 8

Quoted 6 lines
drivers/gpu/drm/sprd/Kconfig new file +6 -0
+     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

I guess this should rather be moved to your DSI introduction patch?

Thks, it's will be fixed on patch v5.

Quoted 7 lines

Review

Quoted 6 lines
drivers/gpu/drm/sprd/Kconfig new file +4 -0
+     help
+       Choose this option if you have a Unisoc chipset.
+       If M is selected the module will be called sprd_drm.
+
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/Makefile

Quoted 12 lines

drivers/gpu/drm/sprd/Makefile line 3

Quoted 8 lines

new file mode 100644
index 000000000..86d95d93a

drivers/gpu/drm/sprd/Makefile new file +3 -0
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)

Is it really needed? I'm not seeing any header that aren't in the include path already.

Thks, it's will be fixed on patch v5.

Quoted 164 lines

Review

Quoted 3 lines
drivers/gpu/drm/sprd/Makefile new file +1 -0
+obj-y := sprd_drm.o
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_drm.c

Quoted 25 lines

new file mode 100644
index 000000000..a1d3ed655

drivers/gpu/drm/sprd/sprd_drm.c new file +20 -0
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>

drivers/gpu/drm/sprd/sprd_drm.c line 30

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+
+#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 = {

Quoted 15 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +15 -0
+     .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;

drivers/gpu/drm/sprd/sprd_drm.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+     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,

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+     .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,

drivers/gpu/drm/sprd/sprd_drm.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+     struct platform_device *pdev = to_platform_device(dev);
+     struct drm_device *drm;
+     struct sprd_drm *sprd;
+     int ret;
+
+     sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm,

drm);

Quoted 66 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +66 -0
+     if (IS_ERR(sprd))
+             return PTR_ERR(sprd);
+
+     drm = &sprd->drm;
+     platform_set_drvdata(pdev, drm);
+
+     ret = drmm_mode_config_init(drm);
+     if (ret)
+             return ret;
+
+     sprd_drm_mode_config_init(drm);
+
+     /* bind and init sub drivers */
+     ret = component_bind_all(drm->dev, drm);
+     if (ret) {
+             drm_err(drm, "failed to bind all component.\n");
+             return ret;
+     }
+
+     /* vblank init */
+     ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+     if (ret) {
+             drm_err(drm, "failed to initialize vblank.\n");
+             goto err_unbind_all;
+     }
+     /* 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);
+     return ret;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+     struct drm_device *drm = dev_get_drvdata(dev);
+
+     drm_dev_unregister(drm);
+
+     drm_kms_helper_poll_fini(drm);
+
+     component_unbind_all(drm->dev, drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+     .bind = sprd_drm_bind,
+     .unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+     return dev->of_node == data;

drivers/gpu/drm/sprd/sprd_drm.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+}
+
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+     struct device *dev = &pdev->dev;
+     int ret;
+
+     ret = dma_set_mask_and_coherent(dev, ~0UL);
+     if (ret) {
+             dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n",

ret);

Quoted 6 lines

drivers/gpu/drm/sprd/sprd_drm.c line 153

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +2 -0
+             return ret;
+     }

It's not really clear to me what it's here for. Could you explain with a comment here?

Thks, it's will be fixed on patch v5.

Quoted 4 lines

Thanks
Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

maintainer Acked-by on v4 2/6
#7 lore raw

Re: [PATCH v4 2/6] drm/sprd: add Unisoc's drm kms master

In reply to Kevin Tang

Hi,

I have just a few nits below plus the points that others made.

Am 22.02.21 um 14:28 schrieb Kevin Tang:

Quoted 57 lines

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v4:
- Move the devm_drm_dev_alloc to master_ops->bind function.
- The managed drmm_mode_config_init() it is no longer necessary for drivers to explicitly call drm_mode_config_cleanup, so delete it.

---
  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 | 217 ++++++++++++++++++++++++++++++++
  drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
  6 files changed, 253 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

drivers/gpu/drm/Kconfig +2 -0
index 8bf103de1..9d6ce2867 100644
@@ -382,6 +382,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
drivers/gpu/drm/Makefile +1 -0
index 02c229392..42d211d9c 100644
@@ -126,3 +126,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/
drivers/gpu/drm/sprd/Kconfig new file +0 -0
new file mode 100644
index 000000000..6e80cc9f3

drivers/gpu/drm/sprd/Kconfig line 8

Quoted 8 lines
drivers/gpu/drm/sprd/Kconfig new file +8 -0
@@ -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

Maybe keep the selects sorted by alphabet to make it more readable.

Quoted 209 lines
drivers/gpu/drm/sprd/Kconfig new file +4 -0
+	help
+	  Choose this option if you have a Unisoc chipset.
+	  If M is selected the module will be called sprd_drm.
+
drivers/gpu/drm/sprd/Makefile new file +5 -0
new file mode 100644
index 000000000..86d95d93a
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_drm.c new file +188 -0
new file mode 100644
index 000000000..a1d3ed655
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "sprd_drm.h"
+
+#define DRIVER_NAME	"sprd"
+#define DRIVER_DESC	"Spreadtrum SoCs' DRM Driver"
+#define DRIVER_DATE	"20200201"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = {
+	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void sprd_drm_mode_config_init(struct drm_device *drm)
+{
+	drm->mode_config.min_width = 0;
+	drm->mode_config.min_height = 0;
+	drm->mode_config.max_width = 8192;
+	drm->mode_config.max_height = 8192;
+	drm->mode_config.allow_fb_modifiers = true;
+
+	drm->mode_config.funcs = &sprd_drm_mode_config_funcs;
+	drm->mode_config.helper_private = &sprd_drm_mode_config_helper;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(sprd_drm_fops);
+
+static struct drm_driver sprd_drm_drv = {
+	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+	.fops			= &sprd_drm_fops,
+
+	/* GEM Operations */
+	DRM_GEM_CMA_DRIVER_OPS,
+
+	.name			= DRIVER_NAME,
+	.desc			= DRIVER_DESC,
+	.date			= DRIVER_DATE,
+	.major			= DRIVER_MAJOR,
+	.minor			= DRIVER_MINOR,
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm;
+	struct sprd_drm *sprd;
+	int ret;
+
+	sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm, drm);
+	if (IS_ERR(sprd))
+		return PTR_ERR(sprd);
+
+	drm = &sprd->drm;
+	platform_set_drvdata(pdev, drm);
+
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	sprd_drm_mode_config_init(drm);
+
+	/* bind and init sub drivers */
+	ret = component_bind_all(drm->dev, drm);
+	if (ret) {
+		drm_err(drm, "failed to bind all component.\n");
+		return ret;
+	}
+
+	/* vblank init */
+	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+	if (ret) {
+		drm_err(drm, "failed to initialize vblank.\n");
+		goto err_unbind_all;
+	}
+	/* 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);
+	return ret;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+
+	drm_dev_unregister(drm);
+
+	drm_kms_helper_poll_fini(drm);
+
+	component_unbind_all(drm->dev, drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+	.bind = sprd_drm_bind,
+	.unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	ret = dma_set_mask_and_coherent(dev, ~0UL);
+	if (ret) {
+		dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
+		return ret;
+	}
+
+	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,

drivers/gpu/drm/sprd/sprd_drm.c line 198

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+	},
+};
+
+static struct platform_driver *sprd_drm_drivers[]  = {
+	&sprd_drm_driver,
+};
+
+static int __init sprd_drm_init(void)
+{
+	int ret;

I think ret should just go away.

Quoted 42 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +19 -0
+
+	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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_drm.h new file +16 -0
new file mode 100644
index 000000000..9781fd591
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DRM_H_
+#define _SPRD_DRM_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_print.h>
+
+struct sprd_drm {
+	struct drm_device drm;
+};
+
+#endif /* _SPRD_DRM_H_ */
Signature · 6 lines
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer
author
#8 lore raw

Re: [PATCH v4 2/6] drm/sprd: add Unisoc's drm kms master

Hi Thomas,

First of all, thanks for comments.

Quoted 65 lines · Thomas Zimmermann <[email protected]> 于2021年4月8日周四 下午6:21写道:

Hi,

I have just a few nits below plus the points that others made.

Am 22.02.21 um 14:28 schrieb Kevin Tang:

Quoted 9 lines

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.

Quoted 7 lines
Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>

v4:
- Move the devm_drm_dev_alloc to master_ops->bind function.
- The managed drmm_mode_config_init() it is no longer necessary for

drivers to explicitly call drm_mode_config_cleanup, so delete it.

Quoted 43 lines
---
  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 | 217 ++++++++++++++++++++++++++++++++
  drivers/gpu/drm/sprd/sprd_drm.h |  16 +++
  6 files changed, 253 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

drivers/gpu/drm/Kconfig +2 -0
index 8bf103de1..9d6ce2867 100644
@@ -382,6 +382,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
drivers/gpu/drm/Makefile +1 -0
index 02c229392..42d211d9c 100644
@@ -126,3 +126,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/
drivers/gpu/drm/sprd/Kconfig new file +2 -0
new file mode 100644
index 000000000..6e80cc9f3
@@ -0,0 +1,12 @@
+config DRM_SPRD
+     tristate "DRM Support for Unisoc SoCs Platform"
Quoted 9 lines

drivers/gpu/drm/sprd/Kconfig line 8

Quoted 6 lines
drivers/gpu/drm/sprd/Kconfig new file +6 -0
+     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

Maybe keep the selects sorted by alphabet to make it more readable.

Thks, i will be fix it.

Quoted 221 lines

Review

Quoted 6 lines
drivers/gpu/drm/sprd/Kconfig new file +4 -0
+     help
+       Choose this option if you have a Unisoc chipset.
+       If M is selected the module will be called sprd_drm.
+
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/Makefile

Review

Quoted 11 lines

new file mode 100644
index 000000000..86d95d93a

drivers/gpu/drm/sprd/Makefile new file +5 -0
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+subdir-ccflags-y += -I$(srctree)/$(src)
+
+obj-y := sprd_drm.o
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_drm.c

Quoted 25 lines

new file mode 100644
index 000000000..a1d3ed655

drivers/gpu/drm/sprd/sprd_drm.c new file +20 -0
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>

drivers/gpu/drm/sprd/sprd_drm.c line 30

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+
+#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 = {

Quoted 15 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +15 -0
+     .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;

drivers/gpu/drm/sprd/sprd_drm.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+     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,

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+     .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,

drivers/gpu/drm/sprd/sprd_drm.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+};
+
+static int sprd_drm_bind(struct device *dev)
+{
+     struct platform_device *pdev = to_platform_device(dev);
+     struct drm_device *drm;
+     struct sprd_drm *sprd;
+     int ret;
+
+     sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm,

drm);

Quoted 66 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +66 -0
+     if (IS_ERR(sprd))
+             return PTR_ERR(sprd);
+
+     drm = &sprd->drm;
+     platform_set_drvdata(pdev, drm);
+
+     ret = drmm_mode_config_init(drm);
+     if (ret)
+             return ret;
+
+     sprd_drm_mode_config_init(drm);
+
+     /* bind and init sub drivers */
+     ret = component_bind_all(drm->dev, drm);
+     if (ret) {
+             drm_err(drm, "failed to bind all component.\n");
+             return ret;
+     }
+
+     /* vblank init */
+     ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+     if (ret) {
+             drm_err(drm, "failed to initialize vblank.\n");
+             goto err_unbind_all;
+     }
+     /* 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);
+     return ret;
+}
+
+static void sprd_drm_unbind(struct device *dev)
+{
+     struct drm_device *drm = dev_get_drvdata(dev);
+
+     drm_dev_unregister(drm);
+
+     drm_kms_helper_poll_fini(drm);
+
+     component_unbind_all(drm->dev, drm);
+}
+
+static const struct component_master_ops drm_component_ops = {
+     .bind = sprd_drm_bind,
+     .unbind = sprd_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+     return dev->of_node == data;

drivers/gpu/drm/sprd/sprd_drm.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+}
+
+static int sprd_drm_probe(struct platform_device *pdev)
+{
+     struct device *dev = &pdev->dev;
+     int ret;
+
+     ret = dma_set_mask_and_coherent(dev, ~0UL);
+     if (ret) {
+             dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n",

ret);

Quoted 8 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +8 -0
+             return ret;
+     }
+
+     return drm_of_component_probe(dev, compare_of, &drm_component_ops);
+}
+
+static int sprd_drm_remove(struct platform_device *pdev)
+{

drivers/gpu/drm/sprd/sprd_drm.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +10 -0
+     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");

Quoted 23 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +23 -0
+             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[]  = {
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_drm.c line 198

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +6 -0
+     &sprd_drm_driver,
+};
+
+static int __init sprd_drm_init(void)
+{
+     int ret;

I think ret should just go away.

Like this?
"return platform_register_drivers(sprd_drm_drivers, ARRAY_SIZE(sprd_drm_drivers));"
if so, i will fix it, thks.

Quoted 47 lines

Review

Quoted 20 lines
drivers/gpu/drm/sprd/sprd_drm.c new file +19 -0
+
+     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 <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
+MODULE_LICENSE("GPL v2");
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_drm.h

Quoted 12 lines

new file mode 100644
index 000000000..9781fd591

drivers/gpu/drm/sprd/sprd_drm.h new file +6 -0
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _SPRD_DRM_H_

drivers/gpu/drm/sprd/sprd_drm.h line 16

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_drm.h new file +10 -0
+#define _SPRD_DRM_H_
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_print.h>
+
+struct sprd_drm {
+     struct drm_device drm;
+};
+
+#endif /* _SPRD_DRM_H_ */
Quoted 9 lines

--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#9 lore raw

Re: [PATCH v4 2/6] drm/sprd: add Unisoc's drm kms master

Hi

Am 09.04.21 um 15:50 schrieb Kevin Tang:

Quoted 8 lines

> +static int __init sprd_drm_init(void)
> +{
> +     int ret;

I think ret should just go away.

Like this?
"return platform_register_drivers(sprd_drm_drivers, ARRAY_SIZE(sprd_drm_drivers));"

Sure.

Best regards
Thomas

Quoted 66 lines

if so, i will fix it, thks.

    Acked-by: Thomas Zimmermann <[email protected]
    <mailto:[email protected]>>

     > +
     > +     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 <[email protected]
    <mailto:[email protected]>>");
     > +MODULE_AUTHOR("Kevin Tang <[email protected]
    <mailto:[email protected]>>");
     > +MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver");
     > +MODULE_LICENSE("GPL v2");
     > diff --git a/drivers/gpu/drm/sprd/sprd_drm.h
    b/drivers/gpu/drm/sprd/sprd_drm.h
     > new file mode 100644
     > index 000000000..9781fd591
     > --- /dev/null
     > +++ b/drivers/gpu/drm/sprd/sprd_drm.h
     > @@ -0,0 +1,16 @@
     > +/* SPDX-License-Identifier: GPL-2.0 */
     > +/*
     > + * Copyright (C) 2020 Unisoc Inc.
     > + */
     > +
     > +#ifndef _SPRD_DRM_H_
     > +#define _SPRD_DRM_H_
     > +
     > +#include <drm/drm_atomic.h>
     > +#include <drm/drm_print.h>
     > +
     > +struct sprd_drm {
     > +     struct drm_device drm;
     > +};
     > +
     > +#endif /* _SPRD_DRM_H_ */
     >

    -- 
    Thomas Zimmermann
    Graphics Driver Developer
    SUSE Software Solutions Germany GmbH
    Maxfeldstr. 5, 90409 Nürnberg, Germany
    (HRB 36809, AG Nürnberg)
    Geschäftsführer: Felix Imendörffer

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Signature · 6 lines
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#10 lore raw

PATCH v4 3/6 dt-bindings: display: add Unisoc's dpu bindings

In reply to Kevin Tang

From: Kevin Tang <[email protected]>

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.

---
 .../display/sprd/sprd,sharkl3-dpu.yaml        | 77 +++++++++++++++++++
 1 file changed, 77 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dpu.yaml new file +77 -0
new file mode 100644
index 000000000..4ebea60b8
@@ -0,0 +1,77 @@
+# 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 <[email protected]>
+
+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
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    minItems: 2
+
+  clock-names:
+    items:
+      - const: clk_src_128m
+      - const: clk_src_384m
+
+  power-domains:
+    maxItems: 1
+
+  iommus:
+    maxItems: 1
+
+  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 <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sprd,sc9860-clk.h>
+    dpu: dpu@63000000 {
+        compatible = "sprd,sharkl3-dpu";
+        reg = <0x63000000 0x1000>;
+        interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+        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>;
+            };
+        };
+    };
Signature · 6 lines
2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
author
#11 lore raw

PATCH v4 4/6 drm/sprd: add Unisoc's drm display controller driver

In reply to Kevin Tang

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dpu structure init.

v3:
- Remove dpu_layer stuff layer and commit layers by aotmic_update

v4:
- Use drmm_helpers to allocate crtc and planes.
- Move rotation enum definitions to crtc layer reg bitfields.
- Move allocate crtc and planes to bind funtion.

---
 drivers/gpu/drm/sprd/Kconfig    |   1 +
 drivers/gpu/drm/sprd/Makefile   |   4 +-
 drivers/gpu/drm/sprd/sprd_dpu.c | 964 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h | 109 ++++
 drivers/gpu/drm/sprd/sprd_drm.c |   1 +
 drivers/gpu/drm/sprd/sprd_drm.h |   2 +
 6 files changed, 1079 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
drivers/gpu/drm/sprd/Kconfig +1 -0
index 6e80cc9f3..9b4ef9aea 100644
@@ -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
drivers/gpu/drm/sprd/Makefile +2 -2
index 86d95d93a..6c25bfa99 100644
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-subdir-ccflags-y += -I$(srctree)/$(src)
+obj-y := sprd_drm.o \
+	sprd_dpu.o
 
-obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_dpu.c new file +964 -0

964 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_dpu.h new file +109 -0

109 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index a1d3ed655..c626c6caf 100644
@@ -191,6 +191,7 @@ static struct platform_driver sprd_drm_driver = {
 
 static struct platform_driver *sprd_drm_drivers[]  = {
 	&sprd_drm_driver,
+	&sprd_dpu_driver,
 };
 
 static int __init sprd_drm_init(void)
drivers/gpu/drm/sprd/sprd_drm.h +2 -0
index 9781fd591..85d4a8b9f 100644
@@ -13,4 +13,6 @@ struct sprd_drm {
 	struct drm_device drm;
 };
 
+extern struct platform_driver sprd_dpu_driver;
+
 #endif /* _SPRD_DRM_H_ */
Signature · 6 lines
2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
#12 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Hi,

Quoted 11 lines · Kevin Tang

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dpu structure init.

Quoted 7 lines

v3:
- Remove dpu_layer stuff layer and commit layers by aotmic_update

v4:
- Use drmm_helpers to allocate crtc and planes.
- Move rotation enum definitions to crtc layer reg bitfields.
- Move allocate crtc and planes to bind funtion.

There's a number of checkpatch warnings, make sure to fix them

Quoted 197 lines
---
 drivers/gpu/drm/sprd/Kconfig    |   1 +
 drivers/gpu/drm/sprd/Makefile   |   4 +-
 drivers/gpu/drm/sprd/sprd_dpu.c | 964 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h | 109 ++++
 drivers/gpu/drm/sprd/sprd_drm.c |   1 +
 drivers/gpu/drm/sprd/sprd_drm.h |   2 +
 6 files changed, 1079 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
drivers/gpu/drm/sprd/Kconfig +1 -0
index 6e80cc9f3..9b4ef9aea 100644
@@ -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
drivers/gpu/drm/sprd/Makefile +2 -2
index 86d95d93a..6c25bfa99 100644
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-subdir-ccflags-y += -I$(srctree)/$(src)
+obj-y := sprd_drm.o \
+	sprd_dpu.o
 
-obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_dpu.c new file +156 -0
new file mode 100644
index 000000000..75b7e40d9
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+
+/* Global control registers */
+#define REG_DPU_CTRL	0x04
+#define REG_DPU_CFG0	0x08
+#define REG_PANEL_SIZE	0x20
+#define REG_BLEND_SIZE	0x24
+#define REG_BG_COLOR	0x2C
+
+/* Layer0 control registers */
+#define REG_LAY_BASE_ADDR0	0x30
+#define REG_LAY_BASE_ADDR1	0x34
+#define REG_LAY_BASE_ADDR2	0x38
+#define REG_LAY_CTRL		0x40
+#define REG_LAY_SIZE		0x44
+#define REG_LAY_PITCH		0x48
+#define REG_LAY_POS		0x4C
+#define REG_LAY_ALPHA		0x50
+#define REG_LAY_CROP_START	0x5C
+
+/* Interrupt control registers */
+#define REG_DPU_INT_EN		0x1E0
+#define REG_DPU_INT_CLR		0x1E4
+#define REG_DPU_INT_STS		0x1E8
+
+/* DPI control registers */
+#define REG_DPI_CTRL		0x1F0
+#define REG_DPI_H_TIMING	0x1F4
+#define REG_DPI_V_TIMING	0x1F8
+
+/* MMU control registers */
+#define REG_MMU_EN			0x800
+#define REG_MMU_VPN_RANGE		0x80C
+#define REG_MMU_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)
+
+/* Layer control bits */
+#define BIT_DPU_LAY_EN				BIT(0)
+#define BIT_DPU_LAY_LAYER_ALPHA			(0x01 << 2)
+#define BIT_DPU_LAY_COMBO_ALPHA			(0x02 << 2)
+#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE		(0x00 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE		(0x01 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE		(0x02 << 4)
+#define BIT_DPU_LAY_FORMAT_ARGB8888			(0x03 << 4)
+#define BIT_DPU_LAY_FORMAT_RGB565			(0x04 << 4)
+#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3		(0x00 << 8)
+#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0		(0x01 << 8)
+#define BIT_DPU_LAY_NO_SWITCH			(0x00 << 10)
+#define BIT_DPU_LAY_RB_OR_UV_SWITCH		(0x01 << 10)
+#define BIT_DPU_LAY_MODE_BLEND_NORMAL		(0x00 << 16)
+#define BIT_DPU_LAY_MODE_BLEND_PREMULT		(0x01 << 16)
+#define BIT_DPU_LAY_ROTATION_0		(0x00 << 20)
+#define BIT_DPU_LAY_ROTATION_90		(0x01 << 20)
+#define BIT_DPU_LAY_ROTATION_180	(0x02 << 20)
+#define BIT_DPU_LAY_ROTATION_270	(0x03 << 20)
+#define BIT_DPU_LAY_ROTATION_0_M	(0x04 << 20)
+#define BIT_DPU_LAY_ROTATION_90_M	(0x05 << 20)
+#define BIT_DPU_LAY_ROTATION_180_M	(0x06 << 20)
+#define BIT_DPU_LAY_ROTATION_270_M	(0x07 << 20)
+
+/* Interrupt control & status bits */
+#define BIT_DPU_INT_DONE		BIT(0)
+#define BIT_DPU_INT_TE			BIT(1)
+#define BIT_DPU_INT_ERR			BIT(2)
+#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
+#define BIT_DPU_INT_VSYNC		BIT(5)
+#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,
+};
+
+struct sprd_plane {
+	struct drm_plane base;
+	u32 index;
+};
+
+static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct sprd_plane, base);
+}
+
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	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_err(dpu->drm, "--- iommu interrupt err: 0x%04x ---\n", val);
+
+		if (val & BIT_DPU_INT_MMU_INV_RD)
+			drm_err(dpu->drm, "iommu invalid read error, addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c line 166

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+				readl(ctx->base + REG_MMU_INV_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_INV_WR)
+			drm_err(dpu->drm, "iommu invalid write error, addr: 0x%08x\n",
+				readl(ctx->base + REG_MMU_INV_ADDR_WR));
+		if (val & BIT_DPU_INT_MMU_VAOR_RD)
+			drm_err(dpu->drm, "iommu va out of range read error, addr: 0x%08x\n",
+				readl(ctx->base + REG_MMU_VAOR_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_VAOR_WR)
+			drm_err(dpu->drm, "iommu va out of range write error, addr: 0x%08x\n",
+				readl(ctx->base + REG_MMU_VAOR_ADDR_WR));

Is that the IOMMU page fault interrupt? I would expect it to be in the iommu driver.

Quoted 348 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +348 -0
+		for (i = 0; i < 8; i++) {
+			reg_val = layer_reg_rd(ctx, REG_LAY_CTRL, i);
+			if (reg_val & 0x1)
+				drm_info(dpu->drm, "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 int dpu_wait_stop_done(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	int rc;
+
+	if (ctx->stopped)
+		return 0;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
+					       msecs_to_jiffies(500));
+	ctx->evt_stop = false;
+
+	ctx->stopped = true;
+
+	if (!rc) {
+		drm_err(dpu->drm, "dpu wait for stop done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int dpu_wait_update_done(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	int rc;
+
+	ctx->evt_update = false;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
+					       msecs_to_jiffies(500));
+
+	if (!rc) {
+		drm_err(dpu->drm, "dpu wait for reg update done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static u32 drm_format_to_dpu(struct drm_framebuffer *fb)
+{
+	u32 format = 0;
+
+	switch (fb->format->format) {
+	case DRM_FORMAT_BGRA8888:
+		/* BGRA8888 -> ARGB8888 */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+		/* RGBA8888 -> ABGR8888 */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* fallthrough */
+	case DRM_FORMAT_ABGR8888:
+		/* RB switch */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* fallthrough */
+	case DRM_FORMAT_ARGB8888:
+		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_XBGR8888:
+		/* RB switch */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* fallthrough */
+	case DRM_FORMAT_XRGB8888:
+		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_BGR565:
+		/* RB switch */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* fallthrough */
+	case DRM_FORMAT_RGB565:
+		format |= BIT_DPU_LAY_FORMAT_RGB565;
+		break;
+	case DRM_FORMAT_NV12:
+		/* 2-Lane: Yuv420 */
+		format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_NV21:
+		/* 2-Lane: Yuv420 */
+		format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV16:
+		/* 2-Lane: Yuv422 */
+		format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* UV endian */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV61:
+		/* 2-Lane: Yuv422 */
+		format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YUV420:
+		format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YVU420:
+		format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	default:
+		break;
+	}
+
+	return format;
+}
+
+static u32 drm_rotation_to_dpu(struct drm_plane_state *state)
+{
+	u32 rotation = 0;
+
+	switch (state->rotation) {
+	default:
+	case DRM_MODE_ROTATE_0:
+		rotation = BIT_DPU_LAY_ROTATION_0;
+		break;
+	case DRM_MODE_ROTATE_90:
+		rotation = BIT_DPU_LAY_ROTATION_90;
+		break;
+	case DRM_MODE_ROTATE_180:
+		rotation = BIT_DPU_LAY_ROTATION_180;
+		break;
+	case DRM_MODE_ROTATE_270:
+		rotation = BIT_DPU_LAY_ROTATION_270;
+		break;
+	case DRM_MODE_REFLECT_Y:
+		rotation = BIT_DPU_LAY_ROTATION_180_M;
+		break;
+	case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
+		rotation = BIT_DPU_LAY_ROTATION_90_M;
+		break;
+	case DRM_MODE_REFLECT_X:
+		rotation = BIT_DPU_LAY_ROTATION_0_M;
+		break;
+	case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
+		rotation = BIT_DPU_LAY_ROTATION_270_M;
+		break;
+	}
+
+	return rotation;
+}
+
+static u32 drm_blend_to_dpu(struct drm_plane_state *state)
+{
+	u32 blend = 0;
+
+	switch (state->pixel_blend_mode) {
+	case DRM_MODE_BLEND_COVERAGE:
+		/* alpha mode select - combo alpha */
+		blend |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Normal mode */
+		blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
+		break;
+	case DRM_MODE_BLEND_PREMULTI:
+		/* alpha mode select - combo alpha */
+		blend |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Pre-mult mode */
+		blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
+		break;
+	case DRM_MODE_BLEND_PIXEL_NONE:
+	default:
+		/* don't do blending, maybe RGBX */
+		/* alpha mode select - layer alpha */
+		blend |= BIT_DPU_LAY_LAYER_ALPHA;
+		break;
+	}
+
+	return blend;
+}
+
+static void sprd_dpu_layer(struct sprd_dpu *dpu, struct sprd_plane *plane,
+			struct drm_plane_state *state)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	struct drm_gem_cma_object *cma_obj;
+	const struct drm_format_info *info;
+	struct drm_framebuffer *fb = state->fb;
+	u32 addr, size, offset, pitch, blend, format, rotation;
+	u32 src_x = state->src_x >> 16;
+	u32 src_y = state->src_y >> 16;
+	u32 src_w = state->src_w >> 16;
+	u32 src_h = state->src_h >> 16;
+	u32 dst_x = state->crtc_x;
+	u32 dst_y = state->crtc_y;
+	u32 alpha = state->alpha;
+	int i;
+
+	offset = (dst_x & 0xffff) | (dst_y << 16);
+	size = (src_w & 0xffff) | (src_h << 16);
+
+	for (i = 0; i < fb->format->num_planes; i++) {
+		cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+		addr = cma_obj->paddr + fb->offsets[i];
+
+		if (i == 0)
+			layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, addr, plane->index);
+		else if (i == 1)
+			layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, addr, plane->index);
+		else
+			layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, addr, plane->index);
+	}
+
+	info = drm_format_info(fb->format->format);
+	if (fb->format->num_planes == 3) {
+		/* UV pitch is 1/2 of Y pitch */
+		pitch = (fb->pitches[0] / info->cpp[0]) |
+				(fb->pitches[0] / info->cpp[0] << 15);
+	} else {
+		pitch = fb->pitches[0] / info->cpp[0];
+	}
+
+	layer_reg_wr(ctx, REG_LAY_POS, offset, plane->index);
+	layer_reg_wr(ctx, REG_LAY_SIZE, size, plane->index);
+	layer_reg_wr(ctx, REG_LAY_CROP_START,
+			src_y << 16 | src_x, plane->index);
+	layer_reg_wr(ctx, REG_LAY_ALPHA, alpha, plane->index);
+	layer_reg_wr(ctx, REG_LAY_PITCH, pitch, plane->index);
+
+	format = drm_format_to_dpu(fb);
+	blend = drm_blend_to_dpu(state);
+	rotation = drm_rotation_to_dpu(state);
+
+	layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN |
+				format |
+				blend |
+				rotation,
+				plane->index);
+}
+
+static void sprd_dpu_flip(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 reg_val;
+
+	/*
+	 * Make sure the dpu is in stop status. DPU has no shadow
+	 * registers in EDPI mode. So the config registers can only be
+	 * updated in the rising edge of DPU_RUN bit.
+	 */
+	if (ctx->if_type == SPRD_DPU_IF_EDPI)
+		dpu_wait_stop_done(dpu);
+
+	/* update trigger and wait */
+	if (ctx->if_type == SPRD_DPU_IF_DPI) {
+		if (!ctx->stopped) {
+			dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
+			dpu_wait_update_done(dpu);
+		}
+
+		dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR);
+	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+		dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+		ctx->stopped = false;
+	}
+
+	/*
+	 * If the following interrupt was disabled in isr,
+	 * re-enable it.
+	 */
+	reg_val = 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);
+}
+
+static void sprd_dpu_init(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 size;
+
+	writel(0x00, ctx->base + REG_BG_COLOR);
+
+	size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
+	writel(size, ctx->base + REG_PANEL_SIZE);
+	writel(size, ctx->base + REG_BLEND_SIZE);
+
+	writel(0x00, ctx->base + REG_MMU_EN);
+	writel(0x00, ctx->base + REG_MMU_PPN1);
+	writel(0xffff, ctx->base + REG_MMU_RANGE1);
+	writel(0x00, ctx->base + REG_MMU_PPN2);
+	writel(0xffff, ctx->base + REG_MMU_RANGE2);
+	writel(0x1ffff, ctx->base + REG_MMU_VPN_RANGE);
+}
+
+static void sprd_dpu_fini(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	writel(0x00, ctx->base + REG_DPU_INT_EN);
+	writel(0xff, ctx->base + REG_DPU_INT_CLR);
+}
+
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 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;

drivers/gpu/drm/sprd/sprd_dpu.c line 525

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+		writel(reg_val, ctx->base + REG_DPI_H_TIMING);
+
+		reg_val = ctx->vm.vsync_len << 0 |
+			  ctx->vm.vback_porch << 8 |
+			  ctx->vm.vfront_porch << 20;
+		writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+		if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+			drm_warn(dpu->drm, "Warning: (vsync + vbp) < 32, "
+				"underflow risk!\n");

I don't think a warning is appropriate here. Maybe we should just outright reject any mode that uses it?

Quoted 162 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +162 -0
+		/* 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 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(int_mask, ctx->base + REG_DPU_INT_EN);
+}
+
+void sprd_dpu_run(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+	ctx->stopped = false;
+}
+
+void sprd_dpu_stop(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	if (ctx->if_type == SPRD_DPU_IF_DPI)
+		dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP);
+
+	dpu_wait_stop_done(dpu);
+}
+
+static int sprd_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc_state *crtc_state;
+	u32 fmt;
+
+	if (!fb || !state->crtc)
+		return 0;
+
+	fmt = drm_format_to_dpu(fb);
+	if (!fmt)
+		return -EINVAL;
+
+	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	return drm_atomic_helper_check_plane_state(state, crtc_state,
+						  DRM_PLANE_HELPER_NO_SCALING,
+						  DRM_PLANE_HELPER_NO_SCALING,
+						  true, true);
+}
+
+static void sprd_plane_atomic_update(struct drm_plane *drm_plane,
+				    struct drm_plane_state *old_state)
+{
+	struct drm_plane_state *state = drm_plane->state;
+	struct sprd_plane *plane = to_sprd_plane(drm_plane);
+	struct sprd_dpu *dpu = to_sprd_crtc(state->crtc);
+
+	/* start configure dpu layers */
+	sprd_dpu_layer(dpu, plane, state);
+}
+
+static void sprd_plane_atomic_disable(struct drm_plane *drm_plane,
+				     struct drm_plane_state *old_state)
+{
+	struct sprd_plane *plane = to_sprd_plane(drm_plane);
+	struct sprd_dpu *dpu = to_sprd_crtc(old_state->crtc);
+
+	layer_reg_wr(&dpu->ctx, REG_LAY_CTRL, 0x00, plane->index);
+}
+
+static void sprd_plane_create_properties(struct sprd_plane *plane, int index)
+{
+	unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				       BIT(DRM_MODE_BLEND_PREMULTI) |
+				       BIT(DRM_MODE_BLEND_COVERAGE);
+
+	/* create rotation property */
+	drm_plane_create_rotation_property(&plane->base,
+					   DRM_MODE_ROTATE_0,
+					   DRM_MODE_ROTATE_MASK |
+					   DRM_MODE_REFLECT_MASK);
+
+	/* create alpha property */
+	drm_plane_create_alpha_property(&plane->base);
+
+	/* create blend mode property */
+	drm_plane_create_blend_mode_property(&plane->base, supported_modes);
+
+	/* create zpos property */
+	drm_plane_create_zpos_immutable_property(&plane->base, index);
+}
+
+static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
+	.atomic_check = sprd_plane_atomic_check,
+	.atomic_update = sprd_plane_atomic_update,
+	.atomic_disable = sprd_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs sprd_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct sprd_plane *sprd_plane_init(struct drm_device *drm)
+{
+	struct sprd_plane *plane, *primary;
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		plane = drmm_universal_plane_alloc(drm, struct sprd_plane, base,
+					       1, &sprd_plane_funcs,
+					       primary_fmts, ARRAY_SIZE(primary_fmts),
+					       NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
+		if (IS_ERR(plane)) {
+			drm_err(drm, "fail to init primary plane\n");
+			return plane;
+		}
+
+		drm_plane_helper_add(&plane->base, &sprd_plane_helper_funcs);
+
+		sprd_plane_create_properties(plane, i);
+
+		plane->index = i;
+		if (i == 0)
+			primary = plane;
+	}
+
+	return primary;
+}

drivers/gpu/drm/sprd/sprd_dpu.c line 698

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+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_dbg(dpu->drm, "%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);

You don't seem to use that anywhere else? And that's a bit fragile, nothing really guarantees that it's the mode you're going to use, and even then it can be adjusted.

drivers/gpu/drm/sprd/sprd_dpu.c line 704

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+
+		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;

From an API PoV, you would want that to be in atomic_check. However, I'm not even sure what it's doing in the first place?

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+	}
+
+	return MODE_OK;
+}
+

drivers/gpu/drm/sprd/sprd_dpu.c line 719

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+				   struct drm_atomic_state *state)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+	sprd_dpu_init(dpu);
+
+	sprd_dpi_init(dpu);
+
+	enable_irq(dpu->ctx.irq);

Shouldn't this be in enable_vblank? And I would assume that you would have the interrupts enabled all the time, but disabled in your device?

Quoted 89 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +89 -0
+	drm_crtc_vblank_on(&dpu->base);
+}
+
+static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
+				    struct drm_atomic_state *state)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+	struct drm_device *drm = dpu->base.dev;
+
+	drm_crtc_vblank_off(&dpu->base);
+
+	disable_irq(dpu->ctx.irq);
+
+	sprd_dpu_fini(dpu);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	drm_dbg(crtc->dev, "%s()\n", __func__);
+
+	return 0;
+}
+
+static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+	struct drm_device *drm = dpu->base.dev;
+
+	sprd_dpu_flip(dpu);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+	dpu_reg_set(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+
+	return 0;
+}
+
+static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+	dpu_reg_clr(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
+	.mode_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 struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+			 struct drm_plane *primary)
+{
+	struct device_node *port;
+	struct sprd_dpu *dpu;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 819

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+	/*
+	 * 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_err(drm, "find 'ports' phandle of %s failed\n",
+			  drm->dev->of_node->full_name);
+		return ERR_PTR(-EINVAL);
+	}
+	of_node_put(port);

The YAML binding should already make sure that your binding is sane, and if you still get a DT that doesn't follow it, you have a whole lot of other issues than whether ports is there :)

drivers/gpu/drm/sprd/sprd_dpu.c line 829

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +9 -0
+	dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+					primary, NULL,
+					&sprd_crtc_funcs, NULL);
+	if (IS_ERR(dpu)) {
+		drm_err(drm, "failed to init crtc.\n");
+		return dpu;
+	}
+
+	dpu->base.port = port;

But you're still referencing it here, while you called of_node_put on it already? You should only call it once you're done with it.

I'm not really sure why you would need drm_of_find_possible_crtcs to work then if you don't follow the OF-Graph bindings.

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#13 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Hi Maxime,

Quoted 19 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:10写道:

Hi,

Quoted 1 line · Kevin Tang

Adds DPU(Display Processor Unit) support for the Unisoc's display

subsystem.

Quoted 1 line

It's support multi planes, scaler, rotation, PQ(Picture Quality) and

more.

Quoted 12 lines
Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dpu structure init.

v3:
- Remove dpu_layer stuff layer and commit layers by aotmic_update

Quoted 7 lines
Quoted 4 lines

v4:
- Use drmm_helpers to allocate crtc and planes.
- Move rotation enum definitions to crtc layer reg bitfields.
- Move allocate crtc and planes to bind funtion.

There's a number of checkpatch warnings, make sure to fix them

Thks, it's will be fixed on patch v5.

Quoted 210 lines

Review

Quoted 25 lines
---
 drivers/gpu/drm/sprd/Kconfig    |   1 +
 drivers/gpu/drm/sprd/Makefile   |   4 +-
 drivers/gpu/drm/sprd/sprd_dpu.c | 964 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h | 109 ++++
 drivers/gpu/drm/sprd/sprd_drm.c |   1 +
 drivers/gpu/drm/sprd/sprd_drm.h |   2 +
 6 files changed, 1079 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
drivers/gpu/drm/sprd/Kconfig +1 -0
index 6e80cc9f3..9b4ef9aea 100644
@@ -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
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/Makefile

Review

Quoted 12 lines

index 86d95d93a..6c25bfa99 100644

drivers/gpu/drm/sprd/Makefile +2 -2
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-subdir-ccflags-y += -I$(srctree)/$(src)
+obj-y := sprd_drm.o \
+     sprd_dpu.o
 
-obj-y := sprd_drm.o
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 148 lines

new file mode 100644
index 000000000..75b7e40d9

drivers/gpu/drm/sprd/sprd_dpu.c new file +143 -0
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+
+/* Global control registers */
+#define REG_DPU_CTRL 0x04
+#define REG_DPU_CFG0 0x08
+#define REG_PANEL_SIZE       0x20
+#define REG_BLEND_SIZE       0x24
+#define REG_BG_COLOR 0x2C
+
+/* Layer0 control registers */
+#define REG_LAY_BASE_ADDR0   0x30
+#define REG_LAY_BASE_ADDR1   0x34
+#define REG_LAY_BASE_ADDR2   0x38
+#define REG_LAY_CTRL         0x40
+#define REG_LAY_SIZE         0x44
+#define REG_LAY_PITCH                0x48
+#define REG_LAY_POS          0x4C
+#define REG_LAY_ALPHA                0x50
+#define REG_LAY_CROP_START   0x5C
+
+/* Interrupt control registers */
+#define REG_DPU_INT_EN               0x1E0
+#define REG_DPU_INT_CLR              0x1E4
+#define REG_DPU_INT_STS              0x1E8
+
+/* DPI control registers */
+#define REG_DPI_CTRL         0x1F0
+#define REG_DPI_H_TIMING     0x1F4
+#define REG_DPI_V_TIMING     0x1F8
+
+/* MMU control registers */
+#define REG_MMU_EN                   0x800
+#define REG_MMU_VPN_RANGE            0x80C
+#define REG_MMU_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)
+
+/* Layer control bits */
+#define BIT_DPU_LAY_EN                               BIT(0)
+#define BIT_DPU_LAY_LAYER_ALPHA                      (0x01 << 2)
+#define BIT_DPU_LAY_COMBO_ALPHA                      (0x02 << 2)
+#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE             (0x00 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE             (0x01 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE             (0x02 << 4)
+#define BIT_DPU_LAY_FORMAT_ARGB8888                  (0x03 << 4)
+#define BIT_DPU_LAY_FORMAT_RGB565                    (0x04 << 4)
+#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3             (0x00 << 8)
+#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0             (0x01 << 8)
+#define BIT_DPU_LAY_NO_SWITCH                        (0x00 << 10)
+#define BIT_DPU_LAY_RB_OR_UV_SWITCH          (0x01 << 10)
+#define BIT_DPU_LAY_MODE_BLEND_NORMAL                (0x00 << 16)
+#define BIT_DPU_LAY_MODE_BLEND_PREMULT               (0x01 << 16)
+#define BIT_DPU_LAY_ROTATION_0               (0x00 << 20)
+#define BIT_DPU_LAY_ROTATION_90              (0x01 << 20)
+#define BIT_DPU_LAY_ROTATION_180     (0x02 << 20)
+#define BIT_DPU_LAY_ROTATION_270     (0x03 << 20)
+#define BIT_DPU_LAY_ROTATION_0_M     (0x04 << 20)
+#define BIT_DPU_LAY_ROTATION_90_M    (0x05 << 20)
+#define BIT_DPU_LAY_ROTATION_180_M   (0x06 << 20)
+#define BIT_DPU_LAY_ROTATION_270_M   (0x07 << 20)
+
+/* Interrupt control & status bits */
+#define BIT_DPU_INT_DONE             BIT(0)
+#define BIT_DPU_INT_TE                       BIT(1)
+#define BIT_DPU_INT_ERR                      BIT(2)
+#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
+#define BIT_DPU_INT_VSYNC            BIT(5)
+#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,
+};
+
+struct sprd_plane {
+     struct drm_plane base;
+     u32 index;
+};
+
+static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
+{
+     return container_of(plane, struct sprd_plane, base);
+}
+
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{

drivers/gpu/drm/sprd/sprd_dpu.c line 153

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     struct dpu_context *ctx = &dpu->ctx;
+     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_err(dpu->drm, "--- iommu interrupt err: 0x%04x ---\n",

val);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+             if (val & BIT_DPU_INT_MMU_INV_RD)
+                     drm_err(dpu->drm, "iommu invalid read error, addr:

0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_INV_WR)
+                     drm_err(dpu->drm, "iommu invalid write error,

addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_WR));
+             if (val & BIT_DPU_INT_MMU_VAOR_RD)
+                     drm_err(dpu->drm, "iommu va out of range read

error, addr: 0x%08x\n",

Quoted 8 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_VAOR_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_VAOR_WR)
+                     drm_err(dpu->drm, "iommu va out of range write

error, addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c line 166

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                             readl(ctx->base + REG_MMU_VAOR_ADDR_WR));

Is that the IOMMU page fault interrupt? I would expect it to be in the iommu driver.

Our iommu driver is indeed an separate driver, and also in upstreaming, but iommu fault interrupts reporting by display controller, not iommu
itself,
if use iommu_set_fault_handler() to hook in our reporting function, there must be cross-module access to h/w registers.

Quoted 367 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 168

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+             for (i = 0; i < 8; i++) {

+ reg_val = layer_reg_rd(ctx, REG_LAY_CTRL, i);

drivers/gpu/drm/sprd/sprd_dpu.c line 169

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+                     if (reg_val & 0x1)
+                             drm_info(dpu->drm, "layer%d: 0x%08x 0x%08x

0x%08x ctrl: 0x%08x\n", i,

drivers/gpu/drm/sprd/sprd_dpu.c line 170

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx,

REG_LAY_BASE_ADDR0, i),

drivers/gpu/drm/sprd/sprd_dpu.c line 171

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx,

REG_LAY_BASE_ADDR1, i),

drivers/gpu/drm/sprd/sprd_dpu.c line 172

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx,

REG_LAY_BASE_ADDR2, i),

drivers/gpu/drm/sprd/sprd_dpu.c line 173

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx, REG_LAY_CTRL,

i));

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+             }
+     }
+
+     return val;
+}

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+static int dpu_wait_stop_done(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     int rc;
+
+     if (ctx->stopped)
+             return 0;
+
+     rc = wait_event_interruptible_timeout(ctx->wait_queue,

ctx->evt_stop,

Quoted 11 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +11 -0
+                                            msecs_to_jiffies(500));
+     ctx->evt_stop = false;
+
+     ctx->stopped = true;
+
+     if (!rc) {
+             drm_err(dpu->drm, "dpu wait for stop done time out!\n");
+             return -ETIMEDOUT;
+     }
+
+     return 0;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+}
+
+static int dpu_wait_update_done(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     int rc;
+
+     ctx->evt_update = false;
+
+     rc = wait_event_interruptible_timeout(ctx->wait_queue,

ctx->evt_update,

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+                                            msecs_to_jiffies(500));
+
+     if (!rc) {
+             drm_err(dpu->drm, "dpu wait for reg update done time

out!\n");

Quoted 149 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +149 -0
+             return -ETIMEDOUT;
+     }
+
+     return 0;
+}
+
+static u32 drm_format_to_dpu(struct drm_framebuffer *fb)
+{
+     u32 format = 0;
+
+     switch (fb->format->format) {
+     case DRM_FORMAT_BGRA8888:
+             /* BGRA8888 -> ARGB8888 */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_RGBX8888:
+     case DRM_FORMAT_RGBA8888:
+             /* RGBA8888 -> ABGR8888 */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             /* fallthrough */
+     case DRM_FORMAT_ABGR8888:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_ARGB8888:
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_XBGR8888:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_XRGB8888:
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_BGR565:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_RGB565:
+             format |= BIT_DPU_LAY_FORMAT_RGB565;
+             break;
+     case DRM_FORMAT_NV12:
+             /* 2-Lane: Yuv420 */
+             format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_NV21:
+             /* 2-Lane: Yuv420 */
+             format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     case DRM_FORMAT_NV16:
+             /* 2-Lane: Yuv422 */
+             format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     case DRM_FORMAT_NV61:
+             /* 2-Lane: Yuv422 */
+             format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_YUV420:
+             format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_YVU420:
+             format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     default:
+             break;
+     }
+
+     return format;
+}
+
+static u32 drm_rotation_to_dpu(struct drm_plane_state *state)
+{
+     u32 rotation = 0;
+
+     switch (state->rotation) {
+     default:
+     case DRM_MODE_ROTATE_0:
+             rotation = BIT_DPU_LAY_ROTATION_0;
+             break;
+     case DRM_MODE_ROTATE_90:
+             rotation = BIT_DPU_LAY_ROTATION_90;
+             break;
+     case DRM_MODE_ROTATE_180:
+             rotation = BIT_DPU_LAY_ROTATION_180;
+             break;
+     case DRM_MODE_ROTATE_270:
+             rotation = BIT_DPU_LAY_ROTATION_270;
+             break;
+     case DRM_MODE_REFLECT_Y:
+             rotation = BIT_DPU_LAY_ROTATION_180_M;
+             break;
+     case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
+             rotation = BIT_DPU_LAY_ROTATION_90_M;
+             break;
+     case DRM_MODE_REFLECT_X:
+             rotation = BIT_DPU_LAY_ROTATION_0_M;
+             break;
+     case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
+             rotation = BIT_DPU_LAY_ROTATION_270_M;
+             break;
+     }
+
+     return rotation;
+}
+
+static u32 drm_blend_to_dpu(struct drm_plane_state *state)
+{
+     u32 blend = 0;
+
+     switch (state->pixel_blend_mode) {
+     case DRM_MODE_BLEND_COVERAGE:
+             /* alpha mode select - combo alpha */
+             blend |= BIT_DPU_LAY_COMBO_ALPHA;
+             /* Normal mode */
+             blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
+             break;
+     case DRM_MODE_BLEND_PREMULTI:
+             /* alpha mode select - combo alpha */
+             blend |= BIT_DPU_LAY_COMBO_ALPHA;
+             /* Pre-mult mode */
+             blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
+             break;
+     case DRM_MODE_BLEND_PIXEL_NONE:
+     default:

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+             /* don't do blending, maybe RGBX */
+             /* alpha mode select - layer alpha */
+             blend |= BIT_DPU_LAY_LAYER_ALPHA;
+             break;
+     }
+
+     return blend;
+}
+
+static void sprd_dpu_layer(struct sprd_dpu *dpu, struct sprd_plane

*plane,

Quoted 15 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +15 -0
+                     struct drm_plane_state *state)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     struct drm_gem_cma_object *cma_obj;
+     const struct drm_format_info *info;
+     struct drm_framebuffer *fb = state->fb;
+     u32 addr, size, offset, pitch, blend, format, rotation;
+     u32 src_x = state->src_x >> 16;
+     u32 src_y = state->src_y >> 16;
+     u32 src_w = state->src_w >> 16;
+     u32 src_h = state->src_h >> 16;
+     u32 dst_x = state->crtc_x;
+     u32 dst_y = state->crtc_y;
+     u32 alpha = state->alpha;
+     int i;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+     offset = (dst_x & 0xffff) | (dst_y << 16);
+     size = (src_w & 0xffff) | (src_h << 16);
+
+     for (i = 0; i < fb->format->num_planes; i++) {
+             cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+             addr = cma_obj->paddr + fb->offsets[i];
+
+             if (i == 0)
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, addr,

plane->index);

drivers/gpu/drm/sprd/sprd_dpu.c line 399

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+             else if (i == 1)
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, addr,

plane->index);

drivers/gpu/drm/sprd/sprd_dpu.c line 401

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+             else
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, addr,

plane->index);

Quoted 97 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +97 -0
+     }
+
+     info = drm_format_info(fb->format->format);
+     if (fb->format->num_planes == 3) {
+             /* UV pitch is 1/2 of Y pitch */
+             pitch = (fb->pitches[0] / info->cpp[0]) |
+                             (fb->pitches[0] / info->cpp[0] << 15);
+     } else {
+             pitch = fb->pitches[0] / info->cpp[0];
+     }
+
+     layer_reg_wr(ctx, REG_LAY_POS, offset, plane->index);
+     layer_reg_wr(ctx, REG_LAY_SIZE, size, plane->index);
+     layer_reg_wr(ctx, REG_LAY_CROP_START,
+                     src_y << 16 | src_x, plane->index);
+     layer_reg_wr(ctx, REG_LAY_ALPHA, alpha, plane->index);
+     layer_reg_wr(ctx, REG_LAY_PITCH, pitch, plane->index);
+
+     format = drm_format_to_dpu(fb);
+     blend = drm_blend_to_dpu(state);
+     rotation = drm_rotation_to_dpu(state);
+
+     layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN |
+                             format |
+                             blend |
+                             rotation,
+                             plane->index);
+}
+
+static void sprd_dpu_flip(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 reg_val;
+
+     /*
+      * Make sure the dpu is in stop status. DPU has no shadow
+      * registers in EDPI mode. So the config registers can only be
+      * updated in the rising edge of DPU_RUN bit.
+      */
+     if (ctx->if_type == SPRD_DPU_IF_EDPI)
+             dpu_wait_stop_done(dpu);
+
+     /* update trigger and wait */
+     if (ctx->if_type == SPRD_DPU_IF_DPI) {
+             if (!ctx->stopped) {
+                     dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
+                     dpu_wait_update_done(dpu);
+             }
+
+             dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR);
+     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+             dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+             ctx->stopped = false;
+     }
+
+     /*
+      * If the following interrupt was disabled in isr,
+      * re-enable it.
+      */
+     reg_val = 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);
+}
+
+static void sprd_dpu_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 size;
+
+     writel(0x00, ctx->base + REG_BG_COLOR);
+
+     size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
+     writel(size, ctx->base + REG_PANEL_SIZE);
+     writel(size, ctx->base + REG_BLEND_SIZE);
+
+     writel(0x00, ctx->base + REG_MMU_EN);
+     writel(0x00, ctx->base + REG_MMU_PPN1);
+     writel(0xffff, ctx->base + REG_MMU_RANGE1);
+     writel(0x00, ctx->base + REG_MMU_PPN2);
+     writel(0xffff, ctx->base + REG_MMU_RANGE2);
+     writel(0x1ffff, ctx->base + REG_MMU_VPN_RANGE);
+}
+
+static void sprd_dpu_fini(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     writel(0x00, ctx->base + REG_DPU_INT_EN);
+     writel(0xff, ctx->base + REG_DPU_INT_CLR);
+}
+
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     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);

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+             /* set dpi timing */
+             reg_val = ctx->vm.hsync_len << 0 |
+                       ctx->vm.hback_porch << 8 |
+                       ctx->vm.hfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_H_TIMING);
+
+             reg_val = ctx->vm.vsync_len << 0 |
+                       ctx->vm.vback_porch << 8 |
+                       ctx->vm.vfront_porch << 20;
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 525

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+             writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+                     drm_warn(dpu->drm, "Warning: (vsync + vbp) < 32, "
+                             "underflow risk!\n");

I don't think a warning is appropriate here. Maybe we should just outright reject any mode that uses it?

This issue has been fixed on the new soc, maybe I should remove it.

Quoted 181 lines
Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+             /* 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 */

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+             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);

Quoted 50 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +50 -0
+             /* 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 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(int_mask, ctx->base + REG_DPU_INT_EN);
+}
+
+void sprd_dpu_run(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+     ctx->stopped = false;
+}
+
+void sprd_dpu_stop(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     if (ctx->if_type == SPRD_DPU_IF_DPI)
+             dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP);
+
+     dpu_wait_stop_done(dpu);
+}
+
+static int sprd_plane_atomic_check(struct drm_plane *plane,
+                               struct drm_plane_state *state)
+{
+     struct drm_framebuffer *fb = state->fb;
+     struct drm_crtc_state *crtc_state;
+     u32 fmt;
+
+     if (!fb || !state->crtc)
+             return 0;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 605

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     fmt = drm_format_to_dpu(fb);
+     if (!fmt)
+             return -EINVAL;
+
+     crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+     if (IS_ERR(crtc_state))
+             return PTR_ERR(crtc_state);
+
+     return drm_atomic_helper_check_plane_state(state, crtc_state,
+

DRM_PLANE_HELPER_NO_SCALING,

drivers/gpu/drm/sprd/sprd_dpu.c line 599

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+

DRM_PLANE_HELPER_NO_SCALING,

Quoted 14 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +14 -0
+                                               true, true);
+}
+
+static void sprd_plane_atomic_update(struct drm_plane *drm_plane,
+                                 struct drm_plane_state *old_state)
+{
+     struct drm_plane_state *state = drm_plane->state;
+     struct sprd_plane *plane = to_sprd_plane(drm_plane);
+     struct sprd_dpu *dpu = to_sprd_crtc(state->crtc);
+
+     /* start configure dpu layers */
+     sprd_dpu_layer(dpu, plane, state);
+}
+

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+static void sprd_plane_atomic_disable(struct drm_plane *drm_plane,
+                                  struct drm_plane_state *old_state)
+{
+     struct sprd_plane *plane = to_sprd_plane(drm_plane);
+     struct sprd_dpu *dpu = to_sprd_crtc(old_state->crtc);
+
+     layer_reg_wr(&dpu->ctx, REG_LAY_CTRL, 0x00, plane->index);
+}
+
+static void sprd_plane_create_properties(struct sprd_plane *plane, int

index)

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+{
+     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                                    BIT(DRM_MODE_BLEND_PREMULTI) |
+                                    BIT(DRM_MODE_BLEND_COVERAGE);
+
+     /* create rotation property */

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     drm_plane_create_rotation_property(&plane->base,
+                                        DRM_MODE_ROTATE_0,
+                                        DRM_MODE_ROTATE_MASK |
+                                        DRM_MODE_REFLECT_MASK);
+
+     /* create alpha property */
+     drm_plane_create_alpha_property(&plane->base);
+
+     /* create blend mode property */
+     drm_plane_create_blend_mode_property(&plane->base,

supported_modes);

Quoted 17 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +17 -0
+
+     /* create zpos property */
+     drm_plane_create_zpos_immutable_property(&plane->base, index);
+}
+
+static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
+     .atomic_check = sprd_plane_atomic_check,
+     .atomic_update = sprd_plane_atomic_update,
+     .atomic_disable = sprd_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs sprd_plane_funcs = {
+     .update_plane = drm_atomic_helper_update_plane,
+     .disable_plane  = drm_atomic_helper_disable_plane,
+     .destroy = drm_plane_cleanup,
+     .reset = drm_atomic_helper_plane_reset,
+     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct sprd_plane *sprd_plane_init(struct drm_device *drm)
+{
+     struct sprd_plane *plane, *primary;
+     int i;
+
+     for (i = 0; i < 6; i++) {
+             plane = drmm_universal_plane_alloc(drm, struct sprd_plane,

base,

drivers/gpu/drm/sprd/sprd_dpu.c line 668

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+                                            1, &sprd_plane_funcs,
+                                            primary_fmts,

ARRAY_SIZE(primary_fmts),

drivers/gpu/drm/sprd/sprd_dpu.c line 669

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                            NULL,

DRM_PLANE_TYPE_PRIMARY, NULL);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+             if (IS_ERR(plane)) {
+                     drm_err(drm, "fail to init primary plane\n");
+                     return plane;
+             }
+
+             drm_plane_helper_add(&plane->base,

&sprd_plane_helper_funcs);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 13 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +13 -0
+
+             sprd_plane_create_properties(plane, i);
+
+             plane->index = i;
+             if (i == 0)
+                     primary = plane;
+     }
+
+     return primary;
+}
+
+static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
+                                     const struct drm_display_mode

*mode)

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     drm_dbg(dpu->drm, "%s() mode: "DRM_MODE_FMT"\n", __func__,

DRM_MODE_ARG(mode));

Quoted 8 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 698

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);

You don't seem to use that anywhere else? And that's a bit fragile, nothing really guarantees that it's the mode you're going to use, and even then it can be adjusted.

drm_mode convert to video_mode is been use in "sprd_dpu_init" and
"sprd_dpi_init "
Preferred mode should be fixed mode, we generally don’t adjust it.

Quoted 11 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 704

Quoted 7 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+
+             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;

From an API PoV, you would want that to be in atomic_check. However, I'm not even sure what it's doing in the first place?

dpi interface mode: DPI(dsi video mode panel) and EDPI(dsi cmd mode panel) dpi interface mode has been used on crtc atomic_enable foo, so we need check dpi interface
mode before atomic_enable.

Must be put it in atomic_check? Here is the dpi interface mode selection, maybe here is better?

Quoted 11 lines
Quoted 11 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     }
+
+     return MODE_OK;
+}
+
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+                                struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 719

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+     sprd_dpu_init(dpu);
+
+     sprd_dpi_init(dpu);
+
+     enable_irq(dpu->ctx.irq);

Shouldn't this be in enable_vblank? And I would assume that you would have the interrupts enabled all the time, but disabled in your device?

It seems better to put in enable_vblank, i will try and test it... Thks

And I would assume that you would
have the interrupts enabled all the time, but disabled in your device? [kevin]I don’t quite understand this, can you help me explain it in detail?

Quoted 115 lines
Quoted 90 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +89 -0
+     drm_crtc_vblank_on(&dpu->base);
+}
+
+static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
+                                 struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+     struct drm_device *drm = dpu->base.dev;
+
+     drm_crtc_vblank_off(&dpu->base);
+
+     disable_irq(dpu->ctx.irq);
+
+     sprd_dpu_fini(dpu);
+
+     spin_lock_irq(&drm->event_lock);
+     if (crtc->state->event) {
+             drm_crtc_send_vblank_event(crtc, crtc->state->event);
+             crtc->state->event = NULL;
+     }
+     spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
+                              struct drm_atomic_state *state)
+{
+     drm_dbg(crtc->dev, "%s()\n", __func__);
+
+     return 0;
+}
+
+static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
+                               struct drm_atomic_state *state)
+
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+     struct drm_device *drm = dpu->base.dev;
+
+     sprd_dpu_flip(dpu);
+
+     spin_lock_irq(&drm->event_lock);
+     if (crtc->state->event) {
+             drm_crtc_send_vblank_event(crtc, crtc->state->event);
+             crtc->state->event = NULL;
+     }
+     spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     dpu_reg_set(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+
+     return 0;
+}
+
+static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     dpu_reg_clr(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
+     .mode_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 struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+                      struct drm_plane *primary)
+{
+     struct device_node *port;
+     struct sprd_dpu *dpu;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 819

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     /*
+      * 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_err(drm, "find 'ports' phandle of %s failed\n",
+                       drm->dev->of_node->full_name);
+             return ERR_PTR(-EINVAL);
+     }
+     of_node_put(port);

The YAML binding should already make sure that your binding is sane, and if you still get a DT that doesn't follow it, you have a whole lot of other issues than whether ports is there :)

drivers/gpu/drm/sprd/sprd_dpu.c line 829

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +9 -0
+     dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+                                     primary, NULL,
+                                     &sprd_crtc_funcs, NULL);
+     if (IS_ERR(dpu)) {
+             drm_err(drm, "failed to init crtc.\n");
+             return dpu;
+     }
+
+     dpu->base.port = port;
Quoted 5 lines

But you're still referencing it here, while you called of_node_put on it already? You should only call it once you're done with it.

I'm not really sure why you would need drm_of_find_possible_crtcs to work then if you don't follow the OF-Graph bindings.

My reference here is:
https://elixir.bootlin.com/linux/v4.14.228/source/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c#L593

It’s been a long time, I don’t remember the specific reason, i will recheck it.

Quoted 3 lines

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#14 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Hi,

Adding Jörg, Will and Robin,

Quoted 31 lines · Kevin Tang
Quoted 31 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 12 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +12 -0
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     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_err(dpu->drm, "--- iommu interrupt err: 0x%04x ---\n",

val);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+             if (val & BIT_DPU_INT_MMU_INV_RD)
+                     drm_err(dpu->drm, "iommu invalid read error, addr:

0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_INV_WR)
+                     drm_err(dpu->drm, "iommu invalid write error,

addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_WR));
+             if (val & BIT_DPU_INT_MMU_VAOR_RD)
+                     drm_err(dpu->drm, "iommu va out of range read

error, addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_VAOR_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_VAOR_WR)
+                     drm_err(dpu->drm, "iommu va out of range write

error, addr: 0x%08x\n",

Quoted 1 line

+ readl(ctx->base + REG_MMU_VAOR_ADDR_WR));

Quoted 8 lines
Quoted 2 lines

Is that the IOMMU page fault interrupt? I would expect it to be in the iommu driver.

Our iommu driver is indeed an separate driver, and also in upstreaming, but iommu fault interrupts reporting by display controller, not iommu
itself,
if use iommu_set_fault_handler() to hook in our reporting function, there must be cross-module access to h/w registers.

Can you explain a bit more the design of the hardware then? Each device connected to the IOMMU has a status register (and an interrupt) that reports when there's a fault?

I'd like to get an ack at least from the IOMMU maintainers and reviewers.

Quoted 33 lines
Quoted 24 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 13 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +13 -0
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->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);

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+             /* set dpi timing */
+             reg_val = ctx->vm.hsync_len << 0 |
+                       ctx->vm.hback_porch << 8 |
+                       ctx->vm.hfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_H_TIMING);
+
+             reg_val = ctx->vm.vsync_len << 0 |
+                       ctx->vm.vback_porch << 8 |
+                       ctx->vm.vfront_porch << 20;
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 525

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+             writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+                     drm_warn(dpu->drm, "Warning: (vsync + vbp) < 32, "
+                             "underflow risk!\n");

I don't think a warning is appropriate here. Maybe we should just outright reject any mode that uses it?

Quoted 1 line

This issue has been fixed on the new soc, maybe I should remove it.

If it still requires a workaround on older SoC, you can definitely add it but we should prevent any situation where the underflow might occur instead of reporting it once we're there.

Quoted 12 lines
Quoted 12 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
+                                     const struct drm_display_mode

*mode)

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     drm_dbg(dpu->drm, "%s() mode: "DRM_MODE_FMT"\n", __func__,

DRM_MODE_ARG(mode));

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
Quoted 7 lines
Quoted 4 lines

You don't seem to use that anywhere else? And that's a bit fragile, nothing really guarantees that it's the mode you're going to use, and even then it can be adjusted.

drm_mode convert to video_mode is been use in "sprd_dpu_init" and
"sprd_dpi_init "
Preferred mode should be fixed mode, we generally don’t adjust it.

That's not really the assumption DRM is built upon though. The userspace is even allowed to setup its own mode and try to configure it, and your driver should take that into account.

I'd just drop that mode_valid hook, and retrieve the videomode if you need it in atomic_enable or mode_set_no_fb

Quoted 11 lines
Quoted 11 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 704

Quoted 7 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+
+             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;

From an API PoV, you would want that to be in atomic_check. However, I'm not even sure what it's doing in the first place?

Quoted 7 lines

dpi interface mode: DPI(dsi video mode panel) and EDPI(dsi cmd mode panel) dpi interface mode has been used on crtc atomic_enable foo, so we need check dpi interface
mode before atomic_enable.

Must be put it in atomic_check? Here is the dpi interface mode selection, maybe here is better?

This doesn't have any relationship to the htotal and vtotal though? it's something that is carried over by the MIPI-DSI functions and struct mipi_dsi_device.

Quoted 20 lines
Quoted 11 lines
Quoted 11 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     }
+
+     return MODE_OK;
+}
+
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+                                struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 719

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+     sprd_dpu_init(dpu);
+
+     sprd_dpi_init(dpu);
+
+     enable_irq(dpu->ctx.irq);

Shouldn't this be in enable_vblank? And I would assume that you would have the interrupts enabled all the time, but disabled in your device?

Quoted 6 lines

It seems better to put in enable_vblank, i will try and test it... Thks

And I would assume that you would
have the interrupts enabled all the time, but disabled in your device? [kevin]I don’t quite understand this, can you help me explain it in detail?

You seem to have a register that enables and disables the interrupt in that device. The way we usually deal with them in this case is just to call request_irq in your bind/probe with the interrupts enabled at the controller level, and mask them when needed at the device level by clearing / setting that bit.

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

reviewer
#15 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Quoted 4 lines · Maxime Ripard

Hi,

Adding Jörg, Will and Robin,

You forgot to add them actually :)
I've added Robin and Joerg.

Quoted 42 lines · Kevin Tang
Quoted 31 lines
Quoted 31 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 12 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +12 -0
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     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_err(dpu->drm, "--- iommu interrupt err: 0x%04x ---\n",

val);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+             if (val & BIT_DPU_INT_MMU_INV_RD)
+                     drm_err(dpu->drm, "iommu invalid read error, addr:

0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_INV_WR)
+                     drm_err(dpu->drm, "iommu invalid write error,

addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_WR));
+             if (val & BIT_DPU_INT_MMU_VAOR_RD)
+                     drm_err(dpu->drm, "iommu va out of range read

error, addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_VAOR_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_VAOR_WR)
+                     drm_err(dpu->drm, "iommu va out of range write

error, addr: 0x%08x\n",

Quoted 1 line

+ readl(ctx->base + REG_MMU_VAOR_ADDR_WR));

Quoted 8 lines
Quoted 2 lines

Is that the IOMMU page fault interrupt? I would expect it to be in the iommu driver.

Our iommu driver is indeed an separate driver, and also in upstreaming, but iommu fault interrupts reporting by display controller, not iommu
itself,
if use iommu_set_fault_handler() to hook in our reporting function, there must be cross-module access to h/w registers.

Quoted 3 lines

Can you explain a bit more the design of the hardware then? Each device connected to the IOMMU has a status register (and an interrupt) that reports when there's a fault?

On Unisoc's platforms, one IOMMU serves one master device only, and interrupts are handled by master devices rather than IOMMUs, since the registers are in the physical address range of master devices.

Quoted 127 lines

I'd like to get an ack at least from the IOMMU maintainers and reviewers.

Quoted 33 lines
Quoted 24 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 13 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +13 -0
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->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);

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+             /* set dpi timing */
+             reg_val = ctx->vm.hsync_len << 0 |
+                       ctx->vm.hback_porch << 8 |
+                       ctx->vm.hfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_H_TIMING);
+
+             reg_val = ctx->vm.vsync_len << 0 |
+                       ctx->vm.vback_porch << 8 |
+                       ctx->vm.vfront_porch << 20;
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 525

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+             writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+                     drm_warn(dpu->drm, "Warning: (vsync + vbp) < 32, "
+                             "underflow risk!\n");

I don't think a warning is appropriate here. Maybe we should just outright reject any mode that uses it?

Quoted 1 line

This issue has been fixed on the new soc, maybe I should remove it.

If it still requires a workaround on older SoC, you can definitely add it but we should prevent any situation where the underflow might occur instead of reporting it once we're there.

Quoted 12 lines
Quoted 12 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
+                                     const struct drm_display_mode

*mode)

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     drm_dbg(dpu->drm, "%s() mode: "DRM_MODE_FMT"\n", __func__,

DRM_MODE_ARG(mode));

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
Quoted 7 lines
Quoted 4 lines

You don't seem to use that anywhere else? And that's a bit fragile, nothing really guarantees that it's the mode you're going to use, and even then it can be adjusted.

drm_mode convert to video_mode is been use in "sprd_dpu_init" and
"sprd_dpi_init "
Preferred mode should be fixed mode, we generally don’t adjust it.

That's not really the assumption DRM is built upon though. The userspace is even allowed to setup its own mode and try to configure it, and your driver should take that into account.

I'd just drop that mode_valid hook, and retrieve the videomode if you need it in atomic_enable or mode_set_no_fb

Quoted 11 lines
Quoted 11 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 704

Quoted 7 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+
+             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;

From an API PoV, you would want that to be in atomic_check. However, I'm not even sure what it's doing in the first place?

Quoted 7 lines

dpi interface mode: DPI(dsi video mode panel) and EDPI(dsi cmd mode panel) dpi interface mode has been used on crtc atomic_enable foo, so we need check dpi interface
mode before atomic_enable.

Must be put it in atomic_check? Here is the dpi interface mode selection, maybe here is better?

This doesn't have any relationship to the htotal and vtotal though? it's something that is carried over by the MIPI-DSI functions and struct mipi_dsi_device.

Quoted 20 lines
Quoted 11 lines
Quoted 11 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     }
+
+     return MODE_OK;
+}
+
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+                                struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 719

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+     sprd_dpu_init(dpu);
+
+     sprd_dpi_init(dpu);
+
+     enable_irq(dpu->ctx.irq);

Shouldn't this be in enable_vblank? And I would assume that you would have the interrupts enabled all the time, but disabled in your device?

Quoted 6 lines

It seems better to put in enable_vblank, i will try and test it... Thks

And I would assume that you would
have the interrupts enabled all the time, but disabled in your device? [kevin]I don’t quite understand this, can you help me explain it in detail?

You seem to have a register that enables and disables the interrupt in that device. The way we usually deal with them in this case is just to call request_irq in your bind/probe with the interrupts enabled at the controller level, and mask them when needed at the device level by clearing / setting that bit.

Maxime

author
#16 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

In reply to Maxime Ripard
Quoted 51 lines · Maxime Ripard <[email protected]> 于2021年4月7日周三 下午6:45写道:

Hi,

Adding Jörg, Will and Robin,

Quoted 4 lines · Kevin Tang
Quoted 4 lines
Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 mmu_mask = BIT_DPU_INT_MMU_VAOR_RD |
Quoted 8 lines
Quoted 8 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 8 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +8 -0
+                     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_err(dpu->drm, "--- iommu interrupt err: 0x%04x

---\n",

Quoted 4 lines
Quoted 4 lines

val);

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+             if (val & BIT_DPU_INT_MMU_INV_RD)
+                     drm_err(dpu->drm, "iommu invalid read error,

addr:

Quoted 2 lines
Quoted 2 lines

0x%08x\n",

Quoted 1 line

+ readl(ctx->base +

REG_MMU_INV_ADDR_RD));

Quoted 4 lines
Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+             if (val & BIT_DPU_INT_MMU_INV_WR)
+                     drm_err(dpu->drm, "iommu invalid write error,

addr: 0x%08x\n",

Quoted 1 line

+ readl(ctx->base +

REG_MMU_INV_ADDR_WR));

Quoted 4 lines
Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+             if (val & BIT_DPU_INT_MMU_VAOR_RD)
+                     drm_err(dpu->drm, "iommu va out of range read

error, addr: 0x%08x\n",

Quoted 1 line

+ readl(ctx->base +

REG_MMU_VAOR_ADDR_RD));

Quoted 4 lines
Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+             if (val & BIT_DPU_INT_MMU_VAOR_WR)
+                     drm_err(dpu->drm, "iommu va out of range write

error, addr: 0x%08x\n",

Quoted 1 line

+ readl(ctx->base +

REG_MMU_VAOR_ADDR_WR));

Quoted 9 lines
Quoted 3 lines

Is that the IOMMU page fault interrupt? I would expect it to be in the iommu driver.

Our iommu driver is indeed an separate driver, and also in upstreaming, but iommu fault interrupts reporting by display controller, not iommu
itself,
if use iommu_set_fault_handler() to hook in our reporting function, there must be cross-module access to h/w registers.

Quoted 7 lines

Can you explain a bit more the design of the hardware then? Each device connected to the IOMMU has a status register (and an interrupt) that reports when there's a fault?

I'd like to get an ack at least from the IOMMU maintainers and reviewers.

Chunyan has helped explain it.

Quoted 32 lines
Quoted 15 lines
Quoted 15 lines
Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     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);

Quoted 14 lines
Quoted 6 lines
Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+
+             /* set dpi timing */
+             reg_val = ctx->vm.hsync_len << 0 |
+                       ctx->vm.hback_porch << 8 |
+                       ctx->vm.hfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_H_TIMING);
Quoted 8 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 8 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +8 -0
+
+             reg_val = ctx->vm.vsync_len << 0 |
+                       ctx->vm.vback_porch << 8 |
+                       ctx->vm.vfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+                     drm_warn(dpu->drm, "Warning: (vsync + vbp) <

32, "

Quoted 2 lines
Quoted 2 lines
Quoted 1 line

+ "underflow risk!\n");

Quoted 9 lines
Quoted 4 lines
Quoted 3 lines

I don't think a warning is appropriate here. Maybe we should just outright reject any mode that uses it?

This issue has been fixed on the new soc, maybe I should remove it.

If it still requires a workaround on older SoC, you can definitely add it but we should prevent any situation where the underflow might occur instead of reporting it once we're there.

Thks, porch parameter is obtained from the panel driver, so I think it would be
better to check on the panel side. I will remove it here.

Quoted 22 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

+static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc

*crtc,

Quoted 11 lines
Quoted 11 lines
Quoted 1 line

+ const struct drm_display_mode

*mode)

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     drm_dbg(dpu->drm, "%s() mode: "DRM_MODE_FMT"\n", __func__,

DRM_MODE_ARG(mode));

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+     if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+             drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
Quoted 7 lines
Quoted 4 lines

You don't seem to use that anywhere else? And that's a bit fragile, nothing really guarantees that it's the mode you're going to use, and even then it can be adjusted.

drm_mode convert to video_mode is been use in "sprd_dpu_init" and
"sprd_dpi_init "
Preferred mode should be fixed mode, we generally don’t adjust it.

Quoted 7 lines

That's not really the assumption DRM is built upon though. The userspace is even allowed to setup its own mode and try to configure it, and your driver should take that into account.

I'd just drop that mode_valid hook, and retrieve the videomode if you need it in atomic_enable or mode_set_no_fb

Yeah, i need videomode info to setup display controller, so i will retrieve the videomode
in mode_set_no_fb.

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 704

Quoted 8 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+
+             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;

From an API PoV, you would want that to be in atomic_check. However,

I'm

Quoted 3 lines
Quoted 2 lines

not even sure what it's doing in the first place?

dpi interface mode: DPI(dsi video mode panel) and EDPI(dsi cmd mode

panel)

Quoted 4 lines

dpi interface mode has been used on crtc atomic_enable foo, so we need check dpi interface
mode before atomic_enable.

Quoted 7 lines
Quoted 2 lines

Must be put it in atomic_check? Here is the dpi interface mode selection, maybe here is better?

This doesn't have any relationship to the htotal and vtotal though? it's something that is carried over by the MIPI-DSI functions and struct mipi_dsi_device.

You mean DPI/EDPI should be check in the encoder driver?

Quoted 28 lines
Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     }
+
+     return MODE_OK;
+}
+
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+                                struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 719

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+     sprd_dpu_init(dpu);
+
+     sprd_dpi_init(dpu);
+
+     enable_irq(dpu->ctx.irq);

Shouldn't this be in enable_vblank? And I would assume that you would have the interrupts enabled all the time, but disabled in your device?

Quoted 6 lines

It seems better to put in enable_vblank, i will try and test it... Thks

And I would assume that you would
have the interrupts enabled all the time, but disabled in your device? [kevin]I don’t quite understand this, can you help me explain it in detail?

Quoted 6 lines

You seem to have a register that enables and disables the interrupt in that device. The way we usually deal with them in this case is just to call request_irq in your bind/probe with the interrupts enabled at the controller level, and mask them when needed at the device level by clearing / setting that bit.

Yeah, we have display controller interrupts setting and clear register.
But the interrupts all been enabled in bootloader(eg, lk or uboot), if the interrupt handler is active in the probe/bind phase by request_irq, but the whole display pipeline is not ready, there maybe have some problems.

So we want to manually control interrupt handler enable/disable, make sure all is ready,
then enable/disable interrupt handler.

Quoted 3 lines

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#17 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Hi,

Quoted 32 lines · Kevin Tang
Quoted 26 lines
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     }
+
+     return MODE_OK;
+}
+
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+                                struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 719

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+     sprd_dpu_init(dpu);
+
+     sprd_dpi_init(dpu);
+
+     enable_irq(dpu->ctx.irq);

Shouldn't this be in enable_vblank? And I would assume that you would have the interrupts enabled all the time, but disabled in your device?

Quoted 6 lines

It seems better to put in enable_vblank, i will try and test it... Thks

And I would assume that you would
have the interrupts enabled all the time, but disabled in your device? [kevin]I don’t quite understand this, can you help me explain it in detail?

Quoted 6 lines

You seem to have a register that enables and disables the interrupt in that device. The way we usually deal with them in this case is just to call request_irq in your bind/probe with the interrupts enabled at the controller level, and mask them when needed at the device level by clearing / setting that bit.

Quoted 4 lines

Yeah, we have display controller interrupts setting and clear register.
But the interrupts all been enabled in bootloader(eg, lk or uboot), if the interrupt handler is active in the probe/bind phase by request_irq, but the whole display pipeline is not ready, there maybe have some problems.

It's fairly common to clear / ack the interrupts from the device before calling request_irq precisely to avoid that issue.

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#18 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

In reply to Maxime Ripard
Quoted 917 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:10写道:

Hi,

Quoted 11 lines · Kevin Tang

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dpu structure init.

Quoted 7 lines

v3:
- Remove dpu_layer stuff layer and commit layers by aotmic_update

v4:
- Use drmm_helpers to allocate crtc and planes.
- Move rotation enum definitions to crtc layer reg bitfields.
- Move allocate crtc and planes to bind funtion.

There's a number of checkpatch warnings, make sure to fix them

Quoted 197 lines
---
 drivers/gpu/drm/sprd/Kconfig    |   1 +
 drivers/gpu/drm/sprd/Makefile   |   4 +-
 drivers/gpu/drm/sprd/sprd_dpu.c | 964 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dpu.h | 109 ++++
 drivers/gpu/drm/sprd/sprd_drm.c |   1 +
 drivers/gpu/drm/sprd/sprd_drm.h |   2 +
 6 files changed, 1079 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h
drivers/gpu/drm/sprd/Kconfig +1 -0
index 6e80cc9f3..9b4ef9aea 100644
@@ -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
drivers/gpu/drm/sprd/Makefile +2 -2
index 86d95d93a..6c25bfa99 100644
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-subdir-ccflags-y += -I$(srctree)/$(src)
+obj-y := sprd_drm.o \
+     sprd_dpu.o
 
-obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_dpu.c new file +156 -0
new file mode 100644
index 000000000..75b7e40d9
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+
+/* Global control registers */
+#define REG_DPU_CTRL 0x04
+#define REG_DPU_CFG0 0x08
+#define REG_PANEL_SIZE       0x20
+#define REG_BLEND_SIZE       0x24
+#define REG_BG_COLOR 0x2C
+
+/* Layer0 control registers */
+#define REG_LAY_BASE_ADDR0   0x30
+#define REG_LAY_BASE_ADDR1   0x34
+#define REG_LAY_BASE_ADDR2   0x38
+#define REG_LAY_CTRL         0x40
+#define REG_LAY_SIZE         0x44
+#define REG_LAY_PITCH                0x48
+#define REG_LAY_POS          0x4C
+#define REG_LAY_ALPHA                0x50
+#define REG_LAY_CROP_START   0x5C
+
+/* Interrupt control registers */
+#define REG_DPU_INT_EN               0x1E0
+#define REG_DPU_INT_CLR              0x1E4
+#define REG_DPU_INT_STS              0x1E8
+
+/* DPI control registers */
+#define REG_DPI_CTRL         0x1F0
+#define REG_DPI_H_TIMING     0x1F4
+#define REG_DPI_V_TIMING     0x1F8
+
+/* MMU control registers */
+#define REG_MMU_EN                   0x800
+#define REG_MMU_VPN_RANGE            0x80C
+#define REG_MMU_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)
+
+/* Layer control bits */
+#define BIT_DPU_LAY_EN                               BIT(0)
+#define BIT_DPU_LAY_LAYER_ALPHA                      (0x01 << 2)
+#define BIT_DPU_LAY_COMBO_ALPHA                      (0x02 << 2)
+#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE             (0x00 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE             (0x01 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE             (0x02 << 4)
+#define BIT_DPU_LAY_FORMAT_ARGB8888                  (0x03 << 4)
+#define BIT_DPU_LAY_FORMAT_RGB565                    (0x04 << 4)
+#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3             (0x00 << 8)
+#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0             (0x01 << 8)
+#define BIT_DPU_LAY_NO_SWITCH                        (0x00 << 10)
+#define BIT_DPU_LAY_RB_OR_UV_SWITCH          (0x01 << 10)
+#define BIT_DPU_LAY_MODE_BLEND_NORMAL                (0x00 << 16)
+#define BIT_DPU_LAY_MODE_BLEND_PREMULT               (0x01 << 16)
+#define BIT_DPU_LAY_ROTATION_0               (0x00 << 20)
+#define BIT_DPU_LAY_ROTATION_90              (0x01 << 20)
+#define BIT_DPU_LAY_ROTATION_180     (0x02 << 20)
+#define BIT_DPU_LAY_ROTATION_270     (0x03 << 20)
+#define BIT_DPU_LAY_ROTATION_0_M     (0x04 << 20)
+#define BIT_DPU_LAY_ROTATION_90_M    (0x05 << 20)
+#define BIT_DPU_LAY_ROTATION_180_M   (0x06 << 20)
+#define BIT_DPU_LAY_ROTATION_270_M   (0x07 << 20)
+
+/* Interrupt control & status bits */
+#define BIT_DPU_INT_DONE             BIT(0)
+#define BIT_DPU_INT_TE                       BIT(1)
+#define BIT_DPU_INT_ERR                      BIT(2)
+#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
+#define BIT_DPU_INT_VSYNC            BIT(5)
+#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,
+};
+
+struct sprd_plane {
+     struct drm_plane base;
+     u32 index;
+};
+
+static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
+{
+     return container_of(plane, struct sprd_plane, base);
+}
+
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     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_err(dpu->drm, "--- iommu interrupt err: 0x%04x ---\n", val);
+
+             if (val & BIT_DPU_INT_MMU_INV_RD)
+                     drm_err(dpu->drm, "iommu invalid read error, addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c line 166

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_INV_WR)
+                     drm_err(dpu->drm, "iommu invalid write error, addr: 0x%08x\n",
+                             readl(ctx->base + REG_MMU_INV_ADDR_WR));
+             if (val & BIT_DPU_INT_MMU_VAOR_RD)
+                     drm_err(dpu->drm, "iommu va out of range read error, addr: 0x%08x\n",
+                             readl(ctx->base + REG_MMU_VAOR_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_VAOR_WR)
+                     drm_err(dpu->drm, "iommu va out of range write error, addr: 0x%08x\n",
+                             readl(ctx->base + REG_MMU_VAOR_ADDR_WR));

Is that the IOMMU page fault interrupt? I would expect it to be in the iommu driver.

Quoted 348 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +348 -0
+             for (i = 0; i < 8; i++) {
+                     reg_val = layer_reg_rd(ctx, REG_LAY_CTRL, i);
+                     if (reg_val & 0x1)
+                             drm_info(dpu->drm, "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 int dpu_wait_stop_done(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     int rc;
+
+     if (ctx->stopped)
+             return 0;
+
+     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
+                                            msecs_to_jiffies(500));
+     ctx->evt_stop = false;
+
+     ctx->stopped = true;
+
+     if (!rc) {
+             drm_err(dpu->drm, "dpu wait for stop done time out!\n");
+             return -ETIMEDOUT;
+     }
+
+     return 0;
+}
+
+static int dpu_wait_update_done(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     int rc;
+
+     ctx->evt_update = false;
+
+     rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
+                                            msecs_to_jiffies(500));
+
+     if (!rc) {
+             drm_err(dpu->drm, "dpu wait for reg update done time out!\n");
+             return -ETIMEDOUT;
+     }
+
+     return 0;
+}
+
+static u32 drm_format_to_dpu(struct drm_framebuffer *fb)
+{
+     u32 format = 0;
+
+     switch (fb->format->format) {
+     case DRM_FORMAT_BGRA8888:
+             /* BGRA8888 -> ARGB8888 */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_RGBX8888:
+     case DRM_FORMAT_RGBA8888:
+             /* RGBA8888 -> ABGR8888 */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             /* fallthrough */
+     case DRM_FORMAT_ABGR8888:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_ARGB8888:
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_XBGR8888:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_XRGB8888:
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_BGR565:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_RGB565:
+             format |= BIT_DPU_LAY_FORMAT_RGB565;
+             break;
+     case DRM_FORMAT_NV12:
+             /* 2-Lane: Yuv420 */
+             format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_NV21:
+             /* 2-Lane: Yuv420 */
+             format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     case DRM_FORMAT_NV16:
+             /* 2-Lane: Yuv422 */
+             format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     case DRM_FORMAT_NV61:
+             /* 2-Lane: Yuv422 */
+             format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_YUV420:
+             format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_YVU420:
+             format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     default:
+             break;
+     }
+
+     return format;
+}
+
+static u32 drm_rotation_to_dpu(struct drm_plane_state *state)
+{
+     u32 rotation = 0;
+
+     switch (state->rotation) {
+     default:
+     case DRM_MODE_ROTATE_0:
+             rotation = BIT_DPU_LAY_ROTATION_0;
+             break;
+     case DRM_MODE_ROTATE_90:
+             rotation = BIT_DPU_LAY_ROTATION_90;
+             break;
+     case DRM_MODE_ROTATE_180:
+             rotation = BIT_DPU_LAY_ROTATION_180;
+             break;
+     case DRM_MODE_ROTATE_270:
+             rotation = BIT_DPU_LAY_ROTATION_270;
+             break;
+     case DRM_MODE_REFLECT_Y:
+             rotation = BIT_DPU_LAY_ROTATION_180_M;
+             break;
+     case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
+             rotation = BIT_DPU_LAY_ROTATION_90_M;
+             break;
+     case DRM_MODE_REFLECT_X:
+             rotation = BIT_DPU_LAY_ROTATION_0_M;
+             break;
+     case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
+             rotation = BIT_DPU_LAY_ROTATION_270_M;
+             break;
+     }
+
+     return rotation;
+}
+
+static u32 drm_blend_to_dpu(struct drm_plane_state *state)
+{
+     u32 blend = 0;
+
+     switch (state->pixel_blend_mode) {
+     case DRM_MODE_BLEND_COVERAGE:
+             /* alpha mode select - combo alpha */
+             blend |= BIT_DPU_LAY_COMBO_ALPHA;
+             /* Normal mode */
+             blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
+             break;
+     case DRM_MODE_BLEND_PREMULTI:
+             /* alpha mode select - combo alpha */
+             blend |= BIT_DPU_LAY_COMBO_ALPHA;
+             /* Pre-mult mode */
+             blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
+             break;
+     case DRM_MODE_BLEND_PIXEL_NONE:
+     default:
+             /* don't do blending, maybe RGBX */
+             /* alpha mode select - layer alpha */
+             blend |= BIT_DPU_LAY_LAYER_ALPHA;
+             break;
+     }
+
+     return blend;
+}
+
+static void sprd_dpu_layer(struct sprd_dpu *dpu, struct sprd_plane *plane,
+                     struct drm_plane_state *state)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     struct drm_gem_cma_object *cma_obj;
+     const struct drm_format_info *info;
+     struct drm_framebuffer *fb = state->fb;
+     u32 addr, size, offset, pitch, blend, format, rotation;
+     u32 src_x = state->src_x >> 16;
+     u32 src_y = state->src_y >> 16;
+     u32 src_w = state->src_w >> 16;
+     u32 src_h = state->src_h >> 16;
+     u32 dst_x = state->crtc_x;
+     u32 dst_y = state->crtc_y;
+     u32 alpha = state->alpha;
+     int i;
+
+     offset = (dst_x & 0xffff) | (dst_y << 16);
+     size = (src_w & 0xffff) | (src_h << 16);
+
+     for (i = 0; i < fb->format->num_planes; i++) {
+             cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+             addr = cma_obj->paddr + fb->offsets[i];
+
+             if (i == 0)
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, addr, plane->index);
+             else if (i == 1)
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, addr, plane->index);
+             else
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, addr, plane->index);
+     }
+
+     info = drm_format_info(fb->format->format);
+     if (fb->format->num_planes == 3) {
+             /* UV pitch is 1/2 of Y pitch */
+             pitch = (fb->pitches[0] / info->cpp[0]) |
+                             (fb->pitches[0] / info->cpp[0] << 15);
+     } else {
+             pitch = fb->pitches[0] / info->cpp[0];
+     }
+
+     layer_reg_wr(ctx, REG_LAY_POS, offset, plane->index);
+     layer_reg_wr(ctx, REG_LAY_SIZE, size, plane->index);
+     layer_reg_wr(ctx, REG_LAY_CROP_START,
+                     src_y << 16 | src_x, plane->index);
+     layer_reg_wr(ctx, REG_LAY_ALPHA, alpha, plane->index);
+     layer_reg_wr(ctx, REG_LAY_PITCH, pitch, plane->index);
+
+     format = drm_format_to_dpu(fb);
+     blend = drm_blend_to_dpu(state);
+     rotation = drm_rotation_to_dpu(state);
+
+     layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN |
+                             format |
+                             blend |
+                             rotation,
+                             plane->index);
+}
+
+static void sprd_dpu_flip(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 reg_val;
+
+     /*
+      * Make sure the dpu is in stop status. DPU has no shadow
+      * registers in EDPI mode. So the config registers can only be
+      * updated in the rising edge of DPU_RUN bit.
+      */
+     if (ctx->if_type == SPRD_DPU_IF_EDPI)
+             dpu_wait_stop_done(dpu);
+
+     /* update trigger and wait */
+     if (ctx->if_type == SPRD_DPU_IF_DPI) {
+             if (!ctx->stopped) {
+                     dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
+                     dpu_wait_update_done(dpu);
+             }
+
+             dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR);
+     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+             dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+             ctx->stopped = false;
+     }
+
+     /*
+      * If the following interrupt was disabled in isr,
+      * re-enable it.
+      */
+     reg_val = 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);
+}
+
+static void sprd_dpu_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 size;
+
+     writel(0x00, ctx->base + REG_BG_COLOR);
+
+     size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
+     writel(size, ctx->base + REG_PANEL_SIZE);
+     writel(size, ctx->base + REG_BLEND_SIZE);
+
+     writel(0x00, ctx->base + REG_MMU_EN);
+     writel(0x00, ctx->base + REG_MMU_PPN1);
+     writel(0xffff, ctx->base + REG_MMU_RANGE1);
+     writel(0x00, ctx->base + REG_MMU_PPN2);
+     writel(0xffff, ctx->base + REG_MMU_RANGE2);
+     writel(0x1ffff, ctx->base + REG_MMU_VPN_RANGE);
+}
+
+static void sprd_dpu_fini(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     writel(0x00, ctx->base + REG_DPU_INT_EN);
+     writel(0xff, ctx->base + REG_DPU_INT_CLR);
+}
+
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 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;

drivers/gpu/drm/sprd/sprd_dpu.c line 525

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+             writel(reg_val, ctx->base + REG_DPI_H_TIMING);
+
+             reg_val = ctx->vm.vsync_len << 0 |
+                       ctx->vm.vback_porch << 8 |
+                       ctx->vm.vfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+                     drm_warn(dpu->drm, "Warning: (vsync + vbp) < 32, "
+                             "underflow risk!\n");

I don't think a warning is appropriate here. Maybe we should just outright reject any mode that uses it?

Quoted 162 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +162 -0
+             /* 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 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(int_mask, ctx->base + REG_DPU_INT_EN);
+}
+
+void sprd_dpu_run(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+     ctx->stopped = false;
+}
+
+void sprd_dpu_stop(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     if (ctx->if_type == SPRD_DPU_IF_DPI)
+             dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP);
+
+     dpu_wait_stop_done(dpu);
+}
+
+static int sprd_plane_atomic_check(struct drm_plane *plane,
+                               struct drm_plane_state *state)
+{
+     struct drm_framebuffer *fb = state->fb;
+     struct drm_crtc_state *crtc_state;
+     u32 fmt;
+
+     if (!fb || !state->crtc)
+             return 0;
+
+     fmt = drm_format_to_dpu(fb);
+     if (!fmt)
+             return -EINVAL;
+
+     crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+     if (IS_ERR(crtc_state))
+             return PTR_ERR(crtc_state);
+
+     return drm_atomic_helper_check_plane_state(state, crtc_state,
+                                               DRM_PLANE_HELPER_NO_SCALING,
+                                               DRM_PLANE_HELPER_NO_SCALING,
+                                               true, true);
+}
+
+static void sprd_plane_atomic_update(struct drm_plane *drm_plane,
+                                 struct drm_plane_state *old_state)
+{
+     struct drm_plane_state *state = drm_plane->state;
+     struct sprd_plane *plane = to_sprd_plane(drm_plane);
+     struct sprd_dpu *dpu = to_sprd_crtc(state->crtc);
+
+     /* start configure dpu layers */
+     sprd_dpu_layer(dpu, plane, state);
+}
+
+static void sprd_plane_atomic_disable(struct drm_plane *drm_plane,
+                                  struct drm_plane_state *old_state)
+{
+     struct sprd_plane *plane = to_sprd_plane(drm_plane);
+     struct sprd_dpu *dpu = to_sprd_crtc(old_state->crtc);
+
+     layer_reg_wr(&dpu->ctx, REG_LAY_CTRL, 0x00, plane->index);
+}
+
+static void sprd_plane_create_properties(struct sprd_plane *plane, int index)
+{
+     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                                    BIT(DRM_MODE_BLEND_PREMULTI) |
+                                    BIT(DRM_MODE_BLEND_COVERAGE);
+
+     /* create rotation property */
+     drm_plane_create_rotation_property(&plane->base,
+                                        DRM_MODE_ROTATE_0,
+                                        DRM_MODE_ROTATE_MASK |
+                                        DRM_MODE_REFLECT_MASK);
+
+     /* create alpha property */
+     drm_plane_create_alpha_property(&plane->base);
+
+     /* create blend mode property */
+     drm_plane_create_blend_mode_property(&plane->base, supported_modes);
+
+     /* create zpos property */
+     drm_plane_create_zpos_immutable_property(&plane->base, index);
+}
+
+static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
+     .atomic_check = sprd_plane_atomic_check,
+     .atomic_update = sprd_plane_atomic_update,
+     .atomic_disable = sprd_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs sprd_plane_funcs = {
+     .update_plane = drm_atomic_helper_update_plane,
+     .disable_plane  = drm_atomic_helper_disable_plane,
+     .destroy = drm_plane_cleanup,
+     .reset = drm_atomic_helper_plane_reset,
+     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct sprd_plane *sprd_plane_init(struct drm_device *drm)
+{
+     struct sprd_plane *plane, *primary;
+     int i;
+
+     for (i = 0; i < 6; i++) {
+             plane = drmm_universal_plane_alloc(drm, struct sprd_plane, base,
+                                            1, &sprd_plane_funcs,
+                                            primary_fmts, ARRAY_SIZE(primary_fmts),
+                                            NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
+             if (IS_ERR(plane)) {
+                     drm_err(drm, "fail to init primary plane\n");
+                     return plane;
+             }
+
+             drm_plane_helper_add(&plane->base, &sprd_plane_helper_funcs);
+
+             sprd_plane_create_properties(plane, i);
+
+             plane->index = i;
+             if (i == 0)
+                     primary = plane;
+     }
+
+     return primary;
+}

drivers/gpu/drm/sprd/sprd_dpu.c line 698

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+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_dbg(dpu->drm, "%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);

You don't seem to use that anywhere else? And that's a bit fragile, nothing really guarantees that it's the mode you're going to use, and even then it can be adjusted.

drivers/gpu/drm/sprd/sprd_dpu.c line 704

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+
+             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;

From an API PoV, you would want that to be in atomic_check. However, I'm not even sure what it's doing in the first place?

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+     }
+
+     return MODE_OK;
+}
+

drivers/gpu/drm/sprd/sprd_dpu.c line 719

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
+                                struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     sprd_dpu_init(dpu);
+
+     sprd_dpi_init(dpu);
+
+     enable_irq(dpu->ctx.irq);

Shouldn't this be in enable_vblank? And I would assume that you would have the interrupts enabled all the time, but disabled in your device?

Quoted 89 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +89 -0
+     drm_crtc_vblank_on(&dpu->base);
+}
+
+static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
+                                 struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+     struct drm_device *drm = dpu->base.dev;
+
+     drm_crtc_vblank_off(&dpu->base);
+
+     disable_irq(dpu->ctx.irq);
+
+     sprd_dpu_fini(dpu);
+
+     spin_lock_irq(&drm->event_lock);
+     if (crtc->state->event) {
+             drm_crtc_send_vblank_event(crtc, crtc->state->event);
+             crtc->state->event = NULL;
+     }
+     spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
+                              struct drm_atomic_state *state)
+{
+     drm_dbg(crtc->dev, "%s()\n", __func__);
+
+     return 0;
+}
+
+static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
+                               struct drm_atomic_state *state)
+
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+     struct drm_device *drm = dpu->base.dev;
+
+     sprd_dpu_flip(dpu);
+
+     spin_lock_irq(&drm->event_lock);
+     if (crtc->state->event) {
+             drm_crtc_send_vblank_event(crtc, crtc->state->event);
+             crtc->state->event = NULL;
+     }
+     spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     dpu_reg_set(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+
+     return 0;
+}
+
+static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     dpu_reg_clr(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
+     .mode_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 struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+                      struct drm_plane *primary)
+{
+     struct device_node *port;
+     struct sprd_dpu *dpu;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 819

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     /*
+      * 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_err(drm, "find 'ports' phandle of %s failed\n",
+                       drm->dev->of_node->full_name);
+             return ERR_PTR(-EINVAL);
+     }
+     of_node_put(port);

The YAML binding should already make sure that your binding is sane, and if you still get a DT that doesn't follow it, you have a whole lot of other issues than whether ports is there :)

drivers/gpu/drm/sprd/sprd_dpu.c line 829

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +9 -0
+     dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+                                     primary, NULL,
+                                     &sprd_crtc_funcs, NULL);
+     if (IS_ERR(dpu)) {
+             drm_err(drm, "failed to init crtc.\n");
+             return dpu;
+     }
+
+     dpu->base.port = port;
Quoted 2 lines

But you're still referencing it here, while you called of_node_put on it already? You should only call it once you're done with it.

of_node_put should be called after done with it, this maybe indeed be a bug.
i will fix it.

Quoted 4 lines

I'm not really sure why you would need drm_of_find_possible_crtcs to work then if you don't follow the OF-Graph bindings.

it scan all endports of encoder, if a matching crtc is found by OF-Graph bindings
and then genarate the crtc mask, here is description:
41 /**

42   * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
43   * @dev: DRM device
44   * @port: encoder port to scan for endpoints
45   *
46   * Scan all endpoints attached to a port, locate their attached CRTCs,
47   * and generate the DRM mask of CRTCs which may be attached to this
48   * encoder.
49   *

if we don't follow the OF-Graph bindings, crtc can't attched to encoder.

Quoted 3 lines

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#19 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Hi,

Quoted 54 lines · Kevin Tang
Quoted 31 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:10写道:
Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+static struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+                      struct drm_plane *primary)
+{
+     struct device_node *port;
+     struct sprd_dpu *dpu;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 819

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     /*
+      * 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_err(drm, "find 'ports' phandle of %s failed\n",
+                       drm->dev->of_node->full_name);
+             return ERR_PTR(-EINVAL);
+     }
+     of_node_put(port);

The YAML binding should already make sure that your binding is sane, and if you still get a DT that doesn't follow it, you have a whole lot of other issues than whether ports is there :)

drivers/gpu/drm/sprd/sprd_dpu.c line 829

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +9 -0
+     dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+                                     primary, NULL,
+                                     &sprd_crtc_funcs, NULL);
+     if (IS_ERR(dpu)) {
+             drm_err(drm, "failed to init crtc.\n");
+             return dpu;
+     }
+
+     dpu->base.port = port;
Quoted 2 lines

But you're still referencing it here, while you called of_node_put on it already? You should only call it once you're done with it.

of_node_put should be called after done with it, this maybe indeed be a bug.
i will fix it.

Quoted 4 lines

I'm not really sure why you would need drm_of_find_possible_crtcs to work then if you don't follow the OF-Graph bindings.

it scan all endports of encoder, if a matching crtc is found by OF-Graph bindings
and then genarate the crtc mask, here is description:
41 /**

42   * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
43   * @dev: DRM device
44   * @port: encoder port to scan for endpoints
45   *
46   * Scan all endpoints attached to a port, locate their attached CRTCs,
47   * and generate the DRM mask of CRTCs which may be attached to this
48   * encoder.
49   *
Quoted 1 line

if we don't follow the OF-Graph bindings, crtc can't attched to encoder.

Yeah, what I'm actually confused about is why you would need the of_parse_phandle call. You usually call drm_of_find_possible_crtcs with the encoder device node, so from your MIPI-DSI driver in your case, and with it's device->of_node pointer and it should work perfectly fine?

Maxime

author
#20 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Hi,

Quoted 60 lines · Maxime Ripard <[email protected]> 于2021年4月15日周四 下午5:03写道:

Hi,

Quoted 54 lines · Kevin Tang
Quoted 31 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:10写道:
Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+static struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+                      struct drm_plane *primary)
+{
+     struct device_node *port;
+     struct sprd_dpu *dpu;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 819

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     /*
+      * 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_err(drm, "find 'ports' phandle of %s failed\n",
+                       drm->dev->of_node->full_name);
+             return ERR_PTR(-EINVAL);
+     }
+     of_node_put(port);

The YAML binding should already make sure that your binding is sane, and if you still get a DT that doesn't follow it, you have a whole lot of other issues than whether ports is there :)

drivers/gpu/drm/sprd/sprd_dpu.c line 829

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +9 -0
+     dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+                                     primary, NULL,
+                                     &sprd_crtc_funcs, NULL);
+     if (IS_ERR(dpu)) {
+             drm_err(drm, "failed to init crtc.\n");
+             return dpu;
+     }
+
+     dpu->base.port = port;
Quoted 2 lines

But you're still referencing it here, while you called of_node_put on it already? You should only call it once you're done with it.

of_node_put should be called after done with it, this maybe indeed be a bug.
i will fix it.

Quoted 4 lines

I'm not really sure why you would need drm_of_find_possible_crtcs to work then if you don't follow the OF-Graph bindings.

it scan all endports of encoder, if a matching crtc is found by OF-Graph bindings
and then genarate the crtc mask, here is description:
41 /**

42   * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
43   * @dev: DRM device
44   * @port: encoder port to scan for endpoints
45   *
46   * Scan all endpoints attached to a port, locate their attached CRTCs,
47   * and generate the DRM mask of CRTCs which may be attached to this
48   * encoder.
49   *
Quoted 1 line

if we don't follow the OF-Graph bindings, crtc can't attched to encoder.

Quoted 4 lines

Yeah, what I'm actually confused about is why you would need the of_parse_phandle call. You usually call drm_of_find_possible_crtcs with the encoder device node, so from your MIPI-DSI driver in your case, and with it's device->of_node pointer and it should work perfectly fine?

I still confused about use drm_of_find_possible_crtcs to bind crtc and encoder, the port of drm_crtc, default is null?

709 /**

710   * struct drm_crtc - central CRTC control structure
711   * @dev: parent DRM device
712   * @port: OF node used by drm_of_find_possible_crtcs()

-----------------------------------------------------------------------------------------------------
25 static uint32_t drm_crtc_port_mask(struct drm_device *dev,
26 struct device_node *port)
27 {
28 unsigned int index = 0;
29 struct drm_crtc *tmp;
30

31   drm_for_each_crtc(tmp, dev) {
32   if (tmp->port == port)
33   return 1 << index;

34
35 index++;
36 }
37
38 return 0;
39 }

I did not see any place to initialize the port of drm_crtc in the drm framework, if not setup it.
it looks like the port of drm_crtc is undefined.

https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/rockchip/rockchip_drm_vop.c#L1851
https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/tilcdc/tilcdc_crtc.c#L1051

The difference from others is that we use of_parse_phandle, as you said, follow the OF-Graph bindings , should be also use of_get_child_by_name replace it?
like this:
-----------------------------------------------------------------------------------

    struct device_node *port;
    port = of_get_child_by_name(dpu->dev->of_node, "port");
    if (!port) {
        DRM_ERROR("find 'ports' phandle of %s failed\n",
              dpu->dev->of_node->full_name);
        return -EINVAL;
    }
    crtc->port = port;
    of_node_put(port);

------------------------------------------------------------------------------------

If we have multiple display controllers, use of_parse_phandle by different index also seem is ok.

display-subsystem {
                compatible = "sprd,display-subsystem";
                ports = <&dpu0_port, &dpu1_port, &dpu2_port......>;

};

dpu0_port = of_parse_phandle(drm->dev->of_node, "ports", 0); dpu1_port = of_parse_phandle(drm->dev->of_node, "ports", 1); dpu2_port = of_parse_phandle(drm->dev->of_node, "ports", 2);

Quoted 2 lines

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#21 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Quoted 86 lines · Kevin Tang
Quoted 57 lines · Maxime Ripard <[email protected]> 于2021年4月15日周四 下午5:03写道:
Quoted 54 lines · Kevin Tang
Quoted 31 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:10写道:
Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+static struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+                      struct drm_plane *primary)
+{
+     struct device_node *port;
+     struct sprd_dpu *dpu;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 819

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     /*
+      * 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_err(drm, "find 'ports' phandle of %s failed\n",
+                       drm->dev->of_node->full_name);
+             return ERR_PTR(-EINVAL);
+     }
+     of_node_put(port);

The YAML binding should already make sure that your binding is sane, and if you still get a DT that doesn't follow it, you have a whole lot of other issues than whether ports is there :)

drivers/gpu/drm/sprd/sprd_dpu.c line 829

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +9 -0
+     dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+                                     primary, NULL,
+                                     &sprd_crtc_funcs, NULL);
+     if (IS_ERR(dpu)) {
+             drm_err(drm, "failed to init crtc.\n");
+             return dpu;
+     }
+
+     dpu->base.port = port;
Quoted 2 lines

But you're still referencing it here, while you called of_node_put on it already? You should only call it once you're done with it.

of_node_put should be called after done with it, this maybe indeed be a bug.
i will fix it.

Quoted 4 lines

I'm not really sure why you would need drm_of_find_possible_crtcs to work then if you don't follow the OF-Graph bindings.

it scan all endports of encoder, if a matching crtc is found by OF-Graph bindings
and then genarate the crtc mask, here is description:
41 /**

42   * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
43   * @dev: DRM device
44   * @port: encoder port to scan for endpoints
45   *
46   * Scan all endpoints attached to a port, locate their attached CRTCs,
47   * and generate the DRM mask of CRTCs which may be attached to this
48   * encoder.
49   *
Quoted 1 line

if we don't follow the OF-Graph bindings, crtc can't attched to encoder.

Quoted 4 lines

Yeah, what I'm actually confused about is why you would need the of_parse_phandle call. You usually call drm_of_find_possible_crtcs with the encoder device node, so from your MIPI-DSI driver in your case, and with it's device->of_node pointer and it should work perfectly fine?

I still confused about use drm_of_find_possible_crtcs to bind crtc and encoder, the port of drm_crtc, default is null?

709 /**

710   * struct drm_crtc - central CRTC control structure
711   * @dev: parent DRM device
712   * @port: OF node used by drm_of_find_possible_crtcs()

-----------------------------------------------------------------------------------------------------
25 static uint32_t drm_crtc_port_mask(struct drm_device *dev,
26 struct device_node *port)
27 {
28 unsigned int index = 0;
29 struct drm_crtc *tmp;
30

31   drm_for_each_crtc(tmp, dev) {
32   if (tmp->port == port)
33   return 1 << index;

34
35 index++;
36 }
37
38 return 0;
39 }

Quoted 6 lines

I did not see any place to initialize the port of drm_crtc in the drm framework, if not setup it.
it looks like the port of drm_crtc is undefined.

https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/rockchip/rockchip_drm_vop.c#L1851
https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/tilcdc/tilcdc_crtc.c#L1051

Yeah, you need to initialize it for drm_of_find_possible_crtcs to work, but my point was that you shouldn't do it through of_parse_phandle, just put the output of_graph_get_port_by_id like you found in those drivers.

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#22 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

In reply to Kevin Tang

Hi,

please see my comments below.

Best regards
Thomas

Am 22.02.21 um 14:28 schrieb Kevin Tang:

Quoted 34 lines

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 <[email protected]>
Cc Chunyan Zhang <[email protected]>

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dpu structure init.

v3:
- Remove dpu_layer stuff layer and commit layers by aotmic_update

v4:
- Use drmm_helpers to allocate crtc and planes.
- Move rotation enum definitions to crtc layer reg bitfields.
- Move allocate crtc and planes to bind funtion.

---
  drivers/gpu/drm/sprd/Kconfig    |   1 +
  drivers/gpu/drm/sprd/Makefile   |   4 +-
  drivers/gpu/drm/sprd/sprd_dpu.c | 964 ++++++++++++++++++++++++++++++++
  drivers/gpu/drm/sprd/sprd_dpu.h | 109 ++++
  drivers/gpu/drm/sprd/sprd_drm.c |   1 +
  drivers/gpu/drm/sprd/sprd_drm.h |   2 +
  6 files changed, 1079 insertions(+), 2 deletions(-)

create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h

drivers/gpu/drm/sprd/Kconfig +0 -0
index 6e80cc9f3..9b4ef9aea 100644

drivers/gpu/drm/sprd/Kconfig config DRM_SPRD · line 6

Quoted 4 lines
drivers/gpu/drm/sprd/Kconfig +1 -0
@@ -3,6 +3,7 @@ config DRM_SPRD
  	depends on ARCH_SPRD || COMPILE_TEST
  	depends on DRM && OF
  	select DRM_KMS_HELPER
+	select VIDEOMODE_HELPERS

Alphabetical order please.

Quoted 146 lines
drivers/gpu/drm/sprd/Kconfig +0 -0
  	select DRM_GEM_CMA_HELPER
  	select DRM_KMS_CMA_HELPER
  	select DRM_MIPI_DSI
drivers/gpu/drm/sprd/Makefile +2 -2
index 86d95d93a..6c25bfa99 100644
@@ -1,5 +1,5 @@
  # SPDX-License-Identifier: GPL-2.0
 
-subdir-ccflags-y += -I$(srctree)/$(src)
+obj-y := sprd_drm.o \
+	sprd_dpu.o
 
-obj-y := sprd_drm.o
drivers/gpu/drm/sprd/sprd_dpu.c new file +125 -0
new file mode 100644
index 000000000..75b7e40d9
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+
+/* Global control registers */
+#define REG_DPU_CTRL	0x04
+#define REG_DPU_CFG0	0x08
+#define REG_PANEL_SIZE	0x20
+#define REG_BLEND_SIZE	0x24
+#define REG_BG_COLOR	0x2C
+
+/* Layer0 control registers */
+#define REG_LAY_BASE_ADDR0	0x30
+#define REG_LAY_BASE_ADDR1	0x34
+#define REG_LAY_BASE_ADDR2	0x38
+#define REG_LAY_CTRL		0x40
+#define REG_LAY_SIZE		0x44
+#define REG_LAY_PITCH		0x48
+#define REG_LAY_POS		0x4C
+#define REG_LAY_ALPHA		0x50
+#define REG_LAY_CROP_START	0x5C
+
+/* Interrupt control registers */
+#define REG_DPU_INT_EN		0x1E0
+#define REG_DPU_INT_CLR		0x1E4
+#define REG_DPU_INT_STS		0x1E8
+
+/* DPI control registers */
+#define REG_DPI_CTRL		0x1F0
+#define REG_DPI_H_TIMING	0x1F4
+#define REG_DPI_V_TIMING	0x1F8
+
+/* MMU control registers */
+#define REG_MMU_EN			0x800
+#define REG_MMU_VPN_RANGE		0x80C
+#define REG_MMU_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)
+
+/* Layer control bits */
+#define BIT_DPU_LAY_EN				BIT(0)
+#define BIT_DPU_LAY_LAYER_ALPHA			(0x01 << 2)
+#define BIT_DPU_LAY_COMBO_ALPHA			(0x02 << 2)
+#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE		(0x00 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE		(0x01 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE		(0x02 << 4)
+#define BIT_DPU_LAY_FORMAT_ARGB8888			(0x03 << 4)
+#define BIT_DPU_LAY_FORMAT_RGB565			(0x04 << 4)
+#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3		(0x00 << 8)
+#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0		(0x01 << 8)
+#define BIT_DPU_LAY_NO_SWITCH			(0x00 << 10)
+#define BIT_DPU_LAY_RB_OR_UV_SWITCH		(0x01 << 10)
+#define BIT_DPU_LAY_MODE_BLEND_NORMAL		(0x00 << 16)
+#define BIT_DPU_LAY_MODE_BLEND_PREMULT		(0x01 << 16)
+#define BIT_DPU_LAY_ROTATION_0		(0x00 << 20)
+#define BIT_DPU_LAY_ROTATION_90		(0x01 << 20)
+#define BIT_DPU_LAY_ROTATION_180	(0x02 << 20)
+#define BIT_DPU_LAY_ROTATION_270	(0x03 << 20)
+#define BIT_DPU_LAY_ROTATION_0_M	(0x04 << 20)
+#define BIT_DPU_LAY_ROTATION_90_M	(0x05 << 20)
+#define BIT_DPU_LAY_ROTATION_180_M	(0x06 << 20)
+#define BIT_DPU_LAY_ROTATION_270_M	(0x07 << 20)
+
+/* Interrupt control & status bits */
+#define BIT_DPU_INT_DONE		BIT(0)
+#define BIT_DPU_INT_TE			BIT(1)
+#define BIT_DPU_INT_ERR			BIT(2)
+#define BIT_DPU_INT_UPDATE_DONE		BIT(4)
+#define BIT_DPU_INT_VSYNC		BIT(5)
+#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,

drivers/gpu/drm/sprd/sprd_dpu.c line 135

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_YVU420,
+};
+
+struct sprd_plane {
+	struct drm_plane base;
+	u32 index;
+};

I cannot figure out the purpose of this structure. 'index' is the index of the plane. But there are only primary planes. So you should be able to use drm_plane.index instead.

If index is more like a position in the plane stack, can't you use the zpos property directly?

If there are other non-primary planes, this structure should be called struct sprd_primary_plane.

Quoted 473 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +473 -0
+
+static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct sprd_plane, base);
+}
+
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	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_err(dpu->drm, "--- iommu interrupt err: 0x%04x ---\n", val);
+
+		if (val & BIT_DPU_INT_MMU_INV_RD)
+			drm_err(dpu->drm, "iommu invalid read error, addr: 0x%08x\n",
+				readl(ctx->base + REG_MMU_INV_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_INV_WR)
+			drm_err(dpu->drm, "iommu invalid write error, addr: 0x%08x\n",
+				readl(ctx->base + REG_MMU_INV_ADDR_WR));
+		if (val & BIT_DPU_INT_MMU_VAOR_RD)
+			drm_err(dpu->drm, "iommu va out of range read error, addr: 0x%08x\n",
+				readl(ctx->base + REG_MMU_VAOR_ADDR_RD));
+		if (val & BIT_DPU_INT_MMU_VAOR_WR)
+			drm_err(dpu->drm, "iommu va out of range write error, addr: 0x%08x\n",
+				readl(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(dpu->drm, "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 int dpu_wait_stop_done(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	int rc;
+
+	if (ctx->stopped)
+		return 0;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_stop,
+					       msecs_to_jiffies(500));
+	ctx->evt_stop = false;
+
+	ctx->stopped = true;
+
+	if (!rc) {
+		drm_err(dpu->drm, "dpu wait for stop done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int dpu_wait_update_done(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	int rc;
+
+	ctx->evt_update = false;
+
+	rc = wait_event_interruptible_timeout(ctx->wait_queue, ctx->evt_update,
+					       msecs_to_jiffies(500));
+
+	if (!rc) {
+		drm_err(dpu->drm, "dpu wait for reg update done time out!\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static u32 drm_format_to_dpu(struct drm_framebuffer *fb)
+{
+	u32 format = 0;
+
+	switch (fb->format->format) {
+	case DRM_FORMAT_BGRA8888:
+		/* BGRA8888 -> ARGB8888 */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_RGBA8888:
+		/* RGBA8888 -> ABGR8888 */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* fallthrough */
+	case DRM_FORMAT_ABGR8888:
+		/* RB switch */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* fallthrough */
+	case DRM_FORMAT_ARGB8888:
+		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_XBGR8888:
+		/* RB switch */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* fallthrough */
+	case DRM_FORMAT_XRGB8888:
+		format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+		break;
+	case DRM_FORMAT_BGR565:
+		/* RB switch */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		/* fallthrough */
+	case DRM_FORMAT_RGB565:
+		format |= BIT_DPU_LAY_FORMAT_RGB565;
+		break;
+	case DRM_FORMAT_NV12:
+		/* 2-Lane: Yuv420 */
+		format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_NV21:
+		/* 2-Lane: Yuv420 */
+		format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV16:
+		/* 2-Lane: Yuv422 */
+		format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+		/* UV endian */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	case DRM_FORMAT_NV61:
+		/* 2-Lane: Yuv422 */
+		format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YUV420:
+		format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_NO_SWITCH;
+		break;
+	case DRM_FORMAT_YVU420:
+		format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+		/* Y endian */
+		format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+		/* UV endian */
+		format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+		break;
+	default:
+		break;
+	}
+
+	return format;
+}
+
+static u32 drm_rotation_to_dpu(struct drm_plane_state *state)
+{
+	u32 rotation = 0;
+
+	switch (state->rotation) {
+	default:
+	case DRM_MODE_ROTATE_0:
+		rotation = BIT_DPU_LAY_ROTATION_0;
+		break;
+	case DRM_MODE_ROTATE_90:
+		rotation = BIT_DPU_LAY_ROTATION_90;
+		break;
+	case DRM_MODE_ROTATE_180:
+		rotation = BIT_DPU_LAY_ROTATION_180;
+		break;
+	case DRM_MODE_ROTATE_270:
+		rotation = BIT_DPU_LAY_ROTATION_270;
+		break;
+	case DRM_MODE_REFLECT_Y:
+		rotation = BIT_DPU_LAY_ROTATION_180_M;
+		break;
+	case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
+		rotation = BIT_DPU_LAY_ROTATION_90_M;
+		break;
+	case DRM_MODE_REFLECT_X:
+		rotation = BIT_DPU_LAY_ROTATION_0_M;
+		break;
+	case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
+		rotation = BIT_DPU_LAY_ROTATION_270_M;
+		break;
+	}
+
+	return rotation;
+}
+
+static u32 drm_blend_to_dpu(struct drm_plane_state *state)
+{
+	u32 blend = 0;
+
+	switch (state->pixel_blend_mode) {
+	case DRM_MODE_BLEND_COVERAGE:
+		/* alpha mode select - combo alpha */
+		blend |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Normal mode */
+		blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
+		break;
+	case DRM_MODE_BLEND_PREMULTI:
+		/* alpha mode select - combo alpha */
+		blend |= BIT_DPU_LAY_COMBO_ALPHA;
+		/* Pre-mult mode */
+		blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
+		break;
+	case DRM_MODE_BLEND_PIXEL_NONE:
+	default:
+		/* don't do blending, maybe RGBX */
+		/* alpha mode select - layer alpha */
+		blend |= BIT_DPU_LAY_LAYER_ALPHA;
+		break;
+	}
+
+	return blend;
+}
+
+static void sprd_dpu_layer(struct sprd_dpu *dpu, struct sprd_plane *plane,
+			struct drm_plane_state *state)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	struct drm_gem_cma_object *cma_obj;
+	const struct drm_format_info *info;
+	struct drm_framebuffer *fb = state->fb;
+	u32 addr, size, offset, pitch, blend, format, rotation;
+	u32 src_x = state->src_x >> 16;
+	u32 src_y = state->src_y >> 16;
+	u32 src_w = state->src_w >> 16;
+	u32 src_h = state->src_h >> 16;
+	u32 dst_x = state->crtc_x;
+	u32 dst_y = state->crtc_y;
+	u32 alpha = state->alpha;
+	int i;
+
+	offset = (dst_x & 0xffff) | (dst_y << 16);
+	size = (src_w & 0xffff) | (src_h << 16);
+
+	for (i = 0; i < fb->format->num_planes; i++) {
+		cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+		addr = cma_obj->paddr + fb->offsets[i];
+
+		if (i == 0)
+			layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, addr, plane->index);
+		else if (i == 1)
+			layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, addr, plane->index);
+		else
+			layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, addr, plane->index);
+	}
+
+	info = drm_format_info(fb->format->format);
+	if (fb->format->num_planes == 3) {
+		/* UV pitch is 1/2 of Y pitch */
+		pitch = (fb->pitches[0] / info->cpp[0]) |
+				(fb->pitches[0] / info->cpp[0] << 15);
+	} else {
+		pitch = fb->pitches[0] / info->cpp[0];
+	}
+
+	layer_reg_wr(ctx, REG_LAY_POS, offset, plane->index);
+	layer_reg_wr(ctx, REG_LAY_SIZE, size, plane->index);
+	layer_reg_wr(ctx, REG_LAY_CROP_START,
+			src_y << 16 | src_x, plane->index);
+	layer_reg_wr(ctx, REG_LAY_ALPHA, alpha, plane->index);
+	layer_reg_wr(ctx, REG_LAY_PITCH, pitch, plane->index);
+
+	format = drm_format_to_dpu(fb);
+	blend = drm_blend_to_dpu(state);
+	rotation = drm_rotation_to_dpu(state);
+
+	layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN |
+				format |
+				blend |
+				rotation,
+				plane->index);
+}
+
+static void sprd_dpu_flip(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 reg_val;
+
+	/*
+	 * Make sure the dpu is in stop status. DPU has no shadow
+	 * registers in EDPI mode. So the config registers can only be
+	 * updated in the rising edge of DPU_RUN bit.
+	 */
+	if (ctx->if_type == SPRD_DPU_IF_EDPI)
+		dpu_wait_stop_done(dpu);
+
+	/* update trigger and wait */
+	if (ctx->if_type == SPRD_DPU_IF_DPI) {
+		if (!ctx->stopped) {
+			dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
+			dpu_wait_update_done(dpu);
+		}
+
+		dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR);
+	} else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+		dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+		ctx->stopped = false;
+	}
+
+	/*
+	 * If the following interrupt was disabled in isr,
+	 * re-enable it.
+	 */
+	reg_val = 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);
+}
+
+static void sprd_dpu_init(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 size;
+
+	writel(0x00, ctx->base + REG_BG_COLOR);
+
+	size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
+	writel(size, ctx->base + REG_PANEL_SIZE);
+	writel(size, ctx->base + REG_BLEND_SIZE);
+
+	writel(0x00, ctx->base + REG_MMU_EN);
+	writel(0x00, ctx->base + REG_MMU_PPN1);
+	writel(0xffff, ctx->base + REG_MMU_RANGE1);
+	writel(0x00, ctx->base + REG_MMU_PPN2);
+	writel(0xffff, ctx->base + REG_MMU_RANGE2);
+	writel(0x1ffff, ctx->base + REG_MMU_VPN_RANGE);
+}
+
+static void sprd_dpu_fini(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	writel(0x00, ctx->base + REG_DPU_INT_EN);
+	writel(0xff, ctx->base + REG_DPU_INT_CLR);
+}
+
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 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(reg_val, ctx->base + REG_DPI_H_TIMING);
+
+		reg_val = ctx->vm.vsync_len << 0 |
+			  ctx->vm.vback_porch << 8 |
+			  ctx->vm.vfront_porch << 20;
+		writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+		if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+			drm_warn(dpu->drm, "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 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(int_mask, ctx->base + REG_DPU_INT_EN);
+}
+
+void sprd_dpu_run(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+	ctx->stopped = false;
+}
+
+void sprd_dpu_stop(struct sprd_dpu *dpu)
+{
+	struct dpu_context *ctx = &dpu->ctx;
+
+	if (ctx->if_type == SPRD_DPU_IF_DPI)
+		dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP);
+
+	dpu_wait_stop_done(dpu);
+}
+
+static int sprd_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc_state *crtc_state;
+	u32 fmt;
+
+	if (!fb || !state->crtc)
+		return 0;
+
+	fmt = drm_format_to_dpu(fb);
+	if (!fmt)
+		return -EINVAL;
+
+	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	return drm_atomic_helper_check_plane_state(state, crtc_state,
+						  DRM_PLANE_HELPER_NO_SCALING,
+						  DRM_PLANE_HELPER_NO_SCALING,
+						  true, true);
+}
+
+static void sprd_plane_atomic_update(struct drm_plane *drm_plane,
+				    struct drm_plane_state *old_state)
+{

drivers/gpu/drm/sprd/sprd_dpu.c line 618

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+	struct drm_plane_state *state = drm_plane->state;
+	struct sprd_plane *plane = to_sprd_plane(drm_plane);
+	struct sprd_dpu *dpu = to_sprd_crtc(state->crtc);
+
+	/* start configure dpu layers */
+	sprd_dpu_layer(dpu, plane, state);
+}
+
+static void sprd_plane_atomic_disable(struct drm_plane *drm_plane,
+				     struct drm_plane_state *old_state)

I think this needs a rebase onto the latest DRM code. Many of these callbacks now receive struct drm_atomic_state instead of drm_plane_state. Same for CRTC states.

Quoted 35 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +35 -0
+{
+	struct sprd_plane *plane = to_sprd_plane(drm_plane);
+	struct sprd_dpu *dpu = to_sprd_crtc(old_state->crtc);
+
+	layer_reg_wr(&dpu->ctx, REG_LAY_CTRL, 0x00, plane->index);
+}
+
+static void sprd_plane_create_properties(struct sprd_plane *plane, int index)
+{
+	unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				       BIT(DRM_MODE_BLEND_PREMULTI) |
+				       BIT(DRM_MODE_BLEND_COVERAGE);
+
+	/* create rotation property */
+	drm_plane_create_rotation_property(&plane->base,
+					   DRM_MODE_ROTATE_0,
+					   DRM_MODE_ROTATE_MASK |
+					   DRM_MODE_REFLECT_MASK);
+
+	/* create alpha property */
+	drm_plane_create_alpha_property(&plane->base);
+
+	/* create blend mode property */
+	drm_plane_create_blend_mode_property(&plane->base, supported_modes);
+
+	/* create zpos property */
+	drm_plane_create_zpos_immutable_property(&plane->base, index);
+}
+
+static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
+	.atomic_check = sprd_plane_atomic_check,
+	.atomic_update = sprd_plane_atomic_update,
+	.atomic_disable = sprd_plane_atomic_disable,
+};
+

drivers/gpu/drm/sprd/sprd_dpu.c line 663

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+static const struct drm_plane_funcs sprd_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane	= drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct sprd_plane *sprd_plane_init(struct drm_device *drm)

Maybe sprd_planes_init()? It initializes all planes.

Quoted 14 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +14 -0
+{
+	struct sprd_plane *plane, *primary;
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		plane = drmm_universal_plane_alloc(drm, struct sprd_plane, base,
+					       1, &sprd_plane_funcs,
+					       primary_fmts, ARRAY_SIZE(primary_fmts),
+					       NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
+		if (IS_ERR(plane)) {
+			drm_err(drm, "fail to init primary plane\n");
+			return plane;
+		}
+

drivers/gpu/drm/sprd/sprd_dpu.c line 687

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+		drm_plane_helper_add(&plane->base, &sprd_plane_helper_funcs);
+
+		sprd_plane_create_properties(plane, i);
+
+		plane->index = i;
+		if (i == 0)
+			primary = plane;
+	}
+
+	return primary;

I find this confusing: there are only primary planes, right? But what's special about the first one? The returned pointer is not used. Maybe just return an errno code or 0 here?

Quoted 416 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +277 -0
+}
+
+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_dbg(dpu->drm, "%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_atomic_state *state)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+	sprd_dpu_init(dpu);
+
+	sprd_dpi_init(dpu);
+
+	enable_irq(dpu->ctx.irq);
+
+	drm_crtc_vblank_on(&dpu->base);
+}
+
+static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
+				    struct drm_atomic_state *state)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+	struct drm_device *drm = dpu->base.dev;
+
+	drm_crtc_vblank_off(&dpu->base);
+
+	disable_irq(dpu->ctx.irq);
+
+	sprd_dpu_fini(dpu);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	drm_dbg(crtc->dev, "%s()\n", __func__);
+
+	return 0;
+}
+
+static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+	struct drm_device *drm = dpu->base.dev;
+
+	sprd_dpu_flip(dpu);
+
+	spin_lock_irq(&drm->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+	dpu_reg_set(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+
+	return 0;
+}
+
+static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+	dpu_reg_clr(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
+	.mode_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 struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+			 struct drm_plane *primary)
+{
+	struct device_node *port;
+	struct sprd_dpu *dpu;
+
+	/*
+	 * 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_err(drm, "find 'ports' phandle of %s failed\n",
+			  drm->dev->of_node->full_name);
+		return ERR_PTR(-EINVAL);
+	}
+	of_node_put(port);
+
+	dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+					primary, NULL,
+					&sprd_crtc_funcs, NULL);
+	if (IS_ERR(dpu)) {
+		drm_err(drm, "failed to init crtc.\n");
+		return dpu;
+	}
+
+	dpu->base.port = port;
+	drm_crtc_helper_add(&dpu->base, &sprd_crtc_helper_funcs);
+
+	return dpu;
+}
+
+static irqreturn_t sprd_dpu_isr(int irq, void *data)
+{
+	struct sprd_dpu *dpu = data;
+	struct dpu_context *ctx = &dpu->ctx;
+	u32 reg_val, int_mask = 0;
+
+	reg_val = readl(ctx->base + REG_DPU_INT_STS);
+
+	/* disable err interrupt */
+	if (reg_val & BIT_DPU_INT_ERR) {
+		int_mask |= BIT_DPU_INT_ERR;
+		drm_warn(dpu->drm, "Warning: dpu underflow!\n");
+	}
+
+	/* dpu update done isr */
+	if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
+		ctx->evt_update = true;
+		wake_up_interruptible_all(&ctx->wait_queue);
+	}
+
+	/* dpu stop done isr */
+	if (reg_val & BIT_DPU_INT_DONE) {
+		ctx->evt_stop = true;
+		wake_up_interruptible_all(&ctx->wait_queue);
+	}
+
+	if (reg_val & BIT_DPU_INT_VSYNC)
+		drm_crtc_handle_vblank(&dpu->base);
+
+	int_mask |= check_mmu_isr(dpu, reg_val);
+
+	writel(reg_val, ctx->base + REG_DPU_INT_CLR);
+	dpu_reg_clr(ctx, REG_DPU_INT_EN, int_mask);
+
+	return IRQ_HANDLED;
+}
+
+static int sprd_dpu_context_init(struct sprd_dpu *dpu,
+				struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct dpu_context *ctx = &dpu->ctx;
+	struct resource *res;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ctx->base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!ctx->base) {
+		dev_err(dev, "failed to map dpu registers\n");
+		return -EFAULT;
+	}
+
+	ctx->irq = platform_get_irq(pdev, 0);
+	if (ctx->irq < 0) {
+		dev_err(dev, "failed to get dpu irq\n");
+		return ctx->irq;
+	}
+
+	irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
+					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 int sprd_dpu_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = data;
+	struct sprd_dpu *dpu;
+	struct sprd_plane *plane;
+	int ret;
+
+	plane = sprd_plane_init(drm);
+	if (IS_ERR(plane))
+		return PTR_ERR(plane);
+
+	dpu = sprd_crtc_init(drm, &plane->base);
+	if (IS_ERR(dpu))
+		return PTR_ERR(dpu);
+
+	dpu->drm = drm;
+	dev_set_drvdata(dev, dpu);
+
+	ret = sprd_dpu_context_init(dpu, dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct component_ops dpu_component_ops = {
+	.bind = sprd_dpu_bind,
+};
+
+static const struct of_device_id dpu_match_table[] = {
+	{ .compatible = "sprd,sharkl3-dpu" },
+	{ /* sentinel */ },
+};
+
+static int sprd_dpu_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &dpu_component_ops);
+}
+
+static int sprd_dpu_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dpu_component_ops);
+
+	return 0;
+}
+
+struct platform_driver sprd_dpu_driver = {
+	.probe = sprd_dpu_probe,
+	.remove = sprd_dpu_remove,
+	.driver = {
+		.name = "sprd-dpu-drv",
+		.of_match_table = dpu_match_table,
+	},
+};
+
+MODULE_AUTHOR("Leon He <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
drivers/gpu/drm/sprd/sprd_dpu.h new file +109 -0
new file mode 100644
index 000000000..157a78f24
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef __SPRD_DPU_H__
+#define __SPRD_DPU_H__
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <uapi/drm/drm_mode.h>
+
+/* DPU Layer registers offset */
+#define DPU_LAY_REG_OFFSET	0x30
+
+enum {
+	SPRD_DPU_IF_DPI,
+	SPRD_DPU_IF_EDPI,
+	SPRD_DPU_IF_LIMIT
+};
+
+/**
+ * Sprd DPU context structure
+ *
+ * @base: DPU controller base address
+ * @irq: IRQ number to install the handler for
+ * @if_type: The type of DPI interface, default is DPI mode.
+ * @vm: videomode structure to use for DPU and DPI initialization
+ * @stopped: indicates whether DPU are stopped
+ * @wait_queue: wait queue, used to wait for DPU shadow register update done and
+ * DPU stop register done interrupt signal.
+ * @evt_update: wait queue condition for DPU shadow register
+ * @evt_stop: wait queue condition for DPU stop register
+ */
+struct dpu_context {
+	void __iomem *base;
+	int irq;
+	u8 if_type;
+	struct videomode vm;
+	bool stopped;
+	wait_queue_head_t wait_queue;
+	bool evt_update;
+	bool evt_stop;
+};
+
+/**
+ * Sprd DPU device structure
+ *
+ * @crtc: crtc object
+ * @drm: A point to drm device
+ * @ctx: DPU's implementation specific context object
+ */
+struct sprd_dpu {
+	struct drm_crtc base;
+	struct drm_device *drm;
+	struct dpu_context ctx;
+};
+
+static inline struct sprd_dpu *to_sprd_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct sprd_dpu, base);
+}
+
+static inline void
+dpu_reg_set(struct dpu_context *ctx, u32 offset, u32 set_bits)
+{
+	u32 bits = readl_relaxed(ctx->base + offset);
+
+	writel(bits | set_bits, ctx->base + offset);
+}
+
+static inline void
+dpu_reg_clr(struct dpu_context *ctx, u32 offset, u32 clr_bits)
+{
+	u32 bits = readl_relaxed(ctx->base + offset);
+
+	writel(bits & ~clr_bits, ctx->base + offset);
+}
+
+static inline u32
+layer_reg_rd(struct dpu_context *ctx, u32 offset, int index)
+{
+	u32 layer_offset = offset + index * DPU_LAY_REG_OFFSET;
+
+	return readl(ctx->base + layer_offset);
+}
+
+static inline void
+layer_reg_wr(struct dpu_context *ctx, u32 offset, u32 cfg_bits, int index)
+{
+	u32 layer_offset =  offset + index * DPU_LAY_REG_OFFSET;
+
+	writel(cfg_bits, ctx->base + layer_offset);
+}
+
+void sprd_dpu_run(struct sprd_dpu *dpu);
+void sprd_dpu_stop(struct sprd_dpu *dpu);
+
+#endif
drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index a1d3ed655..c626c6caf 100644
@@ -191,6 +191,7 @@ static struct platform_driver sprd_drm_driver = {
 
  static struct platform_driver *sprd_drm_drivers[]  = {
  	&sprd_drm_driver,
+	&sprd_dpu_driver,
  };
 
  static int __init sprd_drm_init(void)
drivers/gpu/drm/sprd/sprd_drm.h +2 -0
index 9781fd591..85d4a8b9f 100644
@@ -13,4 +13,6 @@ struct sprd_drm {
  	struct drm_device drm;
  };
 
+extern struct platform_driver sprd_dpu_driver;
+
  #endif /* _SPRD_DRM_H_ */
Signature · 6 lines
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer
author
#23 lore raw

Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

Hi Thomas,

Quoted 39 lines · Thomas Zimmermann <[email protected]> 于2021年4月8日周四 下午6:53写道:

Hi,

please see my comments below.

Best regards
Thomas

Am 22.02.21 um 14:28 schrieb Kevin Tang:

Quoted 1 line

Adds DPU(Display Processor Unit) support for the Unisoc's display

subsystem.

Quoted 1 line

It's support multi planes, scaler, rotation, PQ(Picture Quality) and

more.

Quoted 27 lines
Cc Orson Zhai <[email protected]>
Cc Chunyan Zhang <[email protected]>

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dpu structure init.

v3:
- Remove dpu_layer stuff layer and commit layers by aotmic_update

v4:
- Use drmm_helpers to allocate crtc and planes.
- Move rotation enum definitions to crtc layer reg bitfields.
- Move allocate crtc and planes to bind funtion.

---
  drivers/gpu/drm/sprd/Kconfig    |   1 +
  drivers/gpu/drm/sprd/Makefile   |   4 +-
  drivers/gpu/drm/sprd/sprd_dpu.c | 964 ++++++++++++++++++++++++++++++++
  drivers/gpu/drm/sprd/sprd_dpu.h | 109 ++++
  drivers/gpu/drm/sprd/sprd_drm.c |   1 +
  drivers/gpu/drm/sprd/sprd_drm.h |   2 +
  6 files changed, 1079 insertions(+), 2 deletions(-)

create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.c create mode 100644 drivers/gpu/drm/sprd/sprd_dpu.h

Quoted 12 lines

drivers/gpu/drm/sprd/Kconfig config DRM_SPRD · line 6

Quoted 9 lines
drivers/gpu/drm/sprd/Kconfig +1 -0
index 6e80cc9f3..9b4ef9aea 100644
@@ -3,6 +3,7 @@ config DRM_SPRD
      depends on ARCH_SPRD || COMPILE_TEST
      depends on DRM && OF
      select DRM_KMS_HELPER
+     select VIDEOMODE_HELPERS

Alphabetical order please.

Thks, i will be fix it.

Quoted 165 lines

Review

Quoted 5 lines
drivers/gpu/drm/sprd/Kconfig +0 -0
      select DRM_GEM_CMA_HELPER
      select DRM_KMS_CMA_HELPER
      select DRM_MIPI_DSI
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/Makefile

Review

Quoted 12 lines

index 86d95d93a..6c25bfa99 100644

drivers/gpu/drm/sprd/Makefile +2 -2
@@ -1,5 +1,5 @@
  # SPDX-License-Identifier: GPL-2.0
 
-subdir-ccflags-y += -I$(srctree)/$(src)
+obj-y := sprd_drm.o \
+     sprd_dpu.o
 
-obj-y := sprd_drm.o
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 130 lines

new file mode 100644
index 000000000..75b7e40d9

drivers/gpu/drm/sprd/sprd_dpu.c new file +125 -0
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+
+/* Global control registers */
+#define REG_DPU_CTRL 0x04
+#define REG_DPU_CFG0 0x08
+#define REG_PANEL_SIZE       0x20
+#define REG_BLEND_SIZE       0x24
+#define REG_BG_COLOR 0x2C
+
+/* Layer0 control registers */
+#define REG_LAY_BASE_ADDR0   0x30
+#define REG_LAY_BASE_ADDR1   0x34
+#define REG_LAY_BASE_ADDR2   0x38
+#define REG_LAY_CTRL         0x40
+#define REG_LAY_SIZE         0x44
+#define REG_LAY_PITCH                0x48
+#define REG_LAY_POS          0x4C
+#define REG_LAY_ALPHA                0x50
+#define REG_LAY_CROP_START   0x5C
+
+/* Interrupt control registers */
+#define REG_DPU_INT_EN               0x1E0
+#define REG_DPU_INT_CLR              0x1E4
+#define REG_DPU_INT_STS              0x1E8
+
+/* DPI control registers */
+#define REG_DPI_CTRL         0x1F0
+#define REG_DPI_H_TIMING     0x1F4
+#define REG_DPI_V_TIMING     0x1F8
+
+/* MMU control registers */
+#define REG_MMU_EN                   0x800
+#define REG_MMU_VPN_RANGE            0x80C
+#define REG_MMU_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)
+
+/* Layer control bits */
+#define BIT_DPU_LAY_EN                               BIT(0)
+#define BIT_DPU_LAY_LAYER_ALPHA                      (0x01 << 2)
+#define BIT_DPU_LAY_COMBO_ALPHA                      (0x02 << 2)
+#define BIT_DPU_LAY_FORMAT_YUV422_2PLANE             (0x00 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_2PLANE             (0x01 << 4)
+#define BIT_DPU_LAY_FORMAT_YUV420_3PLANE             (0x02 << 4)
+#define BIT_DPU_LAY_FORMAT_ARGB8888                  (0x03 << 4)
+#define BIT_DPU_LAY_FORMAT_RGB565                    (0x04 << 4)
+#define BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3             (0x00 << 8)
+#define BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0             (0x01 << 8)
+#define BIT_DPU_LAY_NO_SWITCH                        (0x00 << 10)
+#define BIT_DPU_LAY_RB_OR_UV_SWITCH          (0x01 << 10)
+#define BIT_DPU_LAY_MODE_BLEND_NORMAL                (0x00 << 16)
+#define BIT_DPU_LAY_MODE_BLEND_PREMULT               (0x01 << 16)
+#define BIT_DPU_LAY_ROTATION_0               (0x00 << 20)
+#define BIT_DPU_LAY_ROTATION_90              (0x01 << 20)
+#define BIT_DPU_LAY_ROTATION_180     (0x02 << 20)
+#define BIT_DPU_LAY_ROTATION_270     (0x03 << 20)
+#define BIT_DPU_LAY_ROTATION_0_M     (0x04 << 20)
+#define BIT_DPU_LAY_ROTATION_90_M    (0x05 << 20)
+#define BIT_DPU_LAY_ROTATION_180_M   (0x06 << 20)
+#define BIT_DPU_LAY_ROTATION_270_M   (0x07 << 20)
+
+/* Interrupt control & status bits */
+#define BIT_DPU_INT_DONE             BIT(0)
+#define BIT_DPU_INT_TE                       BIT(1)
+#define BIT_DPU_INT_ERR                      BIT(2)
+#define BIT_DPU_INT_UPDATE_DONE              BIT(4)
+#define BIT_DPU_INT_VSYNC            BIT(5)
+#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,

drivers/gpu/drm/sprd/sprd_dpu.c line 135

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     DRM_FORMAT_NV16,
+     DRM_FORMAT_NV61,
+     DRM_FORMAT_YUV420,
+     DRM_FORMAT_YVU420,
+};
+
+struct sprd_plane {
+     struct drm_plane base;
+     u32 index;
+};

I cannot figure out the purpose of this structure. 'index' is the index of the plane. But there are only primary planes. So you should be able to use drm_plane.index instead.

Quoted 2 lines

If index is more like a position in the plane stack, can't you use the zpos property directly?

The sprd_plane.index is a positon of drm plane, i will try the zpos property, thks.

Quoted 4 lines

If there are other non-primary planes, this structure should be called struct sprd_primary_plane.

Thks, i wil fix it.

Quoted 501 lines
Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +8 -0
+
+static inline struct sprd_plane *to_sprd_plane(struct drm_plane *plane)
+{
+     return container_of(plane, struct sprd_plane, base);
+}
+
+static u32 check_mmu_isr(struct sprd_dpu *dpu, u32 reg_val)
+{

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     struct dpu_context *ctx = &dpu->ctx;
+     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_err(dpu->drm, "--- iommu interrupt err: 0x%04x ---\n",

val);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+
+             if (val & BIT_DPU_INT_MMU_INV_RD)
+                     drm_err(dpu->drm, "iommu invalid read error, addr:

0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_INV_WR)
+                     drm_err(dpu->drm, "iommu invalid write error,

addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_INV_ADDR_WR));
+             if (val & BIT_DPU_INT_MMU_VAOR_RD)
+                     drm_err(dpu->drm, "iommu va out of range read

error, addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 3 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +3 -0
+                             readl(ctx->base + REG_MMU_VAOR_ADDR_RD));
+             if (val & BIT_DPU_INT_MMU_VAOR_WR)
+                     drm_err(dpu->drm, "iommu va out of range write

error, addr: 0x%08x\n",

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+                             readl(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(dpu->drm, "layer%d: 0x%08x 0x%08x

0x%08x ctrl: 0x%08x\n", i,

drivers/gpu/drm/sprd/sprd_dpu.c line 172

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx,

REG_LAY_BASE_ADDR0, i),

drivers/gpu/drm/sprd/sprd_dpu.c line 173

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx,

REG_LAY_BASE_ADDR1, i),

drivers/gpu/drm/sprd/sprd_dpu.c line 174

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx,

REG_LAY_BASE_ADDR2, i),

drivers/gpu/drm/sprd/sprd_dpu.c line 175

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                     layer_reg_rd(ctx, REG_LAY_CTRL,

i));

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +5 -0
+             }
+     }
+
+     return val;
+}

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+static int dpu_wait_stop_done(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     int rc;
+
+     if (ctx->stopped)
+             return 0;
+
+     rc = wait_event_interruptible_timeout(ctx->wait_queue,

ctx->evt_stop,

Quoted 11 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +11 -0
+                                            msecs_to_jiffies(500));
+     ctx->evt_stop = false;
+
+     ctx->stopped = true;
+
+     if (!rc) {
+             drm_err(dpu->drm, "dpu wait for stop done time out!\n");
+             return -ETIMEDOUT;
+     }
+
+     return 0;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+}
+
+static int dpu_wait_update_done(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     int rc;
+
+     ctx->evt_update = false;
+
+     rc = wait_event_interruptible_timeout(ctx->wait_queue,

ctx->evt_update,

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+                                            msecs_to_jiffies(500));
+
+     if (!rc) {
+             drm_err(dpu->drm, "dpu wait for reg update done time

out!\n");

Quoted 149 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +149 -0
+             return -ETIMEDOUT;
+     }
+
+     return 0;
+}
+
+static u32 drm_format_to_dpu(struct drm_framebuffer *fb)
+{
+     u32 format = 0;
+
+     switch (fb->format->format) {
+     case DRM_FORMAT_BGRA8888:
+             /* BGRA8888 -> ARGB8888 */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_RGBX8888:
+     case DRM_FORMAT_RGBA8888:
+             /* RGBA8888 -> ABGR8888 */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             /* fallthrough */
+     case DRM_FORMAT_ABGR8888:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_ARGB8888:
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_XBGR8888:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_XRGB8888:
+             format |= BIT_DPU_LAY_FORMAT_ARGB8888;
+             break;
+     case DRM_FORMAT_BGR565:
+             /* RB switch */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             /* fallthrough */
+     case DRM_FORMAT_RGB565:
+             format |= BIT_DPU_LAY_FORMAT_RGB565;
+             break;
+     case DRM_FORMAT_NV12:
+             /* 2-Lane: Yuv420 */
+             format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_NV21:
+             /* 2-Lane: Yuv420 */
+             format |= BIT_DPU_LAY_FORMAT_YUV420_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     case DRM_FORMAT_NV16:
+             /* 2-Lane: Yuv422 */
+             format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B3B2B1B0;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     case DRM_FORMAT_NV61:
+             /* 2-Lane: Yuv422 */
+             format |= BIT_DPU_LAY_FORMAT_YUV422_2PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_YUV420:
+             format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_NO_SWITCH;
+             break;
+     case DRM_FORMAT_YVU420:
+             format |= BIT_DPU_LAY_FORMAT_YUV420_3PLANE;
+             /* Y endian */
+             format |= BIT_DPU_LAY_DATA_ENDIAN_B0B1B2B3;
+             /* UV endian */
+             format |= BIT_DPU_LAY_RB_OR_UV_SWITCH;
+             break;
+     default:
+             break;
+     }
+
+     return format;
+}
+
+static u32 drm_rotation_to_dpu(struct drm_plane_state *state)
+{
+     u32 rotation = 0;
+
+     switch (state->rotation) {
+     default:
+     case DRM_MODE_ROTATE_0:
+             rotation = BIT_DPU_LAY_ROTATION_0;
+             break;
+     case DRM_MODE_ROTATE_90:
+             rotation = BIT_DPU_LAY_ROTATION_90;
+             break;
+     case DRM_MODE_ROTATE_180:
+             rotation = BIT_DPU_LAY_ROTATION_180;
+             break;
+     case DRM_MODE_ROTATE_270:
+             rotation = BIT_DPU_LAY_ROTATION_270;
+             break;
+     case DRM_MODE_REFLECT_Y:
+             rotation = BIT_DPU_LAY_ROTATION_180_M;
+             break;
+     case (DRM_MODE_REFLECT_Y | DRM_MODE_ROTATE_90):
+             rotation = BIT_DPU_LAY_ROTATION_90_M;
+             break;
+     case DRM_MODE_REFLECT_X:
+             rotation = BIT_DPU_LAY_ROTATION_0_M;
+             break;
+     case (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90):
+             rotation = BIT_DPU_LAY_ROTATION_270_M;
+             break;
+     }
+
+     return rotation;
+}
+
+static u32 drm_blend_to_dpu(struct drm_plane_state *state)
+{
+     u32 blend = 0;
+
+     switch (state->pixel_blend_mode) {
+     case DRM_MODE_BLEND_COVERAGE:
+             /* alpha mode select - combo alpha */
+             blend |= BIT_DPU_LAY_COMBO_ALPHA;
+             /* Normal mode */
+             blend |= BIT_DPU_LAY_MODE_BLEND_NORMAL;
+             break;
+     case DRM_MODE_BLEND_PREMULTI:
+             /* alpha mode select - combo alpha */
+             blend |= BIT_DPU_LAY_COMBO_ALPHA;
+             /* Pre-mult mode */
+             blend |= BIT_DPU_LAY_MODE_BLEND_PREMULT;
+             break;
+     case DRM_MODE_BLEND_PIXEL_NONE:
+     default:

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+             /* don't do blending, maybe RGBX */
+             /* alpha mode select - layer alpha */
+             blend |= BIT_DPU_LAY_LAYER_ALPHA;
+             break;
+     }
+
+     return blend;
+}
+
+static void sprd_dpu_layer(struct sprd_dpu *dpu, struct sprd_plane

*plane,

Quoted 15 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +15 -0
+                     struct drm_plane_state *state)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     struct drm_gem_cma_object *cma_obj;
+     const struct drm_format_info *info;
+     struct drm_framebuffer *fb = state->fb;
+     u32 addr, size, offset, pitch, blend, format, rotation;
+     u32 src_x = state->src_x >> 16;
+     u32 src_y = state->src_y >> 16;
+     u32 src_w = state->src_w >> 16;
+     u32 src_h = state->src_h >> 16;
+     u32 dst_x = state->crtc_x;
+     u32 dst_y = state->crtc_y;
+     u32 alpha = state->alpha;
+     int i;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+
+     offset = (dst_x & 0xffff) | (dst_y << 16);
+     size = (src_w & 0xffff) | (src_h << 16);
+
+     for (i = 0; i < fb->format->num_planes; i++) {
+             cma_obj = drm_fb_cma_get_gem_obj(fb, i);
+             addr = cma_obj->paddr + fb->offsets[i];
+
+             if (i == 0)
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR0, addr,

plane->index);

drivers/gpu/drm/sprd/sprd_dpu.c line 401

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+             else if (i == 1)
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR1, addr,

plane->index);

drivers/gpu/drm/sprd/sprd_dpu.c line 403

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+             else
+                     layer_reg_wr(ctx, REG_LAY_BASE_ADDR2, addr,

plane->index);

Quoted 97 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +97 -0
+     }
+
+     info = drm_format_info(fb->format->format);
+     if (fb->format->num_planes == 3) {
+             /* UV pitch is 1/2 of Y pitch */
+             pitch = (fb->pitches[0] / info->cpp[0]) |
+                             (fb->pitches[0] / info->cpp[0] << 15);
+     } else {
+             pitch = fb->pitches[0] / info->cpp[0];
+     }
+
+     layer_reg_wr(ctx, REG_LAY_POS, offset, plane->index);
+     layer_reg_wr(ctx, REG_LAY_SIZE, size, plane->index);
+     layer_reg_wr(ctx, REG_LAY_CROP_START,
+                     src_y << 16 | src_x, plane->index);
+     layer_reg_wr(ctx, REG_LAY_ALPHA, alpha, plane->index);
+     layer_reg_wr(ctx, REG_LAY_PITCH, pitch, plane->index);
+
+     format = drm_format_to_dpu(fb);
+     blend = drm_blend_to_dpu(state);
+     rotation = drm_rotation_to_dpu(state);
+
+     layer_reg_wr(ctx, REG_LAY_CTRL, BIT_DPU_LAY_EN |
+                             format |
+                             blend |
+                             rotation,
+                             plane->index);
+}
+
+static void sprd_dpu_flip(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 reg_val;
+
+     /*
+      * Make sure the dpu is in stop status. DPU has no shadow
+      * registers in EDPI mode. So the config registers can only be
+      * updated in the rising edge of DPU_RUN bit.
+      */
+     if (ctx->if_type == SPRD_DPU_IF_EDPI)
+             dpu_wait_stop_done(dpu);
+
+     /* update trigger and wait */
+     if (ctx->if_type == SPRD_DPU_IF_DPI) {
+             if (!ctx->stopped) {
+                     dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_REG_UPDATE);
+                     dpu_wait_update_done(dpu);
+             }
+
+             dpu_reg_set(ctx, REG_DPU_INT_EN, BIT_DPU_INT_ERR);
+     } else if (ctx->if_type == SPRD_DPU_IF_EDPI) {
+             dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+             ctx->stopped = false;
+     }
+
+     /*
+      * If the following interrupt was disabled in isr,
+      * re-enable it.
+      */
+     reg_val = 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);
+}
+
+static void sprd_dpu_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 size;
+
+     writel(0x00, ctx->base + REG_BG_COLOR);
+
+     size = (ctx->vm.vactive << 16) | ctx->vm.hactive;
+     writel(size, ctx->base + REG_PANEL_SIZE);
+     writel(size, ctx->base + REG_BLEND_SIZE);
+
+     writel(0x00, ctx->base + REG_MMU_EN);
+     writel(0x00, ctx->base + REG_MMU_PPN1);
+     writel(0xffff, ctx->base + REG_MMU_RANGE1);
+     writel(0x00, ctx->base + REG_MMU_PPN2);
+     writel(0xffff, ctx->base + REG_MMU_RANGE2);
+     writel(0x1ffff, ctx->base + REG_MMU_VPN_RANGE);
+}
+
+static void sprd_dpu_fini(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     writel(0x00, ctx->base + REG_DPU_INT_EN);
+     writel(0xff, ctx->base + REG_DPU_INT_CLR);
+}
+
+static void sprd_dpi_init(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     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);

Quoted 21 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +21 -0
+
+             /* set dpi timing */
+             reg_val = ctx->vm.hsync_len << 0 |
+                       ctx->vm.hback_porch << 8 |
+                       ctx->vm.hfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_H_TIMING);
+
+             reg_val = ctx->vm.vsync_len << 0 |
+                       ctx->vm.vback_porch << 8 |
+                       ctx->vm.vfront_porch << 20;
+             writel(reg_val, ctx->base + REG_DPI_V_TIMING);
+
+             if (ctx->vm.vsync_len + ctx->vm.vback_porch < 32)
+                     drm_warn(dpu->drm, "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 */

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+             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);

Quoted 50 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +50 -0
+             /* 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 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(int_mask, ctx->base + REG_DPU_INT_EN);
+}
+
+void sprd_dpu_run(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_RUN);
+
+     ctx->stopped = false;
+}
+
+void sprd_dpu_stop(struct sprd_dpu *dpu)
+{
+     struct dpu_context *ctx = &dpu->ctx;
+
+     if (ctx->if_type == SPRD_DPU_IF_DPI)
+             dpu_reg_set(ctx, REG_DPU_CTRL, BIT_DPU_STOP);
+
+     dpu_wait_stop_done(dpu);
+}
+
+static int sprd_plane_atomic_check(struct drm_plane *plane,
+                               struct drm_plane_state *state)
+{
+     struct drm_framebuffer *fb = state->fb;
+     struct drm_crtc_state *crtc_state;
+     u32 fmt;
+
+     if (!fb || !state->crtc)
+             return 0;
+

drivers/gpu/drm/sprd/sprd_dpu.c line 605

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     fmt = drm_format_to_dpu(fb);
+     if (!fmt)
+             return -EINVAL;
+
+     crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
+     if (IS_ERR(crtc_state))
+             return PTR_ERR(crtc_state);
+
+     return drm_atomic_helper_check_plane_state(state, crtc_state,
+

DRM_PLANE_HELPER_NO_SCALING,

drivers/gpu/drm/sprd/sprd_dpu.c line 602

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+

DRM_PLANE_HELPER_NO_SCALING,

Quoted 12 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +12 -0
+                                               true, true);
+}
+
+static void sprd_plane_atomic_update(struct drm_plane *drm_plane,
+                                 struct drm_plane_state *old_state)
+{
+     struct drm_plane_state *state = drm_plane->state;
+     struct sprd_plane *plane = to_sprd_plane(drm_plane);
+     struct sprd_dpu *dpu = to_sprd_crtc(state->crtc);
+
+     /* start configure dpu layers */
+     sprd_dpu_layer(dpu, plane, state);
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 618

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+}
+
+static void sprd_plane_atomic_disable(struct drm_plane *drm_plane,
+                                  struct drm_plane_state *old_state)

I think this needs a rebase onto the latest DRM code. Many of these callbacks now receive struct drm_atomic_state instead of drm_plane_state. Same for CRTC states.

Thks, i will fix it.

Quoted 42 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +8 -0
+{
+     struct sprd_plane *plane = to_sprd_plane(drm_plane);
+     struct sprd_dpu *dpu = to_sprd_crtc(old_state->crtc);
+
+     layer_reg_wr(&dpu->ctx, REG_LAY_CTRL, 0x00, plane->index);
+}
+
+static void sprd_plane_create_properties(struct sprd_plane *plane, int

index)

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+{
+     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+                                    BIT(DRM_MODE_BLEND_PREMULTI) |
+                                    BIT(DRM_MODE_BLEND_COVERAGE);
+
+     /* create rotation property */

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+     drm_plane_create_rotation_property(&plane->base,
+                                        DRM_MODE_ROTATE_0,
+                                        DRM_MODE_ROTATE_MASK |
+                                        DRM_MODE_REFLECT_MASK);
+
+     /* create alpha property */
+     drm_plane_create_alpha_property(&plane->base);
+
+     /* create blend mode property */
+     drm_plane_create_blend_mode_property(&plane->base,

supported_modes);

Quoted 15 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +15 -0
+
+     /* create zpos property */
+     drm_plane_create_zpos_immutable_property(&plane->base, index);
+}
+
+static const struct drm_plane_helper_funcs sprd_plane_helper_funcs = {
+     .atomic_check = sprd_plane_atomic_check,
+     .atomic_update = sprd_plane_atomic_update,
+     .atomic_disable = sprd_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs sprd_plane_funcs = {
+     .update_plane = drm_atomic_helper_update_plane,
+     .disable_plane  = drm_atomic_helper_disable_plane,
+     .destroy = drm_plane_cleanup,
Quoted 9 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 663

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+     .reset = drm_atomic_helper_plane_reset,
+     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static struct sprd_plane *sprd_plane_init(struct drm_device *drm)

Maybe sprd_planes_init()? It initializes all planes.

Thks, i will fix it.

Quoted 20 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 7 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+{
+     struct sprd_plane *plane, *primary;
+     int i;
+
+     for (i = 0; i < 6; i++) {
+             plane = drmm_universal_plane_alloc(drm, struct sprd_plane,

base,

drivers/gpu/drm/sprd/sprd_dpu.c line 671

Quoted 2 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +2 -0
+                                            1, &sprd_plane_funcs,
+                                            primary_fmts,

ARRAY_SIZE(primary_fmts),

drivers/gpu/drm/sprd/sprd_dpu.c line 672

Quoted 1 line
drivers/gpu/drm/sprd/sprd_dpu.c new file +1 -0
+                                            NULL,

DRM_PLANE_TYPE_PRIMARY, NULL);

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 6 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +6 -0
+             if (IS_ERR(plane)) {
+                     drm_err(drm, "fail to init primary plane\n");
+                     return plane;
+             }
+
+             drm_plane_helper_add(&plane->base,

&sprd_plane_helper_funcs);

Quoted 14 lines

drivers/gpu/drm/sprd/sprd_dpu.c line 687

Quoted 9 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +9 -0
+
+             sprd_plane_create_properties(plane, i);
+
+             plane->index = i;
+             if (i == 0)
+                     primary = plane;
+     }
+
+     return primary;

I find this confusing: there are only primary planes, right? But what's special about the first one? The returned pointer is not used. Maybe just return an errno code or 0 here?

drmm_crtc_alloc_with_planes need a plane as parameter, so the first primary plane to be used for drmm_crtc_alloc_with_planes.

Quoted 421 lines

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 5 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+}
+
+static enum drm_mode_status sprd_crtc_mode_valid(struct drm_crtc *crtc,
+                                     const struct drm_display_mode

*mode)

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +4 -0
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     drm_dbg(dpu->drm, "%s() mode: "DRM_MODE_FMT"\n", __func__,

DRM_MODE_ARG(mode));

Quoted 201 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +201 -0
+
+     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_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     sprd_dpu_init(dpu);
+
+     sprd_dpi_init(dpu);
+
+     enable_irq(dpu->ctx.irq);
+
+     drm_crtc_vblank_on(&dpu->base);
+}
+
+static void sprd_crtc_atomic_disable(struct drm_crtc *crtc,
+                                 struct drm_atomic_state *state)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+     struct drm_device *drm = dpu->base.dev;
+
+     drm_crtc_vblank_off(&dpu->base);
+
+     disable_irq(dpu->ctx.irq);
+
+     sprd_dpu_fini(dpu);
+
+     spin_lock_irq(&drm->event_lock);
+     if (crtc->state->event) {
+             drm_crtc_send_vblank_event(crtc, crtc->state->event);
+             crtc->state->event = NULL;
+     }
+     spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_atomic_check(struct drm_crtc *crtc,
+                              struct drm_atomic_state *state)
+{
+     drm_dbg(crtc->dev, "%s()\n", __func__);
+
+     return 0;
+}
+
+static void sprd_crtc_atomic_flush(struct drm_crtc *crtc,
+                               struct drm_atomic_state *state)
+
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+     struct drm_device *drm = dpu->base.dev;
+
+     sprd_dpu_flip(dpu);
+
+     spin_lock_irq(&drm->event_lock);
+     if (crtc->state->event) {
+             drm_crtc_send_vblank_event(crtc, crtc->state->event);
+             crtc->state->event = NULL;
+     }
+     spin_unlock_irq(&drm->event_lock);
+}
+
+static int sprd_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     dpu_reg_set(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+
+     return 0;
+}
+
+static void sprd_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+     struct sprd_dpu *dpu = to_sprd_crtc(crtc);
+
+     dpu_reg_clr(&dpu->ctx, REG_DPU_INT_EN, BIT_DPU_INT_VSYNC);
+}
+
+static const struct drm_crtc_helper_funcs sprd_crtc_helper_funcs = {
+     .mode_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 struct sprd_dpu *sprd_crtc_init(struct drm_device *drm,
+                      struct drm_plane *primary)
+{
+     struct device_node *port;
+     struct sprd_dpu *dpu;
+
+     /*
+      * 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_err(drm, "find 'ports' phandle of %s failed\n",
+                       drm->dev->of_node->full_name);
+             return ERR_PTR(-EINVAL);
+     }
+     of_node_put(port);
+
+     dpu = drmm_crtc_alloc_with_planes(drm, struct sprd_dpu, base,
+                                     primary, NULL,
+                                     &sprd_crtc_funcs, NULL);
+     if (IS_ERR(dpu)) {
+             drm_err(drm, "failed to init crtc.\n");
+             return dpu;
+     }
+
+     dpu->base.port = port;
+     drm_crtc_helper_add(&dpu->base, &sprd_crtc_helper_funcs);
+
+     return dpu;
+}
+
+static irqreturn_t sprd_dpu_isr(int irq, void *data)
+{
+     struct sprd_dpu *dpu = data;
+     struct dpu_context *ctx = &dpu->ctx;
+     u32 reg_val, int_mask = 0;
+
+     reg_val = readl(ctx->base + REG_DPU_INT_STS);
+
+     /* disable err interrupt */
+     if (reg_val & BIT_DPU_INT_ERR) {
+             int_mask |= BIT_DPU_INT_ERR;
+             drm_warn(dpu->drm, "Warning: dpu underflow!\n");
+     }
+
+     /* dpu update done isr */
+     if (reg_val & BIT_DPU_INT_UPDATE_DONE) {
+             ctx->evt_update = true;
+             wake_up_interruptible_all(&ctx->wait_queue);
+     }
+
+     /* dpu stop done isr */
+     if (reg_val & BIT_DPU_INT_DONE) {
+             ctx->evt_stop = true;
+             wake_up_interruptible_all(&ctx->wait_queue);
+     }
+
+     if (reg_val & BIT_DPU_INT_VSYNC)
+             drm_crtc_handle_vblank(&dpu->base);
+
+     int_mask |= check_mmu_isr(dpu, reg_val);
+
+     writel(reg_val, ctx->base + REG_DPU_INT_CLR);
+     dpu_reg_clr(ctx, REG_DPU_INT_EN, int_mask);
+
+     return IRQ_HANDLED;
+}
+
+static int sprd_dpu_context_init(struct sprd_dpu *dpu,
+                             struct device *dev)
+{
+     struct platform_device *pdev = to_platform_device(dev);
+     struct dpu_context *ctx = &dpu->ctx;
+     struct resource *res;
+     int ret;
+
+     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     ctx->base = devm_ioremap(dev, res->start, resource_size(res));
+     if (!ctx->base) {
+             dev_err(dev, "failed to map dpu registers\n");
+             return -EFAULT;
+     }
+
+     ctx->irq = platform_get_irq(pdev, 0);
+     if (ctx->irq < 0) {
+             dev_err(dev, "failed to get dpu irq\n");
+             return ctx->irq;
+     }
+
+     irq_set_status_flags(ctx->irq, IRQ_NOAUTOEN);
+     ret = devm_request_irq(dev, ctx->irq, sprd_dpu_isr,
+                                     0, "DPU", dpu);
+     if (ret) {

drivers/gpu/drm/sprd/sprd_dpu.c

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +10 -0
+             dev_err(dev, "failed to register dpu irq handler\n");
+             return ret;
+     }
+
+     init_waitqueue_head(&ctx->wait_queue);
+
+     return 0;
+}
+
+static int sprd_dpu_bind(struct device *dev, struct device *master,

void *data)

Review

Quoted 59 lines
drivers/gpu/drm/sprd/sprd_dpu.c new file +58 -0
+{
+     struct drm_device *drm = data;
+     struct sprd_dpu *dpu;
+     struct sprd_plane *plane;
+     int ret;
+
+     plane = sprd_plane_init(drm);
+     if (IS_ERR(plane))
+             return PTR_ERR(plane);
+
+     dpu = sprd_crtc_init(drm, &plane->base);
+     if (IS_ERR(dpu))
+             return PTR_ERR(dpu);
+
+     dpu->drm = drm;
+     dev_set_drvdata(dev, dpu);
+
+     ret = sprd_dpu_context_init(dpu, dev);
+     if (ret)
+             return ret;
+
+     return 0;
+}
+
+static const struct component_ops dpu_component_ops = {
+     .bind = sprd_dpu_bind,
+};
+
+static const struct of_device_id dpu_match_table[] = {
+     { .compatible = "sprd,sharkl3-dpu" },
+     { /* sentinel */ },
+};
+
+static int sprd_dpu_probe(struct platform_device *pdev)
+{
+     return component_add(&pdev->dev, &dpu_component_ops);
+}
+
+static int sprd_dpu_remove(struct platform_device *pdev)
+{
+     component_del(&pdev->dev, &dpu_component_ops);
+
+     return 0;
+}
+
+struct platform_driver sprd_dpu_driver = {
+     .probe = sprd_dpu_probe,
+     .remove = sprd_dpu_remove,
+     .driver = {
+             .name = "sprd-dpu-drv",
+             .of_match_table = dpu_match_table,
+     },
+};
+
+MODULE_AUTHOR("Leon He <[email protected]>");
+MODULE_AUTHOR("Kevin Tang <[email protected]>");
+MODULE_DESCRIPTION("Unisoc Display Controller Driver");
+MODULE_LICENSE("GPL v2");
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_dpu.h

Quoted 35 lines

new file mode 100644
index 000000000..157a78f24

drivers/gpu/drm/sprd/sprd_dpu.h new file +30 -0
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef __SPRD_DPU_H__
+#define __SPRD_DPU_H__
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <uapi/drm/drm_mode.h>
+
+/* DPU Layer registers offset */
+#define DPU_LAY_REG_OFFSET   0x30
+
+enum {
+     SPRD_DPU_IF_DPI,
+     SPRD_DPU_IF_EDPI,
+     SPRD_DPU_IF_LIMIT
+};

drivers/gpu/drm/sprd/sprd_dpu.h line 40

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.h new file +10 -0
+
+/**
+ * Sprd DPU context structure
+ *
+ * @base: DPU controller base address
+ * @irq: IRQ number to install the handler for
+ * @if_type: The type of DPI interface, default is DPI mode.
+ * @vm: videomode structure to use for DPU and DPI initialization
+ * @stopped: indicates whether DPU are stopped
+ * @wait_queue: wait queue, used to wait for DPU shadow register update

done and

Quoted 49 lines
drivers/gpu/drm/sprd/sprd_dpu.h new file +49 -0
+ * DPU stop register done interrupt signal.
+ * @evt_update: wait queue condition for DPU shadow register
+ * @evt_stop: wait queue condition for DPU stop register
+ */
+struct dpu_context {
+     void __iomem *base;
+     int irq;
+     u8 if_type;
+     struct videomode vm;
+     bool stopped;
+     wait_queue_head_t wait_queue;
+     bool evt_update;
+     bool evt_stop;
+};
+
+/**
+ * Sprd DPU device structure
+ *
+ * @crtc: crtc object
+ * @drm: A point to drm device
+ * @ctx: DPU's implementation specific context object
+ */
+struct sprd_dpu {
+     struct drm_crtc base;
+     struct drm_device *drm;
+     struct dpu_context ctx;
+};
+
+static inline struct sprd_dpu *to_sprd_crtc(struct drm_crtc *crtc)
+{
+     return container_of(crtc, struct sprd_dpu, base);
+}
+
+static inline void
+dpu_reg_set(struct dpu_context *ctx, u32 offset, u32 set_bits)
+{
+     u32 bits = readl_relaxed(ctx->base + offset);
+
+     writel(bits | set_bits, ctx->base + offset);
+}
+
+static inline void
+dpu_reg_clr(struct dpu_context *ctx, u32 offset, u32 clr_bits)
+{
+     u32 bits = readl_relaxed(ctx->base + offset);
+
+     writel(bits & ~clr_bits, ctx->base + offset);
+}
+

drivers/gpu/drm/sprd/sprd_dpu.h

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dpu.h new file +10 -0
+static inline u32
+layer_reg_rd(struct dpu_context *ctx, u32 offset, int index)
+{
+     u32 layer_offset = offset + index * DPU_LAY_REG_OFFSET;
+
+     return readl(ctx->base + layer_offset);
+}
+
+static inline void
+layer_reg_wr(struct dpu_context *ctx, u32 offset, u32 cfg_bits, int

index)

Review

Quoted 11 lines
drivers/gpu/drm/sprd/sprd_dpu.h new file +10 -0
+{
+     u32 layer_offset =  offset + index * DPU_LAY_REG_OFFSET;
+
+     writel(cfg_bits, ctx->base + layer_offset);
+}
+
+void sprd_dpu_run(struct sprd_dpu *dpu);
+void sprd_dpu_stop(struct sprd_dpu *dpu);
+
+#endif
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_drm.c

Review

Quoted 12 lines

index a1d3ed655..c626c6caf 100644

drivers/gpu/drm/sprd/sprd_drm.c +1 -0
@@ -191,6 +191,7 @@ static struct platform_driver sprd_drm_driver = {
 
  static struct platform_driver *sprd_drm_drivers[]  = {
      &sprd_drm_driver,
+     &sprd_dpu_driver,
  };
 
  static int __init sprd_drm_init(void)
(quoted diff) +0 -0

b/drivers/gpu/drm/sprd/sprd_drm.h

Quoted 7 lines

index 9781fd591..85d4a8b9f 100644

drivers/gpu/drm/sprd/sprd_drm.h +0 -0
@@ -13,4 +13,6 @@ struct sprd_drm {
      struct drm_device drm;
  };
 
Quoted 14 lines

drivers/gpu/drm/sprd/sprd_drm.h struct sprd_drm { · line 18

Quoted 4 lines
drivers/gpu/drm/sprd/sprd_drm.h +2 -0
+extern struct platform_driver sprd_dpu_driver;
+
  #endif /* _SPRD_DRM_H_ */

--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#24 lore raw

PATCH v4 5/6 dt-bindings: display: add Unisoc's mipi dsi controller bindings

In reply to Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +102 -0
new file mode 100644
index 000000000..d439f688f
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+          Documentation/devicetree/bindings/media/video-interfaces.txt.
+          That port should be the input endpoint, usually coming from
+          the associated DPU.
+      port@1:
+        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 panel.
+
+    required:
+      - "#address-cells"
+      - "#size-cells"
+      - port@0
+      - port@1
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/sprd,sc9860-clk.h>
+    dsi: dsi@63100000 {
+        compatible = "sprd,sharkl3-dsi-host";
+        reg = <0x63100000 0x1000>;
+        interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>,
+          <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
+        clock-names = "clk_src_96m";
+        clocks = <&pll CLK_TWPLL_96M>;
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            port@0 {
+                reg = <0>;
+                dsi_in: endpoint {
+                    remote-endpoint = <&dpu_out>;
+                };
+            };
+            port@1 {
+                reg = <1>;
+                dsi_out: endpoint {
+                    remote-endpoint = <&panel_in>;
+                };
+            };
+        };
+    };
Signature · 6 lines
2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
#25 lore raw

Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

Quoted 65 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +45 -0
new file mode 100644
index 000000000..d439f688f
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 55

Quoted 10 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +10 -0
+          Documentation/devicetree/bindings/media/video-interfaces.txt.
+          That port should be the input endpoint, usually coming from
+          the associated DPU.
+      port@1:
+        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 panel.

The DSI generic binding asks that peripherals that are controlled through a DCS be a subnode of the MIPI-DSI bus, not through a port endpoint.

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#26 lore raw

Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

Hi Maxime,

Quoted 77 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:13写道:
Quoted 5 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

Quoted 8 lines
---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 2 lines

diff --git

a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 4 lines

new file mode 100644
index 000000000..d439f688f
--- /dev/null
+++

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 4

Quoted 5 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +4 -0
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id:

http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#

Quoted 47 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +47 -0
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+          Documentation/devicetree/bindings/media/video-interfaces.txt.
+          That port should be the input endpoint, usually coming from
+          the associated DPU.
+      port@1:
+        type: object
+        description:
Quoted 9 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 55

Quoted 4 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +4 -0
+          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 panel.

The DSI generic binding asks that peripherals that are controlled through a DCS be a subnode of the MIPI-DSI bus, not through a port endpoint.

Our DSI controller don't support DCS now...

Quoted 3 lines

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#27 lore raw

Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

Quoted 90 lines · Kevin Tang

Hi Maxime,

Quoted 77 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:13写道:
Quoted 5 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

Quoted 8 lines
---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 2 lines

diff --git

a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 4 lines

new file mode 100644
index 000000000..d439f688f
--- /dev/null
+++

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 4

Quoted 5 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +4 -0
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id:

http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#

Quoted 47 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +47 -0
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+          Documentation/devicetree/bindings/media/video-interfaces.txt.
+          That port should be the input endpoint, usually coming from
+          the associated DPU.
+      port@1:
+        type: object
+        description:
Quoted 9 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 55

Quoted 4 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +4 -0
+          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 panel.

The DSI generic binding asks that peripherals that are controlled through a DCS be a subnode of the MIPI-DSI bus, not through a port endpoint.

Quoted 1 line

Our DSI controller don't support DCS now...

I'm not sure I follow you, you mentionned in the patch 4 that you were testing for a device to be in command mode, how would that work without DCS support?

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#28 lore raw

Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

Quoted 99 lines · Maxime Ripard <[email protected]> 于2021年4月7日周三 下午6:46写道:
Quoted 16 lines · Kevin Tang

Hi Maxime,

Quoted 12 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:13写道:
Quoted 11 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102

++++++++++++++++++

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines
 1 file changed, 102 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines

diff --git

a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 1 line
Quoted 1 line

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

new file mode 100644
index 000000000..d439f688f
--- /dev/null
+++

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 40 lines
Quoted 40 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 4

Quoted 5 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +4 -0
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id:

http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#

Quoted 34 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +34 -0
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
Quoted 8 lines
Quoted 8 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 56

Quoted 8 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +8 -0
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 1 line
Quoted 1 line

Review line 47

Quoted 1 line
(quoted diff) +1 -0
+          That port should be the input endpoint, usually coming

from

Quoted 6 lines
Quoted 6 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 56

Quoted 6 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +6 -0
+          the associated DPU.
+      port@1:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 7 lines
Quoted 7 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 55

Quoted 2 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +2 -0
+          That port should be the output endpoint, usually output to
+          the associated panel.

The DSI generic binding asks that peripherals that are controlled through a DCS be a subnode of the MIPI-DSI bus, not through a port endpoint.

Quoted 6 lines
Quoted 1 line

Our DSI controller don't support DCS now...

I'm not sure I follow you, you mentionned in the patch 4 that you were testing for a device to be in command mode, how would that work without DCS support?

Sorry, I see DCS as DSC, pls ignore my previous comments.

dsi input node is display controller and dsi output node is panel, I still don't understand what it has to do with dcs? and it seems that other vendors also like this.

can you help provide some cases?

Quoted 3 lines

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#29 lore raw

Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

Quoted 107 lines · Kevin Tang
Quoted 99 lines · Maxime Ripard <[email protected]> 于2021年4月7日周三 下午6:46写道:
Quoted 16 lines · Kevin Tang

Hi Maxime,

Quoted 12 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:13写道:
Quoted 11 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102

++++++++++++++++++

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines
 1 file changed, 102 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines

diff --git

a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 1 line
Quoted 1 line

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

new file mode 100644
index 000000000..d439f688f
--- /dev/null
+++

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 40 lines
Quoted 40 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 4

Quoted 5 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +4 -0
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id:

http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#

Quoted 34 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +34 -0
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
Quoted 8 lines
Quoted 8 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 56

Quoted 8 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +8 -0
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 1 line
Quoted 1 line

Review line 47

Quoted 1 line
(quoted diff) +1 -0
+          That port should be the input endpoint, usually coming

from

Quoted 6 lines
Quoted 6 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 56

Quoted 6 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +6 -0
+          the associated DPU.
+      port@1:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 7 lines
Quoted 7 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 55

Quoted 2 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +2 -0
+          That port should be the output endpoint, usually output to
+          the associated panel.

The DSI generic binding asks that peripherals that are controlled through a DCS be a subnode of the MIPI-DSI bus, not through a port endpoint.

Quoted 6 lines
Quoted 1 line

Our DSI controller don't support DCS now...

I'm not sure I follow you, you mentionned in the patch 4 that you were testing for a device to be in command mode, how would that work without DCS support?

Quoted 7 lines

Sorry, I see DCS as DSC, pls ignore my previous comments.

dsi input node is display controller and dsi output node is panel, I still don't understand what it has to do with dcs? and it seems that other vendors also like this.

can you help provide some cases?

So the device tree is a tree organized through which bus controls which
device: Your DSI controller is accessed through a memory-mapped region and is thus a child node of the main bus (I guess?) and then, since the DSI panel is going to be controlled through the DSI controller and MIPI-DCS, it needs to be a child of the display controller.

This is exactly what is being described here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt#n42

The second port is thus not needed at all

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#30 lore raw

Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

Quoted 115 lines · Maxime Ripard <[email protected]> 于2021年4月15日周四 下午4:42写道:
Quoted 113 lines · Kevin Tang
Quoted 99 lines · Maxime Ripard <[email protected]> 于2021年4月7日周三 下午6:46写道:
Quoted 16 lines · Kevin Tang

Hi Maxime,

Quoted 12 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:13写道:
Quoted 11 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102

++++++++++++++++++

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines
 1 file changed, 102 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines

diff --git

a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 1 line
Quoted 1 line

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

new file mode 100644
index 000000000..d439f688f
--- /dev/null
+++

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 40 lines
Quoted 40 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 4

Quoted 5 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +4 -0
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id:

http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#

Quoted 34 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +34 -0
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
Quoted 8 lines
Quoted 8 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 56

Quoted 8 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +8 -0
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 1 line
Quoted 1 line

Review line 47

Quoted 1 line
(quoted diff) +1 -0
+          That port should be the input endpoint, usually coming

from

Quoted 6 lines
Quoted 6 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 56

Quoted 6 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +6 -0
+          the associated DPU.
+      port@1:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 7 lines
Quoted 7 lines

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml line 55

Quoted 2 lines
Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml new file +2 -0
+          That port should be the output endpoint, usually output to
+          the associated panel.

The DSI generic binding asks that peripherals that are controlled through a DCS be a subnode of the MIPI-DSI bus, not through a port endpoint.

Quoted 6 lines
Quoted 1 line

Our DSI controller don't support DCS now...

I'm not sure I follow you, you mentionned in the patch 4 that you were testing for a device to be in command mode, how would that work without DCS support?

Sorry, I see DCS as DSC, pls ignore my previous comments.

dsi input node is display controller and dsi output node is panel, I still don't understand what it has to do with dcs? and it seems that other vendors also like this.

Quoted 7 lines
Quoted 1 line

can you help provide some cases?

So the device tree is a tree organized through which bus controls which
device: Your DSI controller is accessed through a memory-mapped region and is thus a child node of the main bus (I guess?) and then, since the DSI panel is going to be controlled through the DSI controller and MIPI-DCS, it needs to be a child of the display controller.

Yeah, access DSI controller registers is through AHB bus.

I'm a little confused, DSI panel node should be a child of the display controller?
I found a lot of cases are organized like this, we also do like it.
https://elixir.bootlin.com/linux/v5.11.15/source/arch/arm/boot/dts/tegra114-dalmore.dts#L48

Quoted 5 lines

This is exactly what is being described here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt#n42

The second port is thus not needed at all

Yeah, dsi_out port is no need, should be detele.
Besides, I also have a question, if out dsi-phy is separate driver(eg, dphy have it's own bus)
dsi_out port should be "dphy_in"?

Quoted 2 lines

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#31 lore raw

Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

Hi,

Quoted 123 lines · Kevin Tang
Quoted 115 lines · Maxime Ripard <[email protected]> 于2021年4月15日周四 下午4:42写道:
Quoted 113 lines · Kevin Tang
Quoted 99 lines · Maxime Ripard <[email protected]> 于2021年4月7日周三 下午6:46写道:
Quoted 16 lines · Kevin Tang

Hi Maxime,

Quoted 12 lines · Maxime Ripard <[email protected]> 于2021年3月24日周三 下午7:13写道:
Quoted 11 lines · Kevin Tang

From: Kevin Tang <[email protected]>

Adds MIPI DSI Controller
support for Unisoc's display subsystem.

---
 .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102

++++++++++++++++++

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines
 1 file changed, 102 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines

diff --git

a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 1 line
Quoted 1 line

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

new file mode 100644
index 000000000..d439f688f
--- /dev/null
+++

b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml

Quoted 40 lines
Quoted 40 lines

Review line 4

Quoted 5 lines
(quoted diff) +4 -0
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id:

http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#

Quoted 34 lines
(quoted diff) +34 -0
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc MIPI DSI Controller
+
+maintainers:
+  - Kevin Tang <[email protected]>
+
+properties:
+  compatible:
+    const: sprd,sharkl3-dsi-host
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 2
+
+  clocks:
+    minItems: 1
+
+  clock-names:
+    items:
+      - const: clk_src_96m
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    type: object
+
+    properties:
+      "#address-cells":
+        const: 1
+
Quoted 8 lines
Quoted 8 lines

Review line 46

Quoted 8 lines
(quoted diff) +8 -0
+      "#size-cells":
+        const: 0
+
+      port@0:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 1 line
Quoted 1 line

Review line 47

Quoted 1 line
(quoted diff) +1 -0
+          That port should be the input endpoint, usually coming

from

Quoted 6 lines
Quoted 6 lines

Review line 53

Quoted 6 lines
(quoted diff) +6 -0
+          the associated DPU.
+      port@1:
+        type: object
+        description:
+          A port node with endpoint definitions as defined in
+

Documentation/devicetree/bindings/media/video-interfaces.txt.

Quoted 7 lines
Quoted 7 lines

Review line 55

Quoted 2 lines
(quoted diff) +2 -0
+          That port should be the output endpoint, usually output to
+          the associated panel.

The DSI generic binding asks that peripherals that are controlled through a DCS be a subnode of the MIPI-DSI bus, not through a port endpoint.

Quoted 6 lines
Quoted 1 line

Our DSI controller don't support DCS now...

I'm not sure I follow you, you mentionned in the patch 4 that you were testing for a device to be in command mode, how would that work without DCS support?

Sorry, I see DCS as DSC, pls ignore my previous comments.

dsi input node is display controller and dsi output node is panel, I still don't understand what it has to do with dcs? and it seems that other vendors also like this.

Quoted 7 lines
Quoted 1 line

can you help provide some cases?

So the device tree is a tree organized through which bus controls which
device: Your DSI controller is accessed through a memory-mapped region and is thus a child node of the main bus (I guess?) and then, since the DSI panel is going to be controlled through the DSI controller and MIPI-DCS, it needs to be a child of the display controller.

Quoted 4 lines

Yeah, access DSI controller registers is through AHB bus.

I'm a little confused, DSI panel node should be a child of the display controller?

If they are controlled through DSI, yes.

Quoted 4 lines

I found a lot of cases are organized like this, we also do like it.
https://elixir.bootlin.com/linux/v5.11.15/source/arch/arm/boot/dts/tegra114-dalmore.dts#L48

Quoted 1 line
Quoted 8 lines
Quoted 4 lines

This is exactly what is being described here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/display/mipi-dsi-bus.txt#n42

The second port is thus not needed at all

Yeah, dsi_out port is no need, should be detele.
Besides, I also have a question, if out dsi-phy is separate driver(eg, dphy have it's own bus)
dsi_out port should be "dphy_in"?

You could argue both, but phys are usually represented through the phys property.

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

author
#32 lore raw

PATCH v4 6/6 drm/sprd: add Unisoc's drm mipi dsi&dphy driver

In reply to Kevin Tang

Adds dsi host controller support for the Unisoc's display subsystem. Adds dsi phy support for the Unisoc's display subsystem. Only MIPI DSI Displays supported, DP/TV/HMDI will be support in the feature.

v1:
- Remove dphy and dsi graph binding, merge the dphy driver into the dsi.

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dsi structure init.

v4:
- Use drmm_helpers to allocate encoder.
- Move allocate encoder and connector to bind funtion.

---
 drivers/gpu/drm/sprd/Makefile          |    7 +-
 drivers/gpu/drm/sprd/dw_dsi_ctrl.c     |  794 +++++++++++++
 drivers/gpu/drm/sprd/dw_dsi_ctrl.h     | 1475 ++++++++++++++++++++++++
 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c |  157 +++
 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h |   26 +
 drivers/gpu/drm/sprd/megacores_pll.c   |  317 +++++
 drivers/gpu/drm/sprd/megacores_pll.h   |  146 +++
 drivers/gpu/drm/sprd/sprd_drm.c        |    1 +
 drivers/gpu/drm/sprd/sprd_drm.h        |    1 +
 drivers/gpu/drm/sprd/sprd_dsi.c        | 1132 ++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dsi.h        |  104 ++
 11 files changed, 4158 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl.c
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl.h
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h
 create mode 100644 drivers/gpu/drm/sprd/megacores_pll.c
 create mode 100644 drivers/gpu/drm/sprd/megacores_pll.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
drivers/gpu/drm/sprd/Makefile +5 -2
index 6c25bfa99..d49f4977b 100644
@@ -1,5 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-y := sprd_drm.o \
-	sprd_dpu.o
-
+	sprd_dpu.o \
+	sprd_dsi.o \
+	dw_dsi_ctrl.o \
+	dw_dsi_ctrl_ppi.o \
+	megacores_pll.o
drivers/gpu/drm/sprd/dw_dsi_ctrl.c new file +794 -0

794 lines not on this page · raw message

drivers/gpu/drm/sprd/dw_dsi_ctrl.h new file +1475 -0

1,475 lines not on this page · raw message

drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c new file +157 -0

157 lines not on this page · raw message

drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h new file +26 -0
new file mode 100644
index 000000000..d87fb7579
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DW_DSI_CTRL_PPI_H_
+#define _DW_DSI_CTRL_PPI_H_
+
+#include "sprd_dsi.h"
+
+void dsi_phy_rstz(struct dsi_context *ctx, int level);
+void dsi_phy_shutdownz(struct dsi_context *ctx, int level);
+void dsi_phy_force_pll(struct dsi_context *ctx, int force);
+void dsi_phy_stop_wait_time(struct dsi_context *ctx, u8 byte_clk);
+void dsi_phy_datalane_en(struct dsi_context *ctx);
+void dsi_phy_clklane_en(struct dsi_context *ctx, int en);
+void dsi_phy_clk_hs_rqst(struct dsi_context *ctx, int en);
+u8 dsi_phy_is_pll_locked(struct dsi_context *ctx);
+void dsi_phy_test_clk(struct dsi_context *ctx, u8 level);
+void dsi_phy_test_clr(struct dsi_context *ctx, u8 level);
+void dsi_phy_test_en(struct dsi_context *ctx, u8 level);
+u8 dsi_phy_test_dout(struct dsi_context *ctx);
+void dsi_phy_test_din(struct dsi_context *ctx, u8 data);
+void dsi_phy_bist_en(struct dsi_context *ctx, int en);
+
+#endif /* _DW_DSI_CTRL_PPI_H_ */
\ No newline at end of file
drivers/gpu/drm/sprd/megacores_pll.c new file +317 -0

317 lines not on this page · raw message

drivers/gpu/drm/sprd/megacores_pll.h new file +146 -0

147 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index c626c6caf..1613b5db3 100644
@@ -192,6 +192,7 @@ static struct platform_driver sprd_drm_driver = {
 static struct platform_driver *sprd_drm_drivers[]  = {
 	&sprd_drm_driver,
 	&sprd_dpu_driver,
+	&sprd_dsi_driver,
 };
 
 static int __init sprd_drm_init(void)
drivers/gpu/drm/sprd/sprd_drm.h +1 -0
index 85d4a8b9f..95d1b972f 100644
@@ -14,5 +14,6 @@ struct sprd_drm {
 };
 
 extern struct platform_driver sprd_dpu_driver;
+extern struct platform_driver sprd_dsi_driver;
 
 #endif /* _SPRD_DRM_H_ */
drivers/gpu/drm/sprd/sprd_dsi.c new file +1132 -0

60 lines not on this page · raw message

drivers/gpu/drm/sprd/sprd_dsi.h new file +104 -0

104 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 6 lines
2.29.0

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Part of the diff is not shown here. View the raw message.

#33 lore raw

Re: [PATCH v4 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver

Quoted 8 lines · Kevin Tang

Adds dsi host controller support for the Unisoc's display subsystem. Adds dsi phy support for the Unisoc's display subsystem. Only MIPI DSI Displays supported, DP/TV/HMDI will be support in the feature.

v1:
- Remove dphy and dsi graph binding, merge the dphy driver into the dsi.

Quoted 7 lines

v2:
- Use drm_xxx to replace all DRM_XXX.
- Use kzalloc to replace devm_kzalloc for sprd_dsi structure init.

v4:
- Use drmm_helpers to allocate encoder.
- Move allocate encoder and connector to bind funtion.

You're missing your Signed-off-by

And there's a bunch of errors warnings and (important) checks reported by checkpatch --strict that you should fix.

Quoted 27 lines
---
 drivers/gpu/drm/sprd/Makefile          |    7 +-
 drivers/gpu/drm/sprd/dw_dsi_ctrl.c     |  794 +++++++++++++
 drivers/gpu/drm/sprd/dw_dsi_ctrl.h     | 1475 ++++++++++++++++++++++++
 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c |  157 +++
 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h |   26 +
 drivers/gpu/drm/sprd/megacores_pll.c   |  317 +++++
 drivers/gpu/drm/sprd/megacores_pll.h   |  146 +++
 drivers/gpu/drm/sprd/sprd_drm.c        |    1 +
 drivers/gpu/drm/sprd/sprd_drm.h        |    1 +
 drivers/gpu/drm/sprd/sprd_dsi.c        | 1132 ++++++++++++++++++
 drivers/gpu/drm/sprd/sprd_dsi.h        |  104 ++
 11 files changed, 4158 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl.c
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl.h
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c
 create mode 100644 drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h
 create mode 100644 drivers/gpu/drm/sprd/megacores_pll.c
 create mode 100644 drivers/gpu/drm/sprd/megacores_pll.h
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.c
 create mode 100644 drivers/gpu/drm/sprd/sprd_dsi.h
drivers/gpu/drm/sprd/Makefile +0 -0
index 6c25bfa99..d49f4977b 100644

drivers/gpu/drm/sprd/Makefile line 7

Quoted 9 lines
drivers/gpu/drm/sprd/Makefile +4 -2
@@ -1,5 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-y := sprd_drm.o \
-	sprd_dpu.o
-
+	sprd_dpu.o \
+	sprd_dsi.o \
+	dw_dsi_ctrl.o \
+	dw_dsi_ctrl_ppi.o \

So it's a designware IP? There's a driver for it already that seems fairly similar:
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c

Quoted 3,286 lines
drivers/gpu/drm/sprd/Makefile +1 -0
+	megacores_pll.o
drivers/gpu/drm/sprd/dw_dsi_ctrl.c new file +794 -0
new file mode 100644
index 000000000..1034aba4a
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include "dw_dsi_ctrl.h"
+
+/*
+ * Modify power status of DSI Host core
+ */
+void dsi_power_enable(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(enable, &reg->SOFT_RESET);
+}
+/*
+ * Enable/disable DPI video mode
+ */
+void dsi_video_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(0, &reg->DSI_MODE_CFG);
+}
+/*
+ * Enable command mode (Generic interface)
+ */
+void dsi_cmd_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(1, &reg->DSI_MODE_CFG);
+}
+
+bool dsi_is_cmd_mode(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	return readl(&reg->DSI_MODE_CFG);
+}
+/*
+ * Configure the read back virtual channel for the generic interface
+ */
+void dsi_rx_vcid(struct dsi_context *ctx, u8 vc)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x1C virtual_channel_id;
+
+	virtual_channel_id.val = readl(&reg->VIRTUAL_CHANNEL_ID);
+	virtual_channel_id.bits.gen_rx_vcid = vc;
+
+	writel(virtual_channel_id.val, &reg->VIRTUAL_CHANNEL_ID);
+}
+/*
+ * Write the DPI video virtual channel destination
+ */
+void dsi_video_vcid(struct dsi_context *ctx, u8 vc)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x1C virtual_channel_id;
+
+	virtual_channel_id.val = readl(&reg->VIRTUAL_CHANNEL_ID);
+	virtual_channel_id.bits.video_pkt_vcid = vc;
+
+	writel(virtual_channel_id.val, &reg->VIRTUAL_CHANNEL_ID);
+}
+/*
+ * Set DPI video mode type (burst/non-burst - with sync pulses or events)
+ */
+void dsi_dpi_video_burst_mode(struct dsi_context *ctx, int mode)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.vid_mode_type = mode;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/*
+ * Set DPI video color coding
+ */
+void dsi_dpi_color_coding(struct dsi_context *ctx, int coding)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x20 dpi_video_format;
+
+	dpi_video_format.val = readl(&reg->DPI_VIDEO_FORMAT);
+	dpi_video_format.bits.dpi_video_mode_format = coding;
+
+	writel(dpi_video_format.val, &reg->DPI_VIDEO_FORMAT);
+}
+/*
+ * Configure the Horizontal Line time
+ * param "byte_cycle" taken to transmit the total of the horizontal line
+ */
+void dsi_dpi_hline_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x2C video_line_time;
+
+	video_line_time.val = readl(&reg->VIDEO_LINE_TIME);
+	video_line_time.bits.video_line_time = byte_cycle;
+
+	writel(video_line_time.val, &reg->VIDEO_LINE_TIME);
+}
+/*
+ * Configure the Horizontal back porch time
+ * param "byte_cycle" taken to transmit the horizontal back porch
+ */
+void dsi_dpi_hbp_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x28 video_line_hblk_time;
+
+	video_line_hblk_time.val = readl(&reg->VIDEO_LINE_HBLK_TIME);
+	video_line_hblk_time.bits.video_line_hbp_time = byte_cycle;
+
+	writel(video_line_hblk_time.val, &reg->VIDEO_LINE_HBLK_TIME);
+}
+/*
+ * Configure the Horizontal sync time,
+ * param "byte_cycle" taken to transmit the horizontal sync
+ */
+void dsi_dpi_hsync_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x28 video_line_hblk_time;
+
+	video_line_hblk_time.val = readl(&reg->VIDEO_LINE_HBLK_TIME);
+	video_line_hblk_time.bits.video_line_hsa_time = byte_cycle;
+
+	writel(video_line_hblk_time.val, &reg->VIDEO_LINE_HBLK_TIME);
+}
+/*
+ * Configure the vertical active lines of the video stream
+ */
+void dsi_dpi_vact(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x34 video_active_lines;
+
+	video_active_lines.val = readl(&reg->VIDEO_VACTIVE_LINES);
+	video_active_lines.bits.vactive_lines = lines;
+
+	writel(video_active_lines.val, &reg->VIDEO_VACTIVE_LINES);
+}
+
+void dsi_dpi_vfp(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vfp_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+
+void dsi_dpi_vbp(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vbp_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+
+void dsi_dpi_vsync(struct dsi_context *ctx, u16 lines)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x30 video_vblk_lines;
+
+	video_vblk_lines.val = readl(&reg->VIDEO_VBLK_LINES);
+	video_vblk_lines.bits.vsa_lines = lines;
+
+	writel(video_vblk_lines.val, &reg->VIDEO_VBLK_LINES);
+}
+
+void dsi_dpi_hporch_lp_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+
+	vid_mode_cfg.bits.lp_hfp_en = enable;
+	vid_mode_cfg.bits.lp_hbp_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/*
+ * Enable return to low power mode inside vertical active lines periods when
+ * timing allows
+ */
+void dsi_dpi_vporch_lp_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+
+	vid_mode_cfg.bits.lp_vact_en = enable;
+	vid_mode_cfg.bits.lp_vfp_en = enable;
+	vid_mode_cfg.bits.lp_vbp_en = enable;
+	vid_mode_cfg.bits.lp_vsa_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/*
+ * Enable FRAME BTA ACK
+ */
+void dsi_dpi_frame_ack_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.frame_bta_ack_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+/*
+ * Write no of chunks to core - taken into consideration only when multi packet
+ * is enabled
+ */
+void dsi_dpi_chunk_num(struct dsi_context *ctx, u16 num)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x24 video_pkt_config;
+
+	video_pkt_config.val = readl(&reg->VIDEO_PKT_CONFIG);
+	video_pkt_config.bits.video_line_chunk_num = num;
+
+	writel(video_pkt_config.val, &reg->VIDEO_PKT_CONFIG);
+}
+/*
+ * Write the null packet size - will only be taken into account when null
+ * packets are enabled.
+ */
+void dsi_dpi_null_packet_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xC0 video_nullpkt_size;
+
+	video_nullpkt_size.val = readl(&reg->VIDEO_NULLPKT_SIZE);
+	video_nullpkt_size.bits.video_nullpkt_size = size;
+
+	writel(video_nullpkt_size.val, &reg->VIDEO_NULLPKT_SIZE);
+}
+/*
+ * Write video packet size. obligatory for sending video
+ */
+void dsi_dpi_video_packet_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x24 video_pkt_config;
+
+	video_pkt_config.val = readl(&reg->VIDEO_PKT_CONFIG);
+	video_pkt_config.bits.video_pkt_size = size;
+
+	writel(video_pkt_config.val, &reg->VIDEO_PKT_CONFIG);
+}
+/*
+ * Specifiy the size of the packet memory write start/continue
+ */
+void dsi_edpi_max_pkt_size(struct dsi_context *ctx, u16 size)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xC4 dcs_wm_pkt_size;
+
+	dcs_wm_pkt_size.val = readl(&reg->DCS_WM_PKT_SIZE);
+	dcs_wm_pkt_size.bits.dcs_wm_pkt_size = size;
+
+	writel(dcs_wm_pkt_size.val, &reg->DCS_WM_PKT_SIZE);
+}
+/*
+ * Enable tear effect acknowledge
+ */
+void dsi_tear_effect_ack_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x68 cmd_mode_cfg;
+
+	cmd_mode_cfg.val = readl(&reg->CMD_MODE_CFG);
+	cmd_mode_cfg.bits.tear_fx_en = enable;
+
+	writel(cmd_mode_cfg.val, &reg->CMD_MODE_CFG);
+}
+/*
+ * Set DCS command packet transmission to transmission type
+ */
+void dsi_cmd_mode_lp_cmd_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x68 cmd_mode_cfg;
+
+	cmd_mode_cfg.val = readl(&reg->CMD_MODE_CFG);
+
+	cmd_mode_cfg.bits.gen_sw_0p_tx = enable;
+	cmd_mode_cfg.bits.gen_sw_1p_tx = enable;
+	cmd_mode_cfg.bits.gen_sw_2p_tx = enable;
+	cmd_mode_cfg.bits.gen_lw_tx = enable;
+	cmd_mode_cfg.bits.dcs_sw_0p_tx = enable;
+	cmd_mode_cfg.bits.dcs_sw_1p_tx = enable;
+	cmd_mode_cfg.bits.dcs_lw_tx = enable;
+	cmd_mode_cfg.bits.max_rd_pkt_size = enable;
+
+	cmd_mode_cfg.bits.gen_sr_0p_tx = enable;
+	cmd_mode_cfg.bits.gen_sr_1p_tx = enable;
+	cmd_mode_cfg.bits.gen_sr_2p_tx = enable;
+	cmd_mode_cfg.bits.dcs_sr_0p_tx = enable;
+
+	writel(cmd_mode_cfg.val, &reg->CMD_MODE_CFG);
+}
+/*
+ * Set DCS read command packet transmission to transmission type
+ */
+void dsi_video_mode_lp_cmd_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x38 vid_mode_cfg;
+
+	vid_mode_cfg.val = readl(&reg->VID_MODE_CFG);
+	vid_mode_cfg.bits.lp_cmd_en = enable;
+
+	writel(vid_mode_cfg.val, &reg->VID_MODE_CFG);
+}
+
+/*
+ * Write command header in the generic interface (which also sends DCS commands) as a subset
+ */
+void dsi_set_packet_header(struct dsi_context *ctx,
+				   u8 vc,
+				   u8 type,
+				   u8 wc_lsb,
+				   u8 wc_msb)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x6C gen_hdr;
+
+	gen_hdr.bits.gen_dt = type;
+	gen_hdr.bits.gen_vc = vc;
+	gen_hdr.bits.gen_wc_lsbyte = wc_lsb;
+	gen_hdr.bits.gen_wc_msbyte = wc_msb;
+
+	writel(gen_hdr.val, &reg->GEN_HDR);
+}
+/*
+ * Write the payload of the long packet commands
+ */
+void dsi_set_packet_payload(struct dsi_context *ctx, u32 payload)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(payload, &reg->GEN_PLD_DATA);
+}
+/*
+ * Read the payload of the long packet commands
+ */
+u32 dsi_get_rx_payload(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	return readl(&reg->GEN_PLD_DATA);
+}
+
+/*
+ * Enable Bus Turn-around request
+ */
+void dsi_bta_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(enable, &reg->TA_EN);
+}
+/*
+ * Enable EOTp reception
+ */
+void dsi_eotp_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xBC eotp_en;
+
+	eotp_en.val = readl(&reg->EOTP_EN);
+	eotp_en.bits.rx_eotp_en = enable;
+
+	writel(eotp_en.val, &reg->EOTP_EN);
+}
+/*
+ * Enable EOTp transmission
+ */
+void dsi_eotp_tx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xBC eotp_en;
+
+	eotp_en.val = readl(&reg->EOTP_EN);
+	eotp_en.bits.tx_eotp_en = enable;
+
+	writel(eotp_en.val, &reg->EOTP_EN);
+}
+/*
+ * Enable ECC reception, error correction and reporting
+ */
+void dsi_ecc_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xB4 rx_pkt_check_config;
+
+	rx_pkt_check_config.val = readl(&reg->RX_PKT_CHECK_CONFIG);
+	rx_pkt_check_config.bits.rx_pkt_ecc_en = enable;
+
+	writel(rx_pkt_check_config.val, &reg->RX_PKT_CHECK_CONFIG);
+}
+/*
+ * Enable CRC reception, error reporting
+ */
+void dsi_crc_rx_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xB4 rx_pkt_check_config;
+
+	rx_pkt_check_config.val = readl(&reg->RX_PKT_CHECK_CONFIG);
+	rx_pkt_check_config.bits.rx_pkt_crc_en = enable;
+
+	writel(rx_pkt_check_config.val, &reg->RX_PKT_CHECK_CONFIG);
+}
+/*
+ * Get status of read command
+ */
+bool dsi_is_bta_returned(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdcmd_done;
+}
+/*
+ * Get the FULL status of generic read payload fifo
+ */
+bool dsi_is_rx_payload_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdata_fifo_full;
+}
+/*
+ * Get the EMPTY status of generic read payload fifo
+ */
+bool dsi_is_rx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_rdata_fifo_empty;
+}
+/*
+ * Get the FULL status of generic write payload fifo
+ */
+bool dsi_is_tx_payload_fifo_full(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_wdata_fifo_full;
+}
+/*
+ * Get the EMPTY status of generic write payload fifo
+ */
+bool dsi_is_tx_payload_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_wdata_fifo_empty;
+}
+/*
+ * Get the EMPTY status of generic command fifo
+ */
+bool dsi_is_tx_cmd_fifo_empty(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x98 cmd_mode_status;
+
+	cmd_mode_status.val = readl(&reg->CMD_MODE_STATUS);
+
+	return cmd_mode_status.bits.gen_cmd_cmd_fifo_empty;
+}
+/*
+ * DPI interface signal delay config
+ * param byte_cycle period for waiting after controller receiving HSYNC from
+ * DPI interface to start read pixel data from memory.
+ */
+void dsi_dpi_sig_delay(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xD0 video_sig_delay_config;
+
+	video_sig_delay_config.val = readl(&reg->VIDEO_SIG_DELAY_CONFIG);
+	video_sig_delay_config.bits.video_sig_delay = byte_cycle;
+
+	writel(video_sig_delay_config.val, &reg->VIDEO_SIG_DELAY_CONFIG);
+}
+/*
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch data lane from high speed to low power
+ */
+void dsi_datalane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xAC phy_datalane_time_config;
+
+	phy_datalane_time_config.val = readl(&reg->PHY_DATALANE_TIME_CONFIG);
+	phy_datalane_time_config.bits.phy_datalane_hs_to_lp_time = byte_cycle;
+
+	writel(phy_datalane_time_config.val, &reg->PHY_DATALANE_TIME_CONFIG);
+}
+/*
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch the data lane from to low power high speed
+ */
+void dsi_datalane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xAC phy_datalane_time_config;
+
+	phy_datalane_time_config.val = readl(&reg->PHY_DATALANE_TIME_CONFIG);
+	phy_datalane_time_config.bits.phy_datalane_lp_to_hs_time = byte_cycle;
+
+	writel(phy_datalane_time_config.val, &reg->PHY_DATALANE_TIME_CONFIG);
+}
+/*
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch clock lane from high speed to low power
+ */
+void dsi_clklane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xA8 phy_clklane_time_config;
+
+	phy_clklane_time_config.val = readl(&reg->PHY_CLKLANE_TIME_CONFIG);
+	phy_clklane_time_config.bits.phy_clklane_hs_to_lp_time = byte_cycle;
+
+	writel(phy_clklane_time_config.val, &reg->PHY_CLKLANE_TIME_CONFIG);
+}
+/*
+ * Configure how many cycles of byte clock would the PHY module take
+ * to switch clock lane from to low power high speed
+ */
+void dsi_clklane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xA8 phy_clklane_time_config;
+
+	phy_clklane_time_config.val = readl(&reg->PHY_CLKLANE_TIME_CONFIG);
+	phy_clklane_time_config.bits.phy_clklane_lp_to_hs_time = byte_cycle;
+
+	writel(phy_clklane_time_config.val, &reg->PHY_CLKLANE_TIME_CONFIG);
+}
+/*
+ * Configure how many cycles of byte clock would the PHY module take
+ * to turn the bus around to start receiving
+ */
+void dsi_max_read_time(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->MAX_READ_TIME);
+}
+/*
+ * Enable the automatic mechanism to stop providing clock in the clock
+ * lane when time allows
+ */
+void dsi_nc_clk_en(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x74 phy_clk_lane_lp_ctrl;
+
+	phy_clk_lane_lp_ctrl.val = readl(&reg->PHY_CLK_LANE_LP_CTRL);
+	phy_clk_lane_lp_ctrl.bits.auto_clklane_ctrl_en = enable;
+
+	writel(phy_clk_lane_lp_ctrl.val, &reg->PHY_CLK_LANE_LP_CTRL);
+}
+/*
+ * Write transmission escape timeout
+ * a safe guard so that the state machine would reset if transmission
+ * takes too long
+ */
+void dsi_tx_escape_division(struct dsi_context *ctx, u8 div)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(div, &reg->TX_ESC_CLK_CONFIG);
+}
+/* 
+ * Configure timeout divisions (so they would have more clock ticks)
+ * div no of hs cycles before transiting back to LP in
+ *  (lane_clk / div)
+ */
+void dsi_timeout_clock_division(struct dsi_context *ctx, u8 div)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(div, &reg->TIMEOUT_CNT_CLK_CONFIG);
+}
+/*
+ * Configure the Low power receive time out
+ */
+void dsi_lp_rx_timeout(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->LRX_H_TO_CONFIG);
+}
+/*
+ * Configure a high speed transmission time out
+ */
+void dsi_hs_tx_timeout(struct dsi_context *ctx, u16 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->HTX_TO_CONFIG);
+}
+/*
+ * Get the error 0 interrupt register status
+ */
+u32 dsi_int0_status(struct dsi_context *ctx)
+{
+	struct sprd_dsi *dsi = container_of(ctx, struct sprd_dsi, ctx);
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x08 protocol_int_sts;
+
+	protocol_int_sts.val = readl(&reg->PROTOCOL_INT_STS);
+	writel(protocol_int_sts.val, &reg->PROTOCOL_INT_CLR);
+
+	if (protocol_int_sts.bits.dphy_errors_0)
+		drm_err(dsi->drm, "dphy_err: escape entry error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_1)
+		drm_err(dsi->drm, "dphy_err: lp data transmission sync error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_2)
+		drm_err(dsi->drm, "dphy_err: control error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_3)
+		drm_err(dsi->drm, "dphy_err: LP0 contention error\n");
+
+	if (protocol_int_sts.bits.dphy_errors_4)
+		drm_err(dsi->drm, "dphy_err: LP1 contention error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_0)
+		drm_err(dsi->drm, "ack_err: SoT error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_1)
+		drm_err(dsi->drm, "ack_err: SoT Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_2)
+		drm_err(dsi->drm, "ack_err: EoT Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_3)
+		drm_err(dsi->drm, "ack_err: Escape Mode Entry Command error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_4)
+		drm_err(dsi->drm, "ack_err: LP Transmit Sync error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_5)
+		drm_err(dsi->drm, "ack_err: Peripheral Timeout error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_6)
+		drm_err(dsi->drm, "ack_err: False Control error\n");
+
+	if (protocol_int_sts.bits.ack_with_err_7)
+		drm_err(dsi->drm, "ack_err: reserved (specific to device)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_8)
+		drm_err(dsi->drm, "ack_err: ECC error, single-bit (corrected)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_9)
+		drm_err(dsi->drm, "ack_err: ECC error, multi-bit (not corrected)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_10)
+		drm_err(dsi->drm, "ack_err: checksum error (long packet only)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_11)
+		drm_err(dsi->drm, "ack_err: not recognized DSI data type\n");
+
+	if (protocol_int_sts.bits.ack_with_err_12)
+		drm_err(dsi->drm, "ack_err: DSI VC ID Invalid\n");
+
+	if (protocol_int_sts.bits.ack_with_err_13)
+		drm_err(dsi->drm, "ack_err: invalid transmission length\n");
+
+	if (protocol_int_sts.bits.ack_with_err_14)
+		drm_err(dsi->drm, "ack_err: reserved (specific to device)\n");
+
+	if (protocol_int_sts.bits.ack_with_err_15)
+		drm_err(dsi->drm, "ack_err: DSI protocol violation\n");
+
+	return 0;
+}
+/*
+ * Get the error 1 interrupt register status
+ */
+u32 dsi_int1_status(struct dsi_context *ctx)
+{
+	struct sprd_dsi *dsi = container_of(ctx, struct sprd_dsi, ctx);
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x10 internal_int_sts;
+	u32 status = 0;
+
+	internal_int_sts.val = readl(&reg->INTERNAL_INT_STS);
+	writel(internal_int_sts.val, &reg->INTERNAL_INT_CLR);
+
+	if (internal_int_sts.bits.receive_pkt_size_err)
+		drm_err(dsi->drm, "receive packet size error\n");
+
+	if (internal_int_sts.bits.eotp_not_receive_err)
+		drm_err(dsi->drm, "EoTp packet is not received\n");
+
+	if (internal_int_sts.bits.gen_cmd_cmd_fifo_wr_err)
+		drm_err(dsi->drm, "cmd header-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_rdata_fifo_rd_err)
+		drm_err(dsi->drm, "cmd read-payload-fifo is empty\n");
+
+	if (internal_int_sts.bits.gen_cmd_rdata_fifo_wr_err)
+		drm_err(dsi->drm, "cmd read-payload-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_wdata_fifo_wr_err)
+		drm_err(dsi->drm, "cmd write-payload-fifo is full\n");
+
+	if (internal_int_sts.bits.gen_cmd_wdata_fifo_rd_err)
+		drm_err(dsi->drm, "cmd write-payload-fifo is empty\n");
+
+	if (internal_int_sts.bits.dpi_pix_fifo_wr_err) {
+		drm_err(dsi->drm, "DPI pixel-fifo is full\n");
+		status |= DSI_INT_STS_NEED_SOFT_RESET;
+	}
+
+	if (internal_int_sts.bits.ecc_single_err)
+		drm_err(dsi->drm, "ECC single error in a received packet\n");
+
+	if (internal_int_sts.bits.ecc_multi_err)
+		drm_err(dsi->drm, "ECC multiple error in a received packet\n");
+
+	if (internal_int_sts.bits.crc_err)
+		drm_err(dsi->drm, "CRC error in the received packet payload\n");
+
+	if (internal_int_sts.bits.hs_tx_timeout)
+		drm_err(dsi->drm, "high-speed transmission timeout\n");
+
+	if (internal_int_sts.bits.lp_rx_timeout)
+		drm_err(dsi->drm, "low-power reception timeout\n");
+
+	return status;
+}
+/*
+ * Configure MASK (hiding) of interrupts coming from error 0 source
+ */
+void dsi_int0_mask(struct dsi_context *ctx, u32 mask)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(mask, &reg->MASK_PROTOCOL_INT);
+}
+/*
+ * Configure MASK (hiding) of interrupts coming from error 1 source
+ */
+void dsi_int1_mask(struct dsi_context *ctx, u32 mask)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(mask, &reg->MASK_INTERNAL_INT);
+}
drivers/gpu/drm/sprd/dw_dsi_ctrl.h new file +1475 -0
new file mode 100644
index 000000000..ffba62130
@@ -0,0 +1,1475 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DW_DSI_CTRL_H_
+#define _DW_DSI_CTRL_H_
+
+#include <asm/types.h>
+
+#include "sprd_dsi.h"
+
+struct dsi_reg {
+	union _0x00 {
+		u32 val;
+		struct _DSI_VERSION {
+		u32 dsi_version: 16;
+		u32 reserved: 16;
+		} bits;
+	} DSI_VERSION;
+
+	union _0x04 {
+		u32 val;
+		struct _SOFT_RESET {
+		/*
+		 * This bit configures the core either to work normal or to
+		 * reset. It's default value is 0. After the core configur-
+		 * ation, to enable the mipi_dsi_host, set this register to 1.
+		 * 1: power up     0: reset core
+		 */
+		u32 dsi_soft_reset: 1;
+
+		u32 reserved: 31;
+		} bits;
+	} SOFT_RESET;
+
+	union _0x08 {
+		u32 val;
+		struct _PROTOCOL_INT_STS {
+		/* ErrEsc escape entry error from Lane 0 */
+		u32 dphy_errors_0: 1;
+
+		/* ErrSyncEsc low-power data transmission synchronization
+		 * error from Lane 0
+		 */
+		u32 dphy_errors_1: 1;
+
+		/* ErrControl error from Lane 0 */
+		u32 dphy_errors_2: 1;
+
+		/* ErrContentionLP0 LP0 contention error from Lane 0 */
+		u32 dphy_errors_3: 1;
+
+		/* ErrContentionLP1 LP1 contention error from Lane 0 */
+		u32 dphy_errors_4: 1;
+
+		/* debug mode protocol errors */
+		u32 protocol_debug_err: 11;
+
+		/* SoT error from the Acknowledge error report */
+		u32 ack_with_err_0: 1;
+
+		/* SoT Sync error from the Acknowledge error report */
+		u32 ack_with_err_1: 1;
+
+		/* EoT Sync error from the Acknowledge error report */
+		u32 ack_with_err_2: 1;
+
+		/* Escape Mode Entry Command error from the Acknowledge
+		 * error report
+		 */
+		u32 ack_with_err_3: 1;
+
+		/* LP Transmit Sync error from the Acknowledge error report */
+		u32 ack_with_err_4: 1;
+
+		/* Peripheral Timeout error from the Acknowledge error report */
+		u32 ack_with_err_5: 1;
+
+		/* False Control error from the Acknowledge error report */
+		u32 ack_with_err_6: 1;
+
+		/* reserved (specific to device) from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_7: 1;
+
+		/* ECC error, single-bit (detected and corrected) from the
+		 * Acknowledge error report
+		 */
+		u32 ack_with_err_8: 1;
+
+		/* ECC error, multi-bit (detected, not corrected) from the
+		 * Acknowledge error report
+		 */
+		u32 ack_with_err_9: 1;
+
+		/* checksum error (long packet only) from the Acknowledge
+		 * error report
+		 */
+		u32 ack_with_err_10: 1;
+
+		/* not recognized DSI data type from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_11: 1;
+
+		/* DSI VC ID Invalid from the Acknowledge error report */
+		u32 ack_with_err_12: 1;
+
+		/* invalid transmission length from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_13: 1;
+
+		/* reserved (specific to device) from the Acknowledge error
+		 * report
+		 */
+		u32 ack_with_err_14: 1;
+
+		/* DSI protocol violation from the Acknowledge error report */
+		u32 ack_with_err_15: 1;
+
+		} bits;
+	} PROTOCOL_INT_STS;
+
+	union _0x0C {
+		u32 val;
+		struct _MASK_PROTOCOL_INT {
+		u32 mask_dphy_errors_0: 1;
+		u32 mask_dphy_errors_1: 1;
+		u32 mask_dphy_errors_2: 1;
+		u32 mask_dphy_errors_3: 1;
+		u32 mask_dphy_errors_4: 1;
+		u32 mask_protocol_debug_err: 11;
+		u32 mask_ack_with_err_0: 1;
+		u32 mask_ack_with_err_1: 1;
+		u32 mask_ack_with_err_2: 1;
+		u32 mask_ack_with_err_3: 1;
+		u32 mask_ack_with_err_4: 1;
+		u32 mask_ack_with_err_5: 1;
+		u32 mask_ack_with_err_6: 1;
+		u32 mask_ack_with_err_7: 1;
+		u32 mask_ack_with_err_8: 1;
+		u32 mask_ack_with_err_9: 1;
+		u32 mask_ack_with_err_10: 1;
+		u32 mask_ack_with_err_11: 1;
+		u32 mask_ack_with_err_12: 1;
+		u32 mask_ack_with_err_13: 1;
+		u32 mask_ack_with_err_14: 1;
+		u32 mask_ack_with_err_15: 1;
+		} bits;
+	} MASK_PROTOCOL_INT;
+
+	union _0x10 {
+		u32 val;
+		struct _INTERNAL_INT_STS {
+		/* This bit indicates that the packet size error is detected
+		 * during the packet reception.
+		 */
+		u32 receive_pkt_size_err: 1;
+
+		/* This bit indicates that the EoTp packet is not received at
+		 * the end of the incoming peripheral transmission
+		 */
+		u32 eotp_not_receive_err: 1;
+
+		/* This bit indicates that the system tried to write a command
+		 * through the Generic interface and the FIFO is full. There-
+		 * fore, the command is not written.
+		 */
+		u32 gen_cmd_cmd_fifo_wr_err: 1;
+
+		/* This bit indicates that during a DCS read data, the payload
+		 * FIFO becomes	empty and the data sent to the interface is
+		 * corrupted.
+		 */
+		u32 gen_cmd_rdata_fifo_rd_err: 1;
+
+		/* This bit indicates that during a generic interface packet
+		 * read back, the payload FIFO becomes full and the received
+		 * data is corrupted.
+		 */
+		u32 gen_cmd_rdata_fifo_wr_err: 1;
+
+		/* This bit indicates that the system tried to write a payload
+		 * data through the Generic interface and the FIFO is full.
+		 * Therefore, the payload is not written.
+		 */
+		u32 gen_cmd_wdata_fifo_wr_err: 1;
+
+		/* This bit indicates that during a Generic interface packet
+		 * build, the payload FIFO becomes empty and corrupt data is
+		 * sent.
+		 */
+		u32 gen_cmd_wdata_fifo_rd_err: 1;
+
+		/* This bit indicates that during a DPI pixel line storage,
+		 * the payload FIFO becomes full and the data stored is
+		 * corrupted.
+		 */
+		u32 dpi_pix_fifo_wr_err: 1;
+
+		/* internal debug error	*/
+		u32 internal_debug_err: 19;
+
+		/* This bit indicates that the ECC single error is detected
+		 * and corrected in a received packet.
+		 */
+		u32 ecc_single_err: 1;
+
+		/* This bit indicates that the ECC multiple error is detected
+		 * in a received packet.
+		 */
+		u32 ecc_multi_err: 1;
+
+		/* This bit indicates that the CRC error is detected in the
+		 * received packet payload.
+		 */
+		u32 crc_err: 1;
+
+		/* This bit indicates that the high-speed transmission timeout
+		 * counter reached the end and contention is detected.
+		 */
+		u32 hs_tx_timeout: 1;
+
+		/* This bit indicates that the low-power reception timeout
+		 * counter reached the end and contention is detected.
+		 */
+		u32 lp_rx_timeout: 1;
+
+		} bits;
+	} INTERNAL_INT_STS;
+
+	union _0x14 {
+		u32 val;
+		struct _MASK_INTERNAL_INT {
+		u32 mask_receive_pkt_size_err: 1;
+		u32 mask_eopt_not_receive_err: 1;
+		u32 mask_gen_cmd_cmd_fifo_wr_err: 1;
+		u32 mask_gen_cmd_rdata_fifo_rd_err: 1;
+		u32 mask_gen_cmd_rdata_fifo_wr_err: 1;
+		u32 mask_gen_cmd_wdata_fifo_wr_err: 1;
+		u32 mask_gen_cmd_wdata_fifo_rd_err: 1;
+		u32 mask_dpi_pix_fifo_wr_err: 1;
+		u32 mask_internal_debug_err: 19;
+		u32 mask_ecc_single_err: 1;
+		u32 mask_ecc_multi_err: 1;
+		u32 mask_crc_err: 1;
+		u32 mask_hs_tx_timeout: 1;
+		u32 mask_lp_rx_timeout: 1;
+		} bits;
+	} MASK_INTERNAL_INT;
+
+	union _0x18 {
+		u32 val;
+		struct _DSI_MODE_CFG {
+		/* This bit configures the operation mode
+		 * 0: Video mode ;   1: Command mode
+		 */
+		u32 cmd_video_mode: 1;
+
+		u32 reserved: 31;
+
+		} bits;
+	} DSI_MODE_CFG;
+
+	union _0x1C {
+		u32 val;
+		struct _VIRTUAL_CHANNEL_ID {
+		/* This field indicates the Generic interface read-back
+		 * virtual channel identification
+		 */
+		u32 gen_rx_vcid: 2;
+
+		/* This field configures the DPI virtual channel id that
+		 * is indexed to the VIDEO mode packets
+		 */
+		u32 video_pkt_vcid: 2;
+
+		u32 reserved: 28;
+
+		} bits;
+	} VIRTUAL_CHANNEL_ID;
+
+	union _0x20 {
+		u32 val;
+		struct _DPI_VIDEO_FORMAT {
+		/*
+		 * This field configures the DPI color coding as follows:
+		 * 0000: 16-bit configuration 1
+		 * 0001: 16-bit configuration 2
+		 * 0010: 16-bit configuration 3
+		 * 0011: 18-bit configuration 1
+		 * 0100: 18-bit configuration 2
+		 * 0101: 24-bit
+		 * 0110: 20-bit YCbCr 4:2:2 loosely packed
+		 * 0111: 24-bit YCbCr 4:2:2
+		 * 1000: 16-bit YCbCr 4:2:2
+		 * 1001: 30-bit
+		 * 1010: 36-bit
+		 * 1011: 12-bit YCbCr 4:2:0
+		 * 1100: Compression Display Stream
+		 * 1101-1111: 12-bit YCbCr 4:2:0
+		 */
+		u32 dpi_video_mode_format: 6;
+
+		/* When set to 1, this bit activates loosely packed
+		 * variant to 18-bit configurations
+		 */
+		u32 loosely18_en: 1;
+
+		u32 reserved: 25;
+
+		} bits;
+	} DPI_VIDEO_FORMAT;
+
+	union _0x24 {
+		u32 val;
+		struct _VIDEO_PKT_CONFIG {
+		/*
+		 * This field configures the number of pixels in a single
+		 * video packet. For 18-bit not loosely packed data types,
+		 * this number must be a multiple of 4. For YCbCr data
+		 * types, it must be a multiple of 2, as described in the
+		 * DSI specification.
+		 */
+		u32 video_pkt_size: 16;
+
+		/*
+		 * This register configures the number of chunks to be
+		 * transmitted during a Line period (a chunk consists of
+		 * a video packet and a null packet). If set to 0 or 1,
+		 * the video line is transmitted in a single packet. If
+		 * set to 1, the packet is part of a chunk, so a null packet
+		 * follows it if vid_null_size > 0. Otherwise, multiple chunks
+		 * are used to transmit each video line.
+		 */
+		u32 video_line_chunk_num: 16;
+
+		} bits;
+	} VIDEO_PKT_CONFIG;
+
+	union _0x28 {
+		u32 val;
+		struct _VIDEO_LINE_HBLK_TIME {
+		/* This field configures the Horizontal Back Porch period
+		 * in lane byte clock cycles
+		 */
+		u32 video_line_hbp_time: 16;
+
+		/* This field configures the Horizontal Synchronism Active
+		 * period in lane byte clock cycles
+		 */
+		u32 video_line_hsa_time: 16;
+
+		} bits;
+	} VIDEO_LINE_HBLK_TIME;
+
+	union _0x2C {
+		u32 val;
+		struct _VIDEO_LINE_TIME {
+		/* This field configures the size of the total line time
+		 * (HSA+HBP+HACT+HFP) counted in lane byte clock cycles
+		 */
+		u32 video_line_time: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} VIDEO_LINE_TIME;
+
+	union _0x30 {
+		u32 val;
+		struct _VIDEO_VBLK_LINES {
+		/* This field configures the Vertical Front Porch period
+		 * measured in number of horizontal lines
+		 */
+		u32 vfp_lines: 10;
+
+		/* This field configures the Vertical Back Porch period
+		 * measured in number of horizontal lines
+		 */
+		u32 vbp_lines: 10;
+
+		/* This field configures the Vertical Synchronism Active
+		 * period measured in number of horizontal lines
+		 */
+		u32 vsa_lines: 10;
+
+		u32 reserved: 2;
+
+		} bits;
+	} VIDEO_VBLK_LINES;
+
+	union _0x34 {
+		u32 val;
+		struct _VIDEO_VACTIVE_LINES {
+		/* This field configures the Vertical Active period measured
+		 * in number of horizontal lines
+		 */
+		u32 vactive_lines: 14;
+
+		u32 reserved: 18;
+
+		} bits;
+	} VIDEO_VACTIVE_LINES;
+
+	union _0x38 {
+		u32 val;
+		struct _VID_MODE_CFG {
+		/*
+		 * This field indicates the video mode transmission type as
+		 * follows:
+		 * 00: Non-burst with sync pulses
+		 * 01: Non-burst with sync events
+		 * 10 and 11: Burst mode
+		 */
+		u32 vid_mode_type: 2;
+
+		u32 reserved_0: 6;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VSA period when timing allows.
+		 */
+		u32 lp_vsa_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VBP period when timing allows.
+		 */
+		u32 lp_vbp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VFP period when timing allows.
+		 */
+		u32 lp_vfp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the VACT period when timing allows.
+		 */
+		u32 lp_vact_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the HBP period when timing allows.
+		 */
+		u32 lp_hbp_en: 1;
+
+		/* When set to 1, this bit enables the return to low-power
+		 * inside the HFP period when timing allows.
+		 */
+		u32 lp_hfp_en: 1;
+
+		/* When set to 1, this bit enables the request for an ack-
+		 * nowledge response at the end of a frame.
+		 */
+		u32 frame_bta_ack_en: 1;
+
+		/* When set to 1, this bit enables the command transmission
+		 * only in low-power mode.
+		 */
+		u32 lp_cmd_en: 1;
+
+		u32 reserved_1: 16;
+
+		} bits;
+	} VID_MODE_CFG;
+
+	union _0x3C {
+		u32 val;
+		struct _SDF_MODE_CONFIG {
+		/*
+		 * This field defines the 3D mode on/off & display orientation:
+		 * 00: 3D mode off (2D mode on)
+		 * 01: 3D mode on, portrait orientation
+		 * 10: 3D mode on, landscape orientation
+		 * 11: Reserved
+		 */
+		u32 rf_3d_mode: 2;
+
+		/*
+		 * This field defines the 3D image format:
+		 * 00: Line (alternating lines of left and right data)
+		 * 01: Frame (alternating frames of left and right data)
+		 * 10: Pixel (alternating pixels of left and right data)
+		 * 11: Reserved
+		 */
+		u32 rf_3d_format: 2;
+
+		/*
+		 * This field defines whether there is a second VSYNC pulse
+		 * between Left and Right Images, when 3D Image Format is
+		 * Frame-based:
+		 * 0: No sync pulses between left and right data
+		 * 1: Sync pulse (HSYNC, VSYNC, blanking) between left and
+		 *    right data
+		 */
+		u32 second_vsync_en: 1;
+
+		/*
+		 * This bit defines the left or right order:
+		 * 0: Left eye data is sent first, and then the right eye data
+		 *    is sent.
+		 * 1: Right eye data is sent first, and then the left eye data
+		 *    is sent.
+		 */
+		u32 left_right_order: 1;
+
+		u32 reserved_0: 2;
+
+		/*
+		 * When set, causes the next VSS packet to include 3D control
+		 * payload in every VSS packet.
+		 */
+		u32 rf_3d_payload_en: 1;
+
+		u32 reserved_1: 23;
+
+		} bits;
+	} SDF_MODE_CONFIG;
+
+	union _0x40 {
+		u32 val;
+		struct _TIMEOUT_CNT_CLK_CONFIG {
+		/*
+		 * This field indicates the division factor for the Time Out
+		 * clock used as the timing unit in the configuration of HS to
+		 * LP and LP to HS transition error.
+		 */
+		u32 timeout_cnt_clk_config: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} TIMEOUT_CNT_CLK_CONFIG;
+
+	union _0x44 {
+		u32 val;
+		struct _HTX_TO_CONFIG {
+		/*
+		 * This field configures the timeout counter that triggers
+		 * a high speed transmission timeout contention detection
+		 * (measured in TO_CLK_DIVISION cycles).
+		 *
+		 * If using the non-burst mode and there is no sufficient
+		 * time to switch from HS to LP and back in the period which
+		 * is from one line data finishing to the next line sync
+		 * start, the DSI link returns the LP state once per frame,
+		 * then you should configure the TO_CLK_DIVISION and
+		 * hstx_to_cnt to be in accordance with:
+		 * hstx_to_cnt * lanebyteclkperiod * TO_CLK_DIVISION >= the
+		 * time of one FRAME data transmission * (1 + 10%)
+		 *
+		 * In burst mode, RGB pixel packets are time-compressed,
+		 * leaving more time during a scan line. Therefore, if in
+		 * burst mode and there is sufficient time to switch from HS
+		 * to LP and back in the period of time from one line data
+		 * finishing to the next line sync start, the DSI link can
+		 * return LP mode and back in this time interval to save power.
+		 * For this, configure the TO_CLK_DIVISION and hstx_to_cnt
+		 * to be in accordance with:
+		 * hstx_to_cnt * lanebyteclkperiod * TO_CLK_DIVISION >= the
+		 * time of one LINE data transmission * (1 + 10%)
+		 */
+		u32 htx_to_cnt_limit: 32;
+		} bits;
+	} HTX_TO_CONFIG;
+
+	union _0x48 {
+		u32 val;
+		struct _LRX_H_TO_CONFIG {
+		/*
+		 * This field configures the timeout counter that triggers
+		 * a low-power reception timeout contention detection (measured
+		 * in TO_CLK_DIVISION cycles).
+		 */
+		u32 lrx_h_to_cnt_limit: 32;
+		} bits;
+	} LRX_H_TO_CONFIG;
+
+	union _0x4C {
+		u32 val;
+		struct _RD_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a low-power read oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 lprd_presp_to_cnt_limit: 16;
+
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a high-speed read oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 hsrd_presp_to_cnt_limit: 16;
+
+		} bits;
+	} RD_PRESP_TO_CONFIG;
+
+	union _0x50 {
+		u32 val;
+		struct _HSWR_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link inactive after sending a high-speed write
+		 * operation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 hswr_presp_to_cnt_limit: 16;
+
+		u32 reserved_0: 8;
+
+		/*
+		 * When set to 1, this bit ensures that the peripheral response
+		 * timeout caused by hs_wr_to_cnt is used only once per eDPI
+		 * frame, when both the following conditions are met:
+		 * dpivsync_edpiwms has risen and fallen.
+		 * Packets originated from eDPI have been transmitted and its
+		 * FIFO is empty again In this scenario no non-eDPI requests
+		 * are sent to the D-PHY, even if there is traffic from generic
+		 * or DBI ready to be sent, making it return to stop state.
+		 * When it does so, PRESP_TO counter is activated and only when
+		 * it finishes does the controller send any other traffic that
+		 * is ready.
+		 */
+		u32 hswr_presp_to_mode: 1;
+
+		u32 reserved_1: 7;
+
+		} bits;
+	} HSWR_PRESP_TO_CONFIG;
+
+	union _0x54 {
+		u32 val;
+		struct _LPWR_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after sending a low-power write oper-
+		 * ation. This period is measured in cycles of lanebyteclk.
+		 * The counting starts when the D-PHY enters the Stop state
+		 * and causes no interrupts.
+		 */
+		u32 lpwr_presp_to_cnt_limit: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} LPWR_PRESP_TO_CONFIG;
+
+	union _0x58 {
+		u32 val;
+		struct _BTA_PRESP_TO_CONFIG {
+		/*
+		 * This field sets a period for which the DWC_mipi_dsi_host
+		 * keeps the link still, after completing a Bus Turn-Around.
+		 * This period is measured in cycles of lanebyteclk. The
+		 * counting starts when the D-PHY enters the Stop state and
+		 * causes no interrupts.
+		 */
+		u32 bta_presp_to_cnt_limit: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} BTA_PRESP_TO_CONFIG;
+
+	union _0x5C {
+		u32 val;
+		struct _TX_ESC_CLK_CONFIG {
+		/*
+		 * This field indicates the division factor for the TX Escape
+		 * clock source (lanebyteclk). The values 0 and 1 stop the
+		 * TX_ESC clock generation.
+		 */
+		u32 tx_esc_clk_config: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} TX_ESC_CLK_CONFIG;
+
+	union _0x60 {
+		u32 val;
+		struct _VACT_CMD_TRANS_LIMIT {
+		/*
+		 * This field is used for the transmission of commands in
+		 * low-power mode. It defines the size, in bytes, of the
+		 * largest packet that can fit in a line during the VACT
+		 * region.
+		 */
+		u32 vact_cmd_trans_limit: 8;
+
+		u32 reserved: 24;
+
+		} bits;
+	} VACT_CMD_TRANS_LIMIT;
+
+	union _0x64 {
+		u32 val;
+		struct _VBLK_CMD_TRANS_LIMIT {
+		/*
+		 * This field is used for the transmission of commands in
+		 * low-power mode. It defines the size, in bytes, of the
+		 * largest packet that can fit in a line during the VSA, VBP,
+		 * and VFP regions.
+		 */
+		u32 vblk_cmd_trans_limit: 8;
+
+		u32 reserved: 24;
+
+		} bits;
+	} VBLK_CMD_TRANS_LIMIT;
+
+	union _0x68 {
+		u32 val;
+		struct _CMD_MODE_CFG {
+		/*
+		 * When set to 1, this bit enables the tearing effect
+		 * acknowledge request.
+		 */
+		u32 tear_fx_en: 1;
+
+		/*
+		 * When set to 1, this bit enables the acknowledge request
+		 * after each packet transmission.
+		 */
+		u32 ack_rqst_en: 1;
+
+		u32 reserved_0: 3;
+
+		u32 pps_tx: 1;
+		u32 exq_tx: 1;
+		u32 cmc_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * zero parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_0p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * one parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_1p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short write packet with
+		 * two parameters command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sw_2p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * zero parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_0p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * one parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_1p_tx: 1;
+
+		/*
+		 * This bit configures the Generic short read packet with
+		 * two parameters command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_sr_2p_tx: 1;
+
+		/*
+		 * This bit configures the Generic long write packet command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 gen_lw_tx: 1;
+
+		u32 reserved_1: 1;
+
+		/*
+		 * This bit configures the DCS short write packet with zero
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sw_0p_tx: 1;
+
+		/*
+		 * This bit configures the DCS short write packet with one
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sw_1p_tx: 1;
+
+		/*
+		 * This bit configures the DCS short read packet with zero
+		 * parameter command transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_sr_0p_tx: 1;
+
+		/*
+		 * This bit configures the DCS long write packet command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 dcs_lw_tx: 1;
+
+		u32 reserved_2: 4;
+
+		/*
+		 * This bit configures the maximum read packet size command
+		 * transmission type:
+		 * 0: High-speed 1: Low-power
+		 */
+		u32 max_rd_pkt_size: 1;
+
+		u32 reserved_3: 7;
+
+		} bits;
+	} CMD_MODE_CFG;
+
+	union _0x6C {
+		u32 val;
+		struct _GEN_HDR {
+		/*
+		 * This field configures the packet data type of the header
+		 * packet.
+		 */
+		u32 gen_dt: 6;
+
+		/*
+		 * This field configures the virtual channel id of the header
+		 * packet.
+		 */
+		u32 gen_vc: 2;
+
+		/*
+		 * This field configures the least significant byte of the
+		 * header packet's Word count for long packets or data 0 for
+		 * short packets.
+		 */
+		u32 gen_wc_lsbyte: 8;
+
+		/*
+		 * This field configures the most significant byte of the
+		 * header packet's word count for long packets or data 1 for
+		 * short packets.
+		 */
+		u32 gen_wc_msbyte: 8;
+
+		u32 reserved: 8;
+
+		} bits;
+	} GEN_HDR;
+
+	union _0x70 {
+		u32 val;
+		struct _GEN_PLD_DATA {
+		/* This field indicates byte 1 of the packet payload. */
+		u32 gen_pld_b1: 8;
+
+		/* This field indicates byte 2 of the packet payload. */
+		u32 gen_pld_b2: 8;
+
+		/* This field indicates byte 3 of the packet payload. */
+		u32 gen_pld_b3: 8;
+
+		/* This field indicates byte 4 of the packet payload. */
+		u32 gen_pld_b4: 8;
+
+		} bits;
+	} GEN_PLD_DATA;
+
+	union _0x74 {
+		u32 val;
+		struct _PHY_CLK_LANE_LP_CTRL {
+		/* This bit controls the D-PHY PPI txrequestclkhs signal */
+		u32 phy_clklane_tx_req_hs: 1;
+
+		/* This bit enables the automatic mechanism to stop providing
+		 * clock in the clock lane when time allows.
+		 */
+		u32 auto_clklane_ctrl_en: 1;
+
+		u32 reserved: 30;
+		} bits;
+	} PHY_CLK_LANE_LP_CTRL;
+
+	union _0x78 {
+		u32 val;
+		struct _PHY_INTERFACE_CTRL {
+		/* When set to 0, this bit places the D-PHY macro in power-
+		 * down state.
+		 */
+		u32 rf_phy_shutdown: 1;
+
+		/* When set to 0, this bit places the digital section of the
+		 * D-PHY in the reset state.
+		 */
+		u32 rf_phy_reset_n: 1;
+
+		/* When set to 1, this bit enables the D-PHY Clock Lane
+		 * module.
+		 */
+		u32 rf_phy_clk_en: 1;
+
+		/* When the D-PHY is in ULPS, this bit enables the D-PHY PLL. */
+		u32 rf_phy_force_pll: 1;
+
+		/* ULPS mode Request on clock lane */
+		u32 rf_phy_clk_txrequlps: 1;
+
+		/* ULPS mode Exit on clock lane */
+		u32 rf_phy_clk_txexitulps: 1;
+
+		/* ULPS mode Request on all active data lanes */
+		u32 rf_phy_data_txrequlps: 1;
+
+		/* ULPS mode Exit on all active data lanes */
+		u32 rf_phy_data_txexitulps: 1;
+
+		u32 reserved: 24;
+		} bits;
+	} PHY_INTERFACE_CTRL;
+
+	union _0x7C {
+		u32 val;
+		struct _PHY_TX_TRIGGERS {
+		/* This field controls the trigger transmissions. */
+		u32 phy_tx_triggers: 4;
+
+		u32 reserved: 28;
+		} bits;
+	} PHY_TX_TRIGGERS;
+
+	union _0x80 {
+		u32 val;
+		struct _DESKEW_START {
+		u32 deskew_start: 1;
+		u32 reserved: 31;
+		} bits;
+	} DESKEW_START;
+
+	union _0x84 {
+		u32 val;
+		struct _DESKEW_MODE {
+		u32 deskew_mode: 2;
+		u32 reserved: 30;
+		} bits;
+	} DESKEW_MODE;
+
+	union _0x88 {
+		u32 val;
+		struct _DESKEW_TIME {
+		u32 deskew_time: 32;
+		} bits;
+	} DESKEW_TIME;
+
+	union _0x8C {
+		u32 val;
+		struct _DESKEW_PERIOD {
+		u32 deskew_period: 32;
+		} bits;
+	} DESKEW_PERIOD;
+
+	union _0x90 {
+		u32 val;
+		struct _DESKEW_BUSY {
+		u32 deskew_busy: 1;
+		u32 reserved: 31;
+		} bits;
+	} DESKEW_BUSY;
+
+	union _0x94 {
+		u32 val;
+		struct _DESKEW_LANE_MASK {
+		u32 deskew_lane0_mask: 1;
+		u32 deskew_lane1_mask: 1;
+		u32 deskew_lane2_mask: 1;
+		u32 deskew_lane3_mask: 1;
+		u32 reserved: 28;
+		} bits;
+	} DESKEW_LANE_MASK;
+
+	union _0x98 {
+		u32 val;
+		struct _CMD_MODE_STATUS {
+		/*
+		 * This bit is set when a read command is issued and cleared
+		 * when the entire response is stored in the FIFO.
+		 * Value after reset: 0x0
+		 *
+		 * NOTE:
+		 * For mipi-dsi-r1p0 IP, this bit is set immediately when
+		 *     the read cmd is set to the GEN_HDR register.
+		 *
+		 * For dsi-ctrl-r1p0 IP, this bit is set only after the read
+		 *     cmd was actually sent out from the controller.
+		 */
+		u32 gen_cmd_rdcmd_ongoing: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic read
+		 * payload FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_rdata_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic read
+		 * payload FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_rdata_fifo_full: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic write
+		 * payload FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_wdata_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic write
+		 * payload FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_wdata_fifo_full: 1;
+
+		/*
+		 * This bit indicates the empty status of the generic
+		 * command FIFO.
+		 * Value after reset: 0x1
+		 */
+		u32 gen_cmd_cmd_fifo_empty: 1;
+
+		/*
+		 * This bit indicates the full status of the generic
+		 * command FIFO.
+		 * Value after reset: 0x0
+		 */
+		u32 gen_cmd_cmd_fifo_full: 1;
+
+		/*
+		 * This bit is set when the entire response of read is
+		 * stored in the rx payload FIFO. And it will be cleared
+		 * automaticlly after read this bit each time.
+		 * Value after reset: 0x0
+		 *
+		 * NOTE: this bit is just supported for dsi-ctrl-r1p0 IP
+		 */
+		u32 gen_cmd_rdcmd_done: 1;
+
+		u32 reserved : 24;
+
+		} bits;
+	} CMD_MODE_STATUS;
+
+	union _0x9C {
+		u32 val;
+		struct _PHY_STATUS {
+		/* the status of phydirection D-PHY signal */
+		u32 phy_direction: 1;
+
+		/* the status of phylock D-PHY signal */
+		u32 phy_lock: 1;
+
+		/* the status of rxulpsesc0lane D-PHY signal */
+		u32 phy_rxulpsesc0lane: 1;
+
+		/* the status of phystopstateclklane D-PHY signal */
+		u32 phy_stopstateclklane: 1;
+
+		/* the status of phystopstate0lane D-PHY signal */
+		u32 phy_stopstate0lane: 1;
+
+		/* the status of phystopstate1lane D-PHY signal */
+		u32 phy_stopstate1lane: 1;
+
+		/* the status of phystopstate2lane D-PHY signal */
+		u32 phy_stopstate2lane: 1;
+
+		/* the status of phystopstate3lane D-PHY signal */
+		u32 phy_stopstate3lane: 1;
+
+		/* the status of phyulpsactivenotclk D-PHY signal */
+		u32 phy_ulpsactivenotclk: 1;
+
+		/* the status of ulpsactivenot0lane D-PHY signal */
+		u32 phy_ulpsactivenot0lane: 1;
+
+		/* the status of ulpsactivenot1lane D-PHY signal */
+		u32 phy_ulpsactivenot1lane: 1;
+
+		/* the status of ulpsactivenot2lane D-PHY signal */
+		u32 phy_ulpsactivenot2lane: 1;
+
+		/* the status of ulpsactivenot3lane D-PHY signal */
+		u32 phy_ulpsactivenot3lane: 1;
+
+		u32 reserved: 19;
+
+		} bits;
+	} PHY_STATUS;
+
+	union _0xA0 {
+		u32 val;
+		struct _PHY_MIN_STOP_TIME {
+		/* This field configures the minimum wait period to request
+		 * a high-speed transmission after the Stop state.
+		 */
+		u32 phy_min_stop_time: 8;
+
+		u32 reserved: 24;
+		} bits;
+	} PHY_MIN_STOP_TIME;
+
+	union _0xA4 {
+		u32 val;
+		struct _PHY_LANE_NUM_CONFIG {
+		/*
+		 * This field configures the number of active data lanes:
+		 * 00: One data lane (lane 0)
+		 * 01: Two data lanes (lanes 0 and 1)
+		 * 10: Three data lanes (lanes 0, 1, and 2)
+		 * 11: Four data lanes (lanes 0, 1, 2, and 3)
+		 */
+		u32 phy_lane_num: 2;
+
+		u32 reserved: 30;
+
+		} bits;
+	} PHY_LANE_NUM_CONFIG;
+
+	union _0xA8 {
+		u32 val;
+		struct _PHY_CLKLANE_TIME_CONFIG {
+		/*
+		 * This field configures the maximum time that the D-PHY
+		 * clock lane takes to go from low-power to high-speed
+		 * transmission measured in lane byte clock cycles.
+		 */
+		u32 phy_clklane_lp_to_hs_time: 16;
+
+		/*
+		 * This field configures the maximum time that the D-PHY
+		 * clock lane takes to go from high-speed to low-power
+		 * transmission measured in lane byte clock cycles.
+		 */
+		u32 phy_clklane_hs_to_lp_time: 16;
+
+		} bits;
+	} PHY_CLKLANE_TIME_CONFIG;
+
+	union _0xAC {
+		u32 val;
+		struct _PHY_DATALANE_TIME_CONFIG {
+		/*
+		 * This field configures the maximum time that the D-PHY data
+		 * lanes take to go from low-power to high-speed transmission
+		 * measured in lane byte clock cycles.
+		 */
+		u32 phy_datalane_lp_to_hs_time: 16;
+
+		/*
+		 * This field configures the maximum time that the D-PHY data
+		 * lanes take to go from high-speed to low-power transmission
+		 * measured in lane byte clock cycles.
+		 */
+		u32 phy_datalane_hs_to_lp_time: 16;
+
+		} bits;
+	} PHY_DATALANE_TIME_CONFIG;
+
+	union _0xB0 {
+		u32 val;
+		struct _MAX_READ_TIME {
+		/*
+		 * This field configures the maximum time required to perform
+		 * a read command in lane byte clock cycles. This register can
+		 * only be modified when no read command is in progress.
+		 */
+		u32 max_rd_time: 16;
+
+		u32 reserved: 16;
+
+		} bits;
+	} MAX_READ_TIME;
+
+	union _0xB4 {
+		u32 val;
+		struct _RX_PKT_CHECK_CONFIG {
+		/* When set to 1, this bit enables the ECC reception, error
+		 * correction, and reporting.
+		 */
+		u32 rx_pkt_ecc_en: 1;
+
+		/* When set to 1, this bit enables the CRC reception and error
+		 * reporting.
+		 */
+		u32 rx_pkt_crc_en: 1;
+
+		u32 reserved: 30;
+
+		} bits;
+	} RX_PKT_CHECK_CONFIG;
+
+	union _0xB8 {
+		u32 val;
+		struct _TA_EN {
+		/* When set to 1, this bit enables the Bus Turn-Around (BTA)
+		 * request.
+		 */
+		u32 ta_en: 1;
+
+		u32 reserved: 31;
+
+		} bits;
+	} TA_EN;
+
+	union _0xBC {
+		u32 val;
+		struct _EOTP_EN {
+		/* When set to 1, this bit enables the EoTp transmission */
+		u32 tx_eotp_en: 1;
+
+		/* When set to 1, this bit enables the EoTp reception. */
+		u32 rx_eotp_en: 1;
+
+		u32 reserved: 30;
+
+		} bits;
+	} EOTP_EN;
+
+	union _0xC0 {
+		u32 val;
+		struct _VIDEO_NULLPKT_SIZE {
+		/*
+		 * This register configures the number of bytes inside a null
+		 * packet. Setting it to 0 disables the null packets.
+		 */
+		u32 video_nullpkt_size: 13;
+
+		u32 reserved: 19;
+
+		} bits;
+	} VIDEO_NULLPKT_SIZE;
+
+	union _0xC4 {
+		u32 val;
+		struct _DCS_WM_PKT_SIZE {
+		/*
+		 * This field configures the maximum allowed size for an eDPI
+		 * write memory command, measured in pixels. Automatic parti-
+		 * tioning of data obtained from eDPI is permanently enabled.
+		 */
+		u32 dcs_wm_pkt_size: 16;
+
+		u32 reserved: 16;
+		} bits;
+	} DCS_WM_PKT_SIZE;
+
+	union _0xC8 {
+		u32 val;
+		struct _PROTOCOL_INT_CLR {
+		u32 clr_dphy_errors_0: 1;
+		u32 clr_dphy_errors_1: 1;
+		u32 clr_dphy_errors_2: 1;
+		u32 clr_dphy_errors_3: 1;
+		u32 clr_dphy_errors_4: 1;
+		u32 clr_protocol_debug_err: 11;
+		u32 clr_ack_with_err_0: 1;
+		u32 clr_ack_with_err_1: 1;
+		u32 clr_ack_with_err_2: 1;
+		u32 clr_ack_with_err_3: 1;
+		u32 clr_ack_with_err_4: 1;
+		u32 clr_ack_with_err_5: 1;
+		u32 clr_ack_with_err_6: 1;
+		u32 clr_ack_with_err_7: 1;
+		u32 clr_ack_with_err_8: 1;
+		u32 clr_ack_with_err_9: 1;
+		u32 clr_ack_with_err_10: 1;
+		u32 clr_ack_with_err_11: 1;
+		u32 clr_ack_with_err_12: 1;
+		u32 clr_ack_with_err_13: 1;
+		u32 clr_ack_with_err_14: 1;
+		u32 clr_ack_with_err_15: 1;
+		} bits;
+	} PROTOCOL_INT_CLR;
+
+	union _0xCC {
+		u32 val;
+		struct _INTERNAL_INT_CLR {
+		u32 clr_receive_pkt_size_err: 1;
+		u32 clr_eopt_not_receive_err: 1;
+		u32 clr_gen_cmd_cmd_fifo_wr_err: 1;
+		u32 clr_gen_cmd_rdata_fifo_rd_err: 1;
+		u32 clr_gen_cmd_rdata_fifo_wr_err: 1;
+		u32 clr_gen_cmd_wdata_fifo_wr_err: 1;
+		u32 clr_gen_cmd_wdata_fifo_rd_err: 1;
+		u32 clr_dpi_pix_fifo_wr_err: 1;
+		u32 clr_internal_debug_err: 19;
+		u32 clr_ecc_single_err: 1;
+		u32 clr_ecc_multi_err: 1;
+		u32 clr_crc_err: 1;
+		u32 clr_hs_tx_timeout: 1;
+		u32 clr_lp_rx_timeout: 1;
+		} bits;
+	} INTERNAL_INT_CLR;
+
+	union _0xD0 {
+		u32 val;
+		struct _VIDEO_SIG_DELAY_CONFIG {
+
+		/*
+		 * DPI interface signal delay to be used in clk lanebyte
+		 * domain for control logic to read video data from pixel
+		 * memory in mannal mode, measured in clk_lanebyte cycles
+		 */
+		u32 video_sig_delay: 24;
+
+		/*
+		 * 1'b1: mannal mode
+		 *       dsi controller will use video_sig_delay value as
+		 *       the delay for the packet handle logic to read video
+		 *       data from pixel memory.
+		 *
+		 * 1'b0: auto mode
+		 *       dsi controller will auto calculate the delay for
+		 *       the packet handle logic to read video data from
+		 *       pixel memory.
+		 */
+		u32 video_sig_delay_mode: 1;
+
+		u32 reserved: 7;
+		} bits;
+	} VIDEO_SIG_DELAY_CONFIG;
+
+	u32 reservedD4_EC[7];
+
+	union _0xF0 {
+		u32 val;
+		struct _PHY_TST_CTRL0 {
+		/* PHY test interface clear (active high) */
+		u32 phy_testclr: 1;
+
+		/* This bit is used to clock the TESTDIN bus into the D-PHY */
+		u32 phy_testclk: 1;
+
+		u32 reserved: 30;
+		} bits;
+	} PHY_TST_CTRL0;
+
+	union _0xF4 {
+		u32 val;
+		struct _PHY_TST_CTRL1 {
+		/* PHY test interface input 8-bit data bus for internal
+		 * register programming and test functionalities access.
+		 */
+		u32 phy_testdin: 8;
+
+		/* PHY output 8-bit data bus for read-back and internal
+		 * probing functionalities.
+		 */
+		u32 phy_testdout: 8;
+
+		/*
+		 * PHY test interface operation selector:
+		 * 1: The address write operation is set on the falling edge
+		 *    of the testclk signal.
+		 * 0: The data write operation is set on the rising edge of
+		 *    the testclk signal.
+		 */
+		u32 phy_testen: 1;
+
+		u32 reserved: 15;
+		} bits;
+	} PHY_TST_CTRL1;
+
+	u32 reservedF8_1FC[66];
+
+	union _0x200 {
+		u32 val;
+		struct _INT_PLL_STS {
+		u32 int_pll_sts: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_STS;
+
+	union _0x204 {
+		u32 val;
+		struct _INT_PLL_MSK {
+		u32 int_pll_msk: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_MSK;
+
+	union _0x208 {
+		u32 val;
+		struct _INT_PLL_CLR {
+		u32 int_pll_clr: 1;
+		u32 reserved: 31;
+		} bits;
+	} INT_PLL_CLR;
+
+};
+
+void dsi_power_enable(struct dsi_context *ctx, int enable);
+void dsi_video_mode(struct dsi_context *ctx);
+void dsi_cmd_mode(struct dsi_context *ctx);
+bool dsi_is_cmd_mode(struct dsi_context *ctx);
+void dsi_rx_vcid(struct dsi_context *ctx, u8 vc);
+void dsi_video_vcid(struct dsi_context *ctx, u8 vc);
+void dsi_dpi_video_burst_mode(struct dsi_context *ctx, int mode);
+void dsi_dpi_color_coding(struct dsi_context *ctx, int coding);
+void dsi_dpi_sig_delay(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hline_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hsync_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_hbp_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_dpi_vact(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vfp(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vbp(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_vsync(struct dsi_context *ctx, u16 lines);
+void dsi_dpi_hporch_lp_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_vporch_lp_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_frame_ack_en(struct dsi_context *ctx, int enable);
+void dsi_dpi_chunk_num(struct dsi_context *ctx, u16 no);
+void dsi_dpi_null_packet_size(struct dsi_context *ctx, u16 size);
+void dsi_dpi_video_packet_size(struct dsi_context *ctx, u16 size);
+void dsi_edpi_max_pkt_size(struct dsi_context *ctx, u16 size);
+void dsi_tear_effect_ack_en(struct dsi_context *ctx, int enable);
+void dsi_cmd_mode_lp_cmd_en(struct dsi_context *ctx, int enable);
+void dsi_video_mode_lp_cmd_en(struct dsi_context *ctx, int enable);
+void dsi_set_packet_header(struct dsi_context *ctx, u8 vc, u8 type,
+						u8 wc_lsb, u8 wc_msb);
+void dsi_set_packet_payload(struct dsi_context *ctx, u32 payload);
+u32 dsi_get_rx_payload(struct dsi_context *ctx);
+void dsi_bta_en(struct dsi_context *ctx, int enable);
+void dsi_eotp_rx_en(struct dsi_context *ctx, int enable);
+void dsi_eotp_tx_en(struct dsi_context *ctx, int enable);
+void dsi_ecc_rx_en(struct dsi_context *ctx, int enable);
+void dsi_crc_rx_en(struct dsi_context *ctx, int enable);
+bool dsi_is_bta_returned(struct dsi_context *ctx);
+bool dsi_is_rx_payload_fifo_full(struct dsi_context *ctx);
+bool dsi_is_rx_payload_fifo_empty(struct dsi_context *ctx);
+bool dsi_is_tx_payload_fifo_full(struct dsi_context *ctx);
+bool dsi_is_tx_payload_fifo_empty(struct dsi_context *ctx);
+bool dsi_is_tx_cmd_fifo_empty(struct dsi_context *ctx);
+void dsi_datalane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_datalane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_clklane_hs2lp_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_clklane_lp2hs_config(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_max_read_time(struct dsi_context *ctx, u16 byte_cycle);
+void dsi_nc_clk_en(struct dsi_context *ctx, int enable);
+void dsi_tx_escape_division(struct dsi_context *ctx, u8 div);
+void dsi_timeout_clock_division(struct dsi_context *ctx, u8 div);
+void dsi_lp_rx_timeout(struct dsi_context *ctx, u16 count);
+void dsi_hs_tx_timeout(struct dsi_context *ctx, u16 count);
+u32 dsi_int0_status(struct dsi_context *ctx);
+u32 dsi_int1_status(struct dsi_context *ctx);
+void dsi_int0_mask(struct dsi_context *ctx, u32 mask);
+void dsi_int1_mask(struct dsi_context *ctx, u32 mask);
+
+#endif /* _DW_DSI_CTRL_H_ */
drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.c new file +157 -0
new file mode 100644
index 000000000..6e28d7cf6
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "dw_dsi_ctrl.h"
+#include "dw_dsi_ctrl_ppi.h"
+
+/*
+ * Reset D-PHY module
+ */
+void dsi_phy_rstz(struct dsi_context *ctx, int level)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_reset_n = level;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/*
+ * Power up/down D-PHY module
+ */
+void dsi_phy_shutdownz(struct dsi_context *ctx, int level)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_shutdown = level;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/*
+ * Configure minimum wait period for HS transmission request after a stop state
+ */
+void dsi_phy_stop_wait_time(struct dsi_context *ctx, u8 byte_cycle)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(byte_cycle, &reg->PHY_MIN_STOP_TIME);
+}
+
+/*
+ * Set number of active lanes
+ */
+void dsi_phy_datalane_en(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+
+	writel(ctx->lanes - 1, &reg->PHY_LANE_NUM_CONFIG);
+}
+
+/*
+ * Enable clock lane module
+ */
+void dsi_phy_clklane_en(struct dsi_context *ctx, int en)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x78 phy_interface_ctrl;
+
+	phy_interface_ctrl.val = readl(&reg->PHY_INTERFACE_CTRL);
+	phy_interface_ctrl.bits.rf_phy_clk_en = en;
+
+	writel(phy_interface_ctrl.val, &reg->PHY_INTERFACE_CTRL);
+}
+
+/*
+ * Request the PHY module to start transmission of high speed clock.
+ * This causes the clock lane to start transmitting DDR clock on the
+ * lane interconnect.
+ */
+void dsi_phy_clk_hs_rqst(struct dsi_context *ctx, int enable)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x74 phy_clk_lane_lp_ctrl;
+
+	phy_clk_lane_lp_ctrl.val = readl(&reg->PHY_CLK_LANE_LP_CTRL);
+	phy_clk_lane_lp_ctrl.bits.auto_clklane_ctrl_en = 0;
+	phy_clk_lane_lp_ctrl.bits.phy_clklane_tx_req_hs = enable;
+
+	writel(phy_clk_lane_lp_ctrl.val, &reg->PHY_CLK_LANE_LP_CTRL);
+}
+
+/*
+ * Get D-PHY PPI status
+ */
+u8 dsi_phy_is_pll_locked(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0x9C phy_status;
+
+	phy_status.val = readl(&reg->PHY_STATUS);
+
+	return phy_status.bits.phy_lock;
+}
+
+void dsi_phy_test_clk(struct dsi_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xF0 phy_tst_ctrl0;
+
+	phy_tst_ctrl0.val = readl(&reg->PHY_TST_CTRL0);
+	phy_tst_ctrl0.bits.phy_testclk = value;
+
+	writel(phy_tst_ctrl0.val, &reg->PHY_TST_CTRL0);
+}
+
+void dsi_phy_test_clr(struct dsi_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xF0 phy_tst_ctrl0;
+
+	phy_tst_ctrl0.val = readl(&reg->PHY_TST_CTRL0);
+	phy_tst_ctrl0.bits.phy_testclr = value;
+
+	writel(phy_tst_ctrl0.val, &reg->PHY_TST_CTRL0);
+}
+
+void dsi_phy_test_en(struct dsi_context *ctx, u8 value)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+	phy_tst_ctrl1.bits.phy_testen = value;
+
+	writel(phy_tst_ctrl1.val, &reg->PHY_TST_CTRL1);
+}
+
+u8 dsi_phy_test_dout(struct dsi_context *ctx)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+
+	return phy_tst_ctrl1.bits.phy_testdout;
+}
+
+void dsi_phy_test_din(struct dsi_context *ctx, u8 data)
+{
+	struct dsi_reg *reg = (struct dsi_reg *)ctx->base;
+	union _0xF4 phy_tst_ctrl1;
+
+	phy_tst_ctrl1.val = readl(&reg->PHY_TST_CTRL1);
+	phy_tst_ctrl1.bits.phy_testdin = data;
+
+	writel(phy_tst_ctrl1.val, &reg->PHY_TST_CTRL1);
+}
drivers/gpu/drm/sprd/dw_dsi_ctrl_ppi.h new file +26 -0
new file mode 100644
index 000000000..d87fb7579
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _DW_DSI_CTRL_PPI_H_
+#define _DW_DSI_CTRL_PPI_H_
+
+#include "sprd_dsi.h"
+
+void dsi_phy_rstz(struct dsi_context *ctx, int level);
+void dsi_phy_shutdownz(struct dsi_context *ctx, int level);
+void dsi_phy_force_pll(struct dsi_context *ctx, int force);
+void dsi_phy_stop_wait_time(struct dsi_context *ctx, u8 byte_clk);
+void dsi_phy_datalane_en(struct dsi_context *ctx);
+void dsi_phy_clklane_en(struct dsi_context *ctx, int en);
+void dsi_phy_clk_hs_rqst(struct dsi_context *ctx, int en);
+u8 dsi_phy_is_pll_locked(struct dsi_context *ctx);
+void dsi_phy_test_clk(struct dsi_context *ctx, u8 level);
+void dsi_phy_test_clr(struct dsi_context *ctx, u8 level);
+void dsi_phy_test_en(struct dsi_context *ctx, u8 level);
+u8 dsi_phy_test_dout(struct dsi_context *ctx);
+void dsi_phy_test_din(struct dsi_context *ctx, u8 data);
+void dsi_phy_bist_en(struct dsi_context *ctx, int en);
+
+#endif /* _DW_DSI_CTRL_PPI_H_ */
\ No newline at end of file
drivers/gpu/drm/sprd/megacores_pll.c new file +317 -0
new file mode 100644
index 000000000..03c1f0f00
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <asm/div64.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+
+#include "megacores_pll.h"
+
+#define L						0
+#define H						1
+#define CLK						0
+#define DATA					1
+#define INFINITY				0xffffffff
+#define MIN_OUTPUT_FREQ			(100)
+
+#define AVERAGE(a, b) (min(a, b) + abs((b) - (a)) / 2)
+
+/* sharkle */
+#define VCO_BAND_LOW	750
+#define VCO_BAND_MID	1100
+#define VCO_BAND_HIGH	1500
+#define PHY_REF_CLK	26000
+
+static int dphy_calc_pll_param(struct dphy_pll *pll)
+{
+	const u32 khz = 1000;
+	const u32 mhz = 1000000;
+	const unsigned long long factor = 100;
+	unsigned long long tmp;
+	int i;
+
+	pll->potential_fvco = pll->freq / khz;
+	pll->ref_clk = PHY_REF_CLK / khz;
+
+	for (i = 0; i < 4; ++i) {
+		if (pll->potential_fvco >= VCO_BAND_LOW &&
+			pll->potential_fvco <= VCO_BAND_HIGH) {
+			pll->fvco = pll->potential_fvco;
+			pll->out_sel = BIT(i);
+			break;
+		}
+		pll->potential_fvco <<= 1;
+	}
+	if (pll->fvco == 0)
+		return -EINVAL;
+
+	if (pll->fvco >= VCO_BAND_LOW && pll->fvco <= VCO_BAND_MID) {
+		/* vco band control */
+		pll->vco_band = 0x0;
+		/* low pass filter control */
+		pll->lpf_sel = 1;
+	} else if (pll->fvco > VCO_BAND_MID && pll->fvco <= VCO_BAND_HIGH) {
+		pll->vco_band = 0x1;
+		pll->lpf_sel = 0;
+	} else
+		return -EINVAL;
+
+	pll->nint = pll->fvco / pll->ref_clk;
+	tmp = pll->fvco * factor * mhz;
+	do_div(tmp, pll->ref_clk);
+	tmp = tmp - pll->nint * factor * mhz;
+	tmp *= BIT(20);
+	do_div(tmp, 100000000);
+	pll->kint = (u32)tmp;
+	pll->refin = 3; /* pre-divider bypass */
+	pll->sdm_en = true; /* use fraction N PLL */
+	pll->fdk_s = 0x1; /* fraction */
+	pll->cp_s = 0x0;
+	pll->det_delay = 0x1;
+
+	return 0;
+}
+
+static void dphy_set_pll_reg(struct dphy_pll *pll, struct regmap *regmap)
+{
+	struct pll_reg *reg = &pll->reg;
+	u8 *val;
+	int i;
+
+	u8 reg_addr[] = {
+		0x03, 0x04, 0x06, 0x08, 0x09,
+		0x0a, 0x0b, 0x0e, 0x0f
+	};
+
+	reg->_03.bits.prbs_bist = 1;
+	reg->_03.bits.en_lp_treot = true;
+	reg->_03.bits.lpf_sel = pll->lpf_sel;
+	reg->_03.bits.txfifo_bypass = 0;
+	reg->_04.bits.div = pll->div;
+	reg->_04.bits.masterof8lane = 1;
+	reg->_04.bits.cp_s = pll->cp_s;
+	reg->_04.bits.fdk_s = pll->fdk_s;
+	reg->_06.bits.nint = pll->nint;
+	reg->_08.bits.vco_band = pll->vco_band;
+	reg->_08.bits.sdm_en = pll->sdm_en;
+	reg->_08.bits.refin = pll->refin;
+	reg->_09.bits.kint_h = pll->kint >> 12;
+	reg->_0a.bits.kint_m = (pll->kint >> 4) & 0xff;
+	reg->_0b.bits.out_sel = pll->out_sel;
+	reg->_0b.bits.kint_l = pll->kint & 0xf;
+	reg->_0e.bits.pll_pu_byp = 0;
+	reg->_0e.bits.pll_pu = 0;
+	reg->_0e.bits.stopstate_sel = 1;
+	reg->_0f.bits.det_delay = pll->det_delay;
+
+	val = (u8 *)&reg;
+
+	for (i = 0; i < sizeof(reg_addr); ++i) {
+		regmap_write(regmap, reg_addr[i], val[i]);
+		DRM_DEBUG("%02x: %02x\n", reg_addr[i], val[i]);
+	}
+}
+
+int dphy_pll_config(struct dsi_context *ctx)
+{
+	struct sprd_dsi *dsi = container_of(ctx, struct sprd_dsi, ctx);
+	struct regmap *regmap = ctx->regmap;
+	struct dphy_pll *pll = ctx->pll;
+	int ret;
+
+	pll->freq = ctx->byte_clk * 8;
+
+	/* FREQ = 26M * (NINT + KINT / 2^20) / out_sel */
+	ret = dphy_calc_pll_param(pll);
+	if (ret) {
+		drm_err(dsi->drm, "failed to calculate dphy pll parameters\n");
+		return ret;
+	}
+	dphy_set_pll_reg(pll, regmap);
+
+	return 0;
+}
+
+static void dphy_set_timing_reg(struct regmap *regmap, int type, u8 val[])
+{
+	switch (type) {
+	case REQUEST_TIME:
+		regmap_write(regmap, 0x31, val[CLK]);
+		regmap_write(regmap, 0x41, val[DATA]);
+		regmap_write(regmap, 0x51, val[DATA]);
+		regmap_write(regmap, 0x61, val[DATA]);
+		regmap_write(regmap, 0x71, val[DATA]);
+
+		regmap_write(regmap, 0x90, val[CLK]);
+		regmap_write(regmap, 0xa0, val[DATA]);
+		regmap_write(regmap, 0xb0, val[DATA]);
+		regmap_write(regmap, 0xc0, val[DATA]);
+		regmap_write(regmap, 0xd0, val[DATA]);
+		break;
+	case PREPARE_TIME:
+		regmap_write(regmap, 0x32, val[CLK]);
+		regmap_write(regmap, 0x42, val[DATA]);
+		regmap_write(regmap, 0x52, val[DATA]);
+		regmap_write(regmap, 0x62, val[DATA]);
+		regmap_write(regmap, 0x72, val[DATA]);
+
+		regmap_write(regmap, 0x91, val[CLK]);
+		regmap_write(regmap, 0xa1, val[DATA]);
+		regmap_write(regmap, 0xb1, val[DATA]);
+		regmap_write(regmap, 0xc1, val[DATA]);
+		regmap_write(regmap, 0xd1, val[DATA]);
+		break;
+	case ZERO_TIME:
+		regmap_write(regmap, 0x33, val[CLK]);
+		regmap_write(regmap, 0x43, val[DATA]);
+		regmap_write(regmap, 0x53, val[DATA]);
+		regmap_write(regmap, 0x63, val[DATA]);
+		regmap_write(regmap, 0x73, val[DATA]);
+
+		regmap_write(regmap, 0x92, val[CLK]);
+		regmap_write(regmap, 0xa2, val[DATA]);
+		regmap_write(regmap, 0xb2, val[DATA]);
+		regmap_write(regmap, 0xc2, val[DATA]);
+		regmap_write(regmap, 0xd2, val[DATA]);
+		break;
+	case TRAIL_TIME:
+		regmap_write(regmap, 0x34, val[CLK]);
+		regmap_write(regmap, 0x44, val[DATA]);
+		regmap_write(regmap, 0x54, val[DATA]);
+		regmap_write(regmap, 0x64, val[DATA]);
+		regmap_write(regmap, 0x74, val[DATA]);
+
+		regmap_write(regmap, 0x93, val[CLK]);
+		regmap_write(regmap, 0xa3, val[DATA]);
+		regmap_write(regmap, 0xb3, val[DATA]);
+		regmap_write(regmap, 0xc3, val[DATA]);
+		regmap_write(regmap, 0xd3, val[DATA]);
+		break;
+	case EXIT_TIME:
+		regmap_write(regmap, 0x36, val[CLK]);
+		regmap_write(regmap, 0x46, val[DATA]);
+		regmap_write(regmap, 0x56, val[DATA]);
+		regmap_write(regmap, 0x66, val[DATA]);
+		regmap_write(regmap, 0x76, val[DATA]);
+
+		regmap_write(regmap, 0x95, val[CLK]);
+		regmap_write(regmap, 0xA5, val[DATA]);
+		regmap_write(regmap, 0xB5, val[DATA]);
+		regmap_write(regmap, 0xc5, val[DATA]);
+		regmap_write(regmap, 0xd5, val[DATA]);
+		break;
+	case CLKPOST_TIME:
+		regmap_write(regmap, 0x35, val[CLK]);
+		regmap_write(regmap, 0x94, val[CLK]);
+		break;
+
+	/* the following just use default value */
+	case SETTLE_TIME:
+	case TA_GET:
+	case TA_GO:
+	case TA_SURE:
+		break;
+	default:
+		break;
+	}
+}
+
+void dphy_timing_config(struct dsi_context *ctx)
+{
+	struct regmap *regmap = ctx->regmap;
+	struct dphy_pll *pll = ctx->pll;
+	const u32 factor = 2;
+	const u32 scale = 100;
+	u32 t_ui, t_byteck, t_half_byteck;
+	u32 range[2], constant;
+	u8 val[2];
+	u32 tmp = 0;
+
+	/* t_ui: 1 ui, byteck: 8 ui, half byteck: 4 ui */
+	t_ui = 1000 * scale / (pll->freq / 1000);
+	t_byteck = t_ui << 3;
+	t_half_byteck = t_ui << 2;
+	constant = t_ui << 1;
+
+	/* REQUEST_TIME: HS T-LPX: LP-01
+	 * For T-LPX, mipi spec defined min value is 50ns,
+	 * but maybe it shouldn't be too small, because BTA,
+	 * LP-10, LP-00, LP-01, all of this is related to T-LPX.
+	 */
+	range[L] = 50 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * (factor << 1), t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_reg(regmap, REQUEST_TIME, val);
+
+	/* PREPARE_TIME: HS sequence: LP-00 */
+	range[L] = 38 * scale;
+	range[H] = 95 * scale;
+	tmp = AVERAGE(range[L], range[H]);
+	val[CLK] = DIV_ROUND_UP(AVERAGE(range[L], range[H]),
+			t_half_byteck) - 1;
+	range[L] = 40 * scale + 4 * t_ui;
+	range[H] = 85 * scale + 6 * t_ui;
+	tmp |= AVERAGE(range[L], range[H]) << 16;
+	val[DATA] = DIV_ROUND_UP(AVERAGE(range[L], range[H]),
+			t_half_byteck) - 1;
+	dphy_set_timing_reg(regmap, PREPARE_TIME, val);
+
+	/* ZERO_TIME: HS-ZERO */
+	range[L] = 300 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor + (tmp & 0xffff)
+			- 525 * t_byteck / 100, t_byteck) - 2;
+	range[L] = 145 * scale + 10 * t_ui;
+	val[DATA] = DIV_ROUND_UP(range[L] * factor
+			+ ((tmp >> 16) & 0xffff) - 525 * t_byteck / 100,
+			t_byteck) - 2;
+	dphy_set_timing_reg(regmap, ZERO_TIME, val);
+
+	/* TRAIL_TIME: HS-TRAIL */
+	range[L] = 60 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor - constant, t_half_byteck);
+	range[L] = max(8 * t_ui, 60 * scale + 4 * t_ui);
+	val[DATA] = DIV_ROUND_UP(range[L] * 3 / 2 - constant, t_half_byteck) - 2;
+	dphy_set_timing_reg(regmap, TRAIL_TIME, val);
+
+	/* EXIT_TIME: */
+	range[L] = 100 * scale;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_reg(regmap, EXIT_TIME, val);
+
+	/* CLKPOST_TIME: */
+	range[L] = 60 * scale + 52 * t_ui;
+	range[H] = INFINITY;
+	val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
+	val[DATA] = val[CLK];
+	dphy_set_timing_reg(regmap, CLKPOST_TIME, val);
+
+	/* SETTLE_TIME:
+	 * This time is used for receiver. So for transmitter,
+	 * it can be ignored.
+	 */
+
+	/* TA_GO:
+	 * transmitter drives bridge state(LP-00) before releasing control,
+	 * reg 0x1f default value: 0x04, which is good.
+	 */
+
+	/* TA_SURE:
+	 * After LP-10 state and before bridge state(LP-00),
+	 * reg 0x20 default value: 0x01, which is good.
+	 */
+
+	/* TA_GET:
+	 * receiver drives Bridge state(LP-00) before releasing control
+	 * reg 0x21 default value: 0x03, which is good.
+	 */
+}
drivers/gpu/drm/sprd/megacores_pll.h new file +146 -0
new file mode 100644
index 000000000..750dbbc09
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#ifndef _MEGACORES_PLL_H_
+#define _MEGACORES_PLL_H_
+
+#include "sprd_dsi.h"
+
+enum PLL_TIMING {
+	NONE,
+	REQUEST_TIME,
+	PREPARE_TIME,
+	SETTLE_TIME,
+	ZERO_TIME,
+	TRAIL_TIME,
+	EXIT_TIME,
+	CLKPOST_TIME,
+	TA_GET,
+	TA_GO,
+	TA_SURE,
+	TA_WAIT,
+};
+
+struct pll_reg {
+	union __reg_03__ {
+		struct __03 {
+			u8 prbs_bist: 1;
+			u8 en_lp_treot: 1;
+			u8 lpf_sel: 4;
+			u8 txfifo_bypass: 1;
+			u8 freq_hopping: 1;
+		} bits;
+		u8 val;
+	} _03;
+	union __reg_04__ {
+		struct __04 {
+			u8 div: 3;
+			u8 masterof8lane: 1;
+			u8 hop_trig: 1;
+			u8 cp_s: 2;
+			u8 fdk_s: 1;
+		} bits;
+		u8 val;
+	} _04;
+	union __reg_06__ {
+		struct __06 {
+			u8 nint: 7;
+			u8 mod_en: 1;
+		} bits;
+		u8 val;
+	} _06;
+	union __reg_07__ {
+		struct __07 {
+			u8 kdelta_h: 8;
+		} bits;
+		u8 val;
+	} _07;
+	union __reg_08__ {
+		struct __08 {
+			u8 vco_band: 1;
+			u8 sdm_en: 1;
+			u8 refin: 2;
+			u8 kdelta_l: 4;
+		} bits;
+		u8 val;
+	} _08;
+	union __reg_09__ {
+		struct __09 {
+			u8 kint_h: 8;
+		} bits;
+		u8 val;
+	} _09;
+	union __reg_0a__ {
+		struct __0a {
+			u8 kint_m: 8;
+		} bits;
+		u8 val;
+	} _0a;
+	union __reg_0b__ {
+		struct __0b {
+			u8 out_sel: 4;
+			u8 kint_l: 4;
+		} bits;
+		u8 val;
+	} _0b;
+	union __reg_0c__ {
+		struct __0c {
+			u8 kstep_h: 8;
+		} bits;
+		u8 val;
+	} _0c;
+	union __reg_0d__ {
+		struct __0d {
+			u8 kstep_m: 8;
+		} bits;
+		u8 val;
+	} _0d;
+	union __reg_0e__ {
+		struct __0e {
+			u8 pll_pu_byp: 1;
+			u8 pll_pu: 1;
+			u8 hsbist_len: 2;
+			u8 stopstate_sel: 1;
+			u8 kstep_l: 3;
+		} bits;
+		u8 val;
+	} _0e;
+	union __reg_0f__ {
+		struct __0f {
+			u8 det_delay:2;
+			u8 kdelta: 4;
+			u8 ldo0p4:2;
+		} bits;
+		u8 val;
+	} _0f;
+};
+
+struct dphy_pll {
+	u8 refin; /* Pre-divider control signal */
+	u8 cp_s; /* 00: SDM_EN=1, 10: SDM_EN=0 */
+	u8 fdk_s; /* PLL mode control: integer or fraction */
+	u8 sdm_en;
+	u8 div;
+	u8 int_n; /* integer N PLL */
+	u32 ref_clk; /* dphy reference clock, unit: MHz */
+	u32 freq; /* panel config, unit: KHz */
+	u32 fvco;
+	u32 potential_fvco;
+	u32 nint; /* sigma delta modulator NINT control */
+	u32 kint; /* sigma delta modulator KINT control */
+	u8 lpf_sel; /* low pass filter control */
+	u8 out_sel; /* post divider control */
+	u8 vco_band; /* vco range */
+	u8 det_delay;
+
+	struct pll_reg reg;
+};
+
+struct dsi_context;
+
+int dphy_pll_config(struct dsi_context *ctx);
+void dphy_timing_config(struct dsi_context *ctx);
+
+#endif /* _MEGACORES_PLL_H_ */
\ No newline at end of file
drivers/gpu/drm/sprd/sprd_drm.c +1 -0
index c626c6caf..1613b5db3 100644
@@ -192,6 +192,7 @@ static struct platform_driver sprd_drm_driver = {
 static struct platform_driver *sprd_drm_drivers[]  = {
 	&sprd_drm_driver,
 	&sprd_dpu_driver,
+	&sprd_dsi_driver,
 };
 
 static int __init sprd_drm_init(void)
drivers/gpu/drm/sprd/sprd_drm.h +1 -0
index 85d4a8b9f..95d1b972f 100644
@@ -14,5 +14,6 @@ struct sprd_drm {
 };
 
 extern struct platform_driver sprd_dpu_driver;
+extern struct platform_driver sprd_dsi_driver;
 
 #endif /* _SPRD_DRM_H_ */
drivers/gpu/drm/sprd/sprd_dsi.c new file +303 -0
new file mode 100644
index 000000000..d892b1934
@@ -0,0 +1,1132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Unisoc Inc.
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_graph.h>
+#include <video/mipi_display.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+
+#include "sprd_drm.h"
+#include "sprd_dpu.h"
+#include "sprd_dsi.h"
+#include "dw_dsi_ctrl.h"
+#include "dw_dsi_ctrl_ppi.h"
+
+#define encoder_to_dsi(encoder) \
+	container_of(encoder, struct sprd_dsi, encoder)
+#define host_to_dsi(host) \
+	container_of(host, struct sprd_dsi, host)
+#define connector_to_dsi(connector) \
+	container_of(connector, struct sprd_dsi, connector)
+
+static int regmap_tst_io_write(void *context, u32 reg, u32 val)
+{
+	struct sprd_dsi *dsi = context;
+	struct dsi_context *ctx = &dsi->ctx;
+
+	if (val > 0xff || reg > 0xff)
+		return -EINVAL;
+
+	drm_dbg(dsi->drm, "reg = 0x%02x, val = 0x%02x\n", reg, val);
+
+	dsi_phy_test_en(ctx, 1);
+	dsi_phy_test_din(ctx, reg);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+	dsi_phy_test_en(ctx, 0);
+	dsi_phy_test_din(ctx, val);
+	dsi_phy_test_clk(ctx, 1);
+	dsi_phy_test_clk(ctx, 0);
+
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

drivers/gpu/drm/sprd/sprd_dsi.c line 60

Quoted 10 lines
drivers/gpu/drm/sprd/sprd_dsi.c new file +10 -0
+	return 0;
+}
+
+static int regmap_tst_io_read(void *context, u32 reg, u32 *val)
+{
+	struct sprd_dsi *dsi = context;
+	struct dsi_context *ctx = &dsi->ctx;
+	int ret;
+
+	if (reg > 0xff)
[… more lines in the raw message]

Strictly speaking, there's nothing that guarantees that the preferred mode is the one you're going to use there. You should use the mode that is given to you through the connector state.

Maxime

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Part of the diff is not shown here. View the raw message.