build/patch/kernel/archive/starfive-6.1/1043-drm-starfive-Add-StarFive-drm-driver.patch

3590 lines
104 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: "sw.multimedia" <sw.multimedia@starfivetech.com>
Date: Tue, 31 Aug 2021 16:48:57 +0800
Subject: drm/starfive: Add StarFive drm driver
Add starfive DRM Display driver framework
Signed-off-by: jack.zhu <jack.zhu@starfivetech.com>
Signed-off-by: keith.zhao <keith.zhao@starfivetech.com>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Jose Exposito <jose.exposito89@gmail.com>
Link: https://lore.kernel.org/r/a8ca722539672d6369d6e4092e1e08cb6b58c546.1645535955.git.geert@linux-m68k.org
Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/starfive/Kconfig | 17 +
drivers/gpu/drm/starfive/Makefile | 13 +
drivers/gpu/drm/starfive/README.txt | 56 +
drivers/gpu/drm/starfive/starfive_drm_crtc.c | 511 ++++++
drivers/gpu/drm/starfive/starfive_drm_crtc.h | 86 +
drivers/gpu/drm/starfive/starfive_drm_drv.c | 265 +++
drivers/gpu/drm/starfive/starfive_drm_drv.h | 25 +
drivers/gpu/drm/starfive/starfive_drm_encoder.c | 129 ++
drivers/gpu/drm/starfive/starfive_drm_encoder.h | 18 +
drivers/gpu/drm/starfive/starfive_drm_gem.c | 346 ++++
drivers/gpu/drm/starfive/starfive_drm_gem.h | 40 +
drivers/gpu/drm/starfive/starfive_drm_lcdc.c | 512 ++++++
drivers/gpu/drm/starfive/starfive_drm_lcdc.h | 160 ++
drivers/gpu/drm/starfive/starfive_drm_plane.c | 227 +++
drivers/gpu/drm/starfive/starfive_drm_plane.h | 12 +
drivers/gpu/drm/starfive/starfive_drm_vpp.c | 806 ++++++++++
drivers/gpu/drm/starfive/starfive_drm_vpp.h | 201 +++
19 files changed, 3427 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f30f99166531..785a124b0e69 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -347,6 +347,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
source "drivers/gpu/drm/sun4i/Kconfig"
+source "drivers/gpu/drm/starfive/Kconfig"
+
source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0b283e46f28b..ffda709edeae 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -114,6 +114,7 @@ obj-y += rcar-du/
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-y += omapdrm/
obj-$(CONFIG_DRM_SUN4I) += sun4i/
+obj-$(CONFIG_DRM_STARFIVE) += starfive/
obj-y += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
diff --git a/drivers/gpu/drm/starfive/Kconfig b/drivers/gpu/drm/starfive/Kconfig
new file mode 100644
index 000000000000..df8eea2c6549
--- /dev/null
+++ b/drivers/gpu/drm/starfive/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 StarFive Technology Co., Ltd.
+
+config DRM_STARFIVE
+ tristate "DRM Support for StarFive SoCs"
+ depends on DRM
+ depends on SIFIVE_CCACHE
+ depends on SOC_STARFIVE || COMPILE_TEST
+ select DRM_GEM_DMA_HELPER
+ select DRM_KMS_HELPER
+ select DRM_MIPI_DSI
+ select DRM_PANEL
+ help
+ Choose this option if you have a StarFive SoCs.
+ The module will be called starfive-drm
+ This driver provides kernel mode setting and
+ buffer management to userspace.
diff --git a/drivers/gpu/drm/starfive/Makefile b/drivers/gpu/drm/starfive/Makefile
new file mode 100644
index 000000000000..8ef9e5f469fd
--- /dev/null
+++ b/drivers/gpu/drm/starfive/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2021 StarFive Technology Co., Ltd.
+#
+starfive-drm-y := starfive_drm_drv.o \
+ starfive_drm_gem.o \
+ starfive_drm_crtc.o \
+ starfive_drm_encoder.o \
+ starfive_drm_plane.o \
+ starfive_drm_lcdc.o \
+ starfive_drm_vpp.o
+
+obj-$(CONFIG_DRM_STARFIVE) += starfive-drm.o
diff --git a/drivers/gpu/drm/starfive/README.txt b/drivers/gpu/drm/starfive/README.txt
new file mode 100644
index 000000000000..dadec80c98bf
--- /dev/null
+++ b/drivers/gpu/drm/starfive/README.txt
@@ -0,0 +1,56 @@
+Display Subsystem:(default FBdev)
+
+Steps switch to DRM:
+1、Disable fbdev,close below config items
+CONFIG_FB_STARFIVE=y
+CONFIG_FB_STARFIVE_HDMI_TDA998X=y
+CONFIG_FB_STARFIVE_VIDEO=y
+
+2、open DRM hdmi pipeline,enable items:
+CONFIG_DRM_I2C_NXP_TDA998X=y
+CONFIG_DRM_I2C_NXP_TDA9950=y
+CONFIG_DRM_STARFIVE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+
+Precautionswhen use DRM hdmi pipeline,please make sure CONFIG_DRM_STARFIVE_MIPI_DSI is disable ,
+ or will cause color abnormal.
+
+3、open DRM mipi pipeline
+
+enable items:
+ CONFIG_PHY_M31_DPHY_RX0=y
+ CONFIG_DRM_STARFIVE_MIPI_DSI=y
+
+
+change jh7100.dtsi display-encoder as below
+
+ display-encoder {
+ compatible = "starfive,display-encoder";
+ encoder-type = <6>; //2-TMDS, 3-LVDS, 6-DSI, 8-DPI
+ status = "okay";
+
+ ports {
+ port@0 {
+ endpoint {
+ remote-endpoint = <&dsi_out_port>;
+ };
+ };
+
+ port@1 {
+ endpoint {
+ remote-endpoint = <&crtc_0_out>;
+ };
+ };
+ };
+ };
+
+install libdrm:
+make buildroot_initramfs-menuconfig
+choose:
+BR2_PACKAGE_LIBDRM=y
+BR2_PACKAGE_LIBDRM_RADEON=y
+BR2_PACKAGE_LIBDRM_AMDGPU=y
+BR2_PACKAGE_LIBDRM_NOUVEAU=y
+BR2_PACKAGE_LIBDRM_ETNAVIV=y
+BR2_PACKAGE_LIBDRM_INSTALL_TESTS=y
+
diff --git a/drivers/gpu/drm/starfive/starfive_drm_crtc.c b/drivers/gpu/drm/starfive/starfive_drm_crtc.c
new file mode 100644
index 000000000000..5ba973377059
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_crtc.c
@@ -0,0 +1,511 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/delay.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include "starfive_drm_drv.h"
+#include "starfive_drm_crtc.h"
+#include "starfive_drm_plane.h"
+#include "starfive_drm_lcdc.h"
+#include "starfive_drm_vpp.h"
+//#include <video/sys_comm_regs.h>
+
+static inline struct drm_encoder *
+starfive_head_atom_get_encoder(struct starfive_crtc *sf_crtc)
+{
+ struct drm_encoder *encoder = NULL;
+
+ /* We only ever have a single encoder */
+ drm_for_each_encoder_mask(encoder, sf_crtc->crtc.dev,
+ sf_crtc->crtc.state->encoder_mask)
+ break;
+
+ return encoder;
+}
+
+static int ddrfmt_to_ppfmt(struct starfive_crtc *sf_crtc)
+{
+ int ddrfmt = sf_crtc->ddr_format;
+ int ret = 0;
+
+ sf_crtc->lcdcfmt = WIN_FMT_XRGB8888; //lcdc default used
+ sf_crtc->pp_conn_lcdc = 1;//default config
+ switch (ddrfmt) {
+ case DRM_FORMAT_UYVY:
+ sf_crtc->vpp_format = COLOR_YUV422_UYVY;
+ break;
+ case DRM_FORMAT_VYUY:
+ sf_crtc->vpp_format = COLOR_YUV422_VYUY;
+ break;
+ case DRM_FORMAT_YUYV:
+ sf_crtc->vpp_format = COLOR_YUV422_YUYV;
+ break;
+ case DRM_FORMAT_YVYU:
+ sf_crtc->vpp_format = COLOR_YUV422_YVYU;
+ break;
+ case DRM_FORMAT_YUV420:
+ sf_crtc->vpp_format = COLOR_YUV420P;
+ break;
+ case DRM_FORMAT_NV21:
+ sf_crtc->vpp_format = COLOR_YUV420_NV21;
+ break;
+ case DRM_FORMAT_NV12:
+ sf_crtc->vpp_format = COLOR_YUV420_NV12;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ sf_crtc->vpp_format = COLOR_RGB888_ARGB;
+ break;
+ case DRM_FORMAT_ABGR8888:
+ sf_crtc->vpp_format = COLOR_RGB888_ABGR;
+ break;
+ case DRM_FORMAT_RGBA8888:
+ sf_crtc->vpp_format = COLOR_RGB888_RGBA;
+ break;
+ case DRM_FORMAT_BGRA8888:
+ sf_crtc->vpp_format = COLOR_RGB888_BGRA;
+ break;
+ case DRM_FORMAT_RGB565:
+ sf_crtc->vpp_format = COLOR_RGB565;
+ //sf_crtc->lcdcfmt = WIN_FMT_RGB565;
+ //this format no need pp, lcdc can direct read ddr buff
+ //sf_crtc->pp_conn_lcdc = -1;
+ break;
+ case DRM_FORMAT_XRGB1555:
+ sf_crtc->lcdcfmt = WIN_FMT_XRGB1555;
+ sf_crtc->pp_conn_lcdc = -1;//this format no need pp, lcdc can direct read ddr buff;
+ break;
+ case DRM_FORMAT_XRGB4444:
+ sf_crtc->lcdcfmt = WIN_FMT_XRGB4444;
+ sf_crtc->pp_conn_lcdc = -1;//this format no need pp, lcdc can direct read ddr buff;
+ break;
+
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+void starfive_crtc_hw_config_simple(struct starfive_crtc *starfive_crtc)
+{
+}
+
+static void starfive_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_crtc_cleanup(crtc);
+}
+
+static void starfive_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct starfive_crtc_state *s = to_starfive_crtc_state(state);
+
+ __drm_atomic_helper_crtc_destroy_state(&s->base);
+ kfree(s);
+}
+
+static void starfive_crtc_reset(struct drm_crtc *crtc)
+{
+ struct starfive_crtc_state *crtc_state =
+ kzalloc(sizeof(*crtc_state), GFP_KERNEL);
+
+ if (crtc->state)
+ starfive_crtc_destroy_state(crtc, crtc->state);
+
+ __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base);
+}
+
+static struct drm_crtc_state *starfive_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+ struct starfive_crtc_state *starfive_state;
+
+ starfive_state = kzalloc(sizeof(*starfive_state), GFP_KERNEL);
+ if (!starfive_state)
+ return NULL;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &starfive_state->base);
+
+ return &starfive_state->base;
+}
+
+static int starfive_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+ //need set hw
+ return 0;
+}
+
+static void starfive_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+ //need set hw
+}
+
+static const struct drm_crtc_funcs starfive_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .destroy = starfive_crtc_destroy,
+ .set_property = NULL,
+ .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
+ .cursor_move = NULL, /* handled by drm_mode_cursor_universal */
+ .reset = starfive_crtc_reset,
+ .atomic_duplicate_state = starfive_crtc_duplicate_state,
+ .atomic_destroy_state = starfive_crtc_destroy_state,
+ //.gamma_set = drm_atomic_helper_legacy_gamma_set,
+ .enable_vblank = starfive_crtc_enable_vblank,
+ .disable_vblank = starfive_crtc_disable_vblank,
+ //.set_crc_source = starfive_crtc_set_crc_source,
+ //.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
+ //.verify_crc_source = starfive_crtc_verify_crc_source,
+};
+
+static bool starfive_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* Nothing to do here, but this callback is mandatory. */
+ return true;
+}
+
+static int starfive_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ //state->no_vblank = true; // hardware without VBLANK interrupt ???
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+ crtc);
+ crtc_state->no_vblank = true;
+
+ return 0;
+}
+
+static void starfive_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_crtc_state)
+{
+ //starfive_crtc_gamma_set(crtcp, crtc, old_crtc_state);
+}
+
+static void starfive_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *old_crtc_state)
+{
+ struct starfive_crtc *crtcp = to_starfive_crtc(crtc);
+
+ //starfive_flush_dcache(crtcp->dma_addr, 1920*1080*2);
+ DRM_DEBUG_DRIVER("ddr_format_change [%d], dma_addr_change [%d]\n",
+ crtcp->ddr_format_change, crtcp->dma_addr_change);
+ if (crtcp->ddr_format_change || crtcp->dma_addr_change) {
+ ddrfmt_to_ppfmt(crtcp);
+ starfive_pp_update(crtcp);
+ } else {
+ DRM_DEBUG_DRIVER("%s with no change\n", __func__);
+ }
+}
+
+static void starfive_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct starfive_crtc *crtcp = to_starfive_crtc(crtc);
+
+// enable crtc HW
+#ifdef CONFIG_DRM_STARFIVE_MIPI_DSI
+ dsitx_vout_init(crtcp);
+ lcdc_dsi_sel(crtcp);
+#else
+ vout_reset(crtcp);
+#endif
+ ddrfmt_to_ppfmt(crtcp);
+ starfive_pp_enable(crtcp);
+ starfive_lcdc_enable(crtcp);
+ crtcp->is_enabled = true; // should before
+}
+
+static void starfive_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct starfive_crtc *crtcp = to_starfive_crtc(crtc);
+ int pp_id;
+
+ for (pp_id = 0; pp_id < PP_NUM; pp_id++) {
+ if (crtcp->pp[pp_id].inited == 1) {
+ pp_disable_intr(crtcp, pp_id);
+ vout_disable(crtcp); // disable crtc HW
+ }
+ }
+ crtcp->is_enabled = false;
+}
+
+static enum drm_mode_status starfive_crtc_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ int refresh = drm_mode_vrefresh(mode);
+
+ if (refresh > 60) //lcdc miss support 60+ fps
+ return MODE_BAD;
+ else
+ return MODE_OK;
+}
+
+static const struct drm_crtc_helper_funcs starfive_crtc_helper_funcs = {
+ .mode_fixup = starfive_crtc_mode_fixup,
+ .atomic_check = starfive_crtc_atomic_check,
+ .atomic_begin = starfive_crtc_atomic_begin,
+ .atomic_flush = starfive_crtc_atomic_flush,
+ .atomic_enable = starfive_crtc_atomic_enable,
+ .atomic_disable = starfive_crtc_atomic_disable,
+ .mode_valid = starfive_crtc_mode_valid,
+};
+
+static int starfive_crtc_create(struct drm_device *drm_dev,
+ struct starfive_crtc *starfive_crtc,
+ const struct drm_crtc_funcs *crtc_funcs,
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs)
+{
+ struct drm_crtc *crtc = &starfive_crtc->crtc;
+ struct device *dev = drm_dev->dev;
+ struct device_node *port;
+ int ret;
+
+ starfive_crtc->planes = devm_kzalloc(dev, sizeof(struct drm_plane), GFP_KERNEL);
+ ret = starfive_plane_init(drm_dev, starfive_crtc, DRM_PLANE_TYPE_PRIMARY);
+ if (ret) {
+ dev_err(drm_dev->dev, "failed to construct primary plane\n");
+ return ret;
+ }
+
+ drm_crtc_init_with_planes(drm_dev, crtc, starfive_crtc->planes, NULL,
+ crtc_funcs, NULL);
+ drm_crtc_helper_add(crtc, crtc_helper_funcs);
+ port = of_get_child_by_name(starfive_crtc->dev->of_node, "port");
+ if (!port) {
+ DRM_ERROR("no port node found in %s\n", dev->of_node->full_name);
+ ret = -ENOENT;
+ }
+
+ crtc->port = port;
+ return ret;
+}
+
+static int starfive_crtc_get_memres(struct platform_device *pdev, struct starfive_crtc *sf_crtc)
+{
+ static const char *const mem_res_name[] = {
+ "lcdc", "vpp0", "vpp1", "vpp2", "clk", "rst", "sys"
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mem_res_name); i++) {
+ const char *name = mem_res_name[i];
+ void __iomem *regs = devm_platform_ioremap_resource_byname(pdev, name);
+
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ if (!strcmp(name, "lcdc"))
+ sf_crtc->base_lcdc = regs;
+ else if (!strcmp(name, "vpp0"))
+ sf_crtc->base_vpp0 = regs;
+ else if (!strcmp(name, "vpp1"))
+ sf_crtc->base_vpp1 = regs;
+ else if (!strcmp(name, "vpp2"))
+ sf_crtc->base_vpp2 = regs;
+ else if (!strcmp(name, "clk"))
+ sf_crtc->base_clk = regs;
+ else if (!strcmp(name, "rst"))
+ sf_crtc->base_rst = regs;
+ else if (!strcmp(name, "sys"))
+ sf_crtc->base_syscfg = regs;
+ else
+ dev_err(&pdev->dev, "Could not match resource name\n");
+ }
+
+ return 0;
+}
+
+static int starfive_parse_dt(struct device *dev, struct starfive_crtc *sf_crtc)
+{
+ int ret;
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int pp_num = 0;
+
+ if (!np)
+ return -EINVAL;
+
+ sf_crtc->pp = devm_kzalloc(dev, sizeof(struct pp_mode) * PP_NUM, GFP_KERNEL);
+ if (!sf_crtc->pp)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ if (of_property_read_u32(child, "pp-id", &pp_num)) {
+ ret = -EINVAL;
+ continue;
+ }
+ if (pp_num >= PP_NUM)
+ dev_err(dev, " pp-id number %d is not support!\n", pp_num);
+
+ sf_crtc->pp[pp_num].pp_id = pp_num;
+ sf_crtc->pp[pp_num].bus_out = of_property_read_bool(child, "sys-bus-out");
+ sf_crtc->pp[pp_num].fifo_out = of_property_read_bool(child, "fifo-out");
+ if (of_property_read_u32(child, "src-format", &sf_crtc->pp[pp_num].src.format)) {
+ dev_err(dev, "Missing src-format property in the DT.\n");
+ ret = -EINVAL;
+ }
+ if (of_property_read_u32(child, "src-width", &sf_crtc->pp[pp_num].src.width)) {
+ dev_err(dev, "Missing src-width property in the DT. w %d\n",
+ sf_crtc->pp[pp_num].src.width);
+ ret = -EINVAL;
+ }
+ if (of_property_read_u32(child, "src-height", &sf_crtc->pp[pp_num].src.height)) {
+ dev_err(dev, "Missing src-height property in the DT.\n");
+ ret = -EINVAL;
+ }
+ if (of_property_read_u32(child, "dst-format", &sf_crtc->pp[pp_num].dst.format)) {
+ dev_err(dev, "Missing dst-format property in the DT.\n");
+ ret = -EINVAL;
+ }
+ if (of_property_read_u32(child, "dst-width", &sf_crtc->pp[pp_num].dst.width)) {
+ dev_err(dev, "Missing dst-width property in the DT.\n");
+ ret = -EINVAL;
+ }
+ if (of_property_read_u32(child, "dst-height", &sf_crtc->pp[pp_num].dst.height)) {
+ dev_err(dev, "Missing dst-height property in the DT.\n");
+ ret = -EINVAL;
+ }
+ sf_crtc->pp[pp_num].inited = 1;
+ }
+
+ return ret;
+}
+
+static int starfive_crtc_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm_dev = data;
+ struct starfive_crtc *crtcp;
+ int ret;
+
+ crtcp = devm_kzalloc(dev, sizeof(*crtcp), GFP_KERNEL);
+ if (!crtcp)
+ return -ENOMEM;
+
+ crtcp->dev = dev;
+ crtcp->drm_dev = drm_dev;
+ dev_set_drvdata(dev, crtcp);
+
+ spin_lock_init(&crtcp->reg_lock);
+
+ ret = starfive_crtc_get_memres(pdev, crtcp);
+ if (ret)
+ return ret;
+
+ crtcp->clk_disp_axi = devm_clk_get(dev, "disp_axi");
+ if (IS_ERR(crtcp->clk_disp_axi))
+ return dev_err_probe(dev, PTR_ERR(crtcp->clk_disp_axi),
+ "error getting axi clock\n");
+
+ crtcp->clk_vout_src = devm_clk_get(dev, "disp_axi");
+ if (IS_ERR(crtcp->clk_vout_src))
+ return dev_err_probe(dev, PTR_ERR(crtcp->clk_vout_src),
+ "error getting vout clock\n");
+
+ crtcp->rst_disp_axi = devm_reset_control_get_exclusive(dev, "disp_axi");
+ if (IS_ERR(crtcp->rst_disp_axi))
+ return dev_err_probe(dev, PTR_ERR(crtcp->rst_disp_axi),
+ "error getting axi reset\n");
+
+ crtcp->rst_vout_src = devm_reset_control_get_exclusive(dev, "vout_src");
+ if (IS_ERR(crtcp->rst_vout_src))
+ return dev_err_probe(dev, PTR_ERR(crtcp->rst_vout_src),
+ "error getting vout reset\n");
+
+ ret = starfive_parse_dt(dev, crtcp);
+
+ crtcp->pp_conn_lcdc = starfive_pp_get_2lcdc_id(crtcp);
+
+ crtcp->lcdc_irq = platform_get_irq_byname(pdev, "lcdc_irq");
+ if (crtcp->lcdc_irq < 0)
+ return dev_err_probe(dev, crtcp->lcdc_irq, "error getting lcdc irq\n");
+
+ crtcp->vpp1_irq = platform_get_irq_byname(pdev, "vpp1_irq");
+ if (crtcp->vpp1_irq < 0)
+ return dev_err_probe(dev, crtcp->vpp1_irq, "error getting vpp1 irq\n");
+
+ ret = devm_request_irq(&pdev->dev, crtcp->lcdc_irq, lcdc_isr_handler, 0,
+ "sf_lcdc", crtcp);
+ if (ret)
+ return dev_err_probe(dev, ret, "error requesting irq %d\n", crtcp->lcdc_irq);
+
+ ret = devm_request_irq(&pdev->dev, crtcp->vpp1_irq, vpp1_isr_handler, 0,
+ "sf_vpp1", crtcp);
+ if (ret)
+ return dev_err_probe(dev, ret, "error requesting irq %d\n", crtcp->vpp1_irq);
+
+ ret = starfive_crtc_create(drm_dev, crtcp,
+ &starfive_crtc_funcs,
+ &starfive_crtc_helper_funcs);
+ if (ret)
+ return ret;
+
+ crtcp->is_enabled = false;
+
+ /* starfive_set_crtc_possible_masks(drm_dev, crtcp); */
+
+ /*
+ ret = drm_self_refresh_helper_init(crtcp);
+ if (ret)
+ DRM_DEV_DEBUG_KMS(crtcp->dev,
+ "Failed to init %s with SR helpers %d, ignoring\n",
+ crtcp->name, ret);
+ */
+
+ return 0;
+}
+
+static void starfive_crtc_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct starfive_crtc *crtcp = dev_get_drvdata(dev);
+
+ drm_crtc_cleanup(&crtcp->crtc);
+ platform_set_drvdata(pdev, NULL);
+}
+
+static const struct component_ops starfive_crtc_component_ops = {
+ .bind = starfive_crtc_bind,
+ .unbind = starfive_crtc_unbind,
+};
+
+static const struct of_device_id starfive_crtc_driver_dt_match[] = {
+ { .compatible = "starfive,jh7100-crtc" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, starfive_crtc_driver_dt_match);
+
+static int starfive_crtc_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &starfive_crtc_component_ops);
+}
+
+static int starfive_crtc_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &starfive_crtc_component_ops);
+ return 0;
+}
+
+struct platform_driver starfive_crtc_driver = {
+ .probe = starfive_crtc_probe,
+ .remove = starfive_crtc_remove,
+ .driver = {
+ .name = "starfive-crtc",
+ .of_match_table = of_match_ptr(starfive_crtc_driver_dt_match),
+ },
+};
diff --git a/drivers/gpu/drm/starfive/starfive_drm_crtc.h b/drivers/gpu/drm/starfive/starfive_drm_crtc.h
new file mode 100644
index 000000000000..b04706ae8282
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_crtc.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef _STARFIVE_DRM_CRTC_H
+#define _STARFIVE_DRM_CRTC_H
+#include <drm/drm_crtc.h>
+
+enum COLOR_FORMAT {
+ COLOR_YUV422_UYVY = 0, //00={Y1,V0,Y0,U0}
+ COLOR_YUV422_VYUY = 1, //01={Y1,U0,Y0,V0}
+ COLOR_YUV422_YUYV = 2, //10={V0,Y1,U0,Y0}
+ COLOR_YUV422_YVYU = 3, //11={U0,Y1,V0,Y0}
+
+ COLOR_YUV420P,
+ COLOR_YUV420_NV21,
+ COLOR_YUV420_NV12,
+
+ COLOR_RGB888_ARGB,
+ COLOR_RGB888_ABGR,
+ COLOR_RGB888_RGBA,
+ COLOR_RGB888_BGRA,
+ COLOR_RGB565,
+};
+
+struct starfive_crtc_state {
+ struct drm_crtc_state base;
+};
+
+#define to_starfive_crtc_state(s) \
+ container_of(s, struct starfive_crtc_state, base)
+
+struct starfive_crtc {
+ struct drm_crtc crtc;
+ struct device *dev;
+ struct drm_device *drm_dev;
+ bool is_enabled;
+
+ void __iomem *base_clk; // 0x12240000
+ void __iomem *base_rst; // 0x12250000
+ void __iomem *base_syscfg; // 0x12260000
+ void __iomem *base_vpp0; // 0x12040000
+ void __iomem *base_vpp1; // 0x12080000
+ void __iomem *base_vpp2; // 0x120c0000
+ void __iomem *base_lcdc; // 0x12000000
+
+ struct clk *clk_disp_axi;
+ struct clk *clk_vout_src;
+
+ struct reset_control *rst_disp_axi;
+ struct reset_control *rst_vout_src;
+
+ int lcdc_irq;
+ int vpp0_irq;
+ int vpp1_irq;
+ int vpp2_irq;
+
+ struct pp_mode *pp;
+
+ int win_num;
+ int pp_conn_lcdc;
+ unsigned int ddr_format;
+ bool ddr_format_change;
+ enum COLOR_FORMAT vpp_format;
+ int lcdcfmt;
+
+ /* one time only one process allowed to config the register */
+ spinlock_t reg_lock;
+
+ struct drm_plane *planes;
+
+ u8 lut_r[256];
+ u8 lut_g[256];
+ u8 lut_b[256];
+
+ bool gamma_lut;
+ dma_addr_t dma_addr;
+ bool dma_addr_change;
+ size_t size;
+};
+
+#define to_starfive_crtc(x) container_of(x, struct starfive_crtc, crtc)
+
+void starfive_crtc_hw_config_simple(struct starfive_crtc *starfive_crtc);
+
+#endif /* _STARFIVE_DRM_CRTC_H */
diff --git a/drivers/gpu/drm/starfive/starfive_drm_drv.c b/drivers/gpu/drm/starfive/starfive_drm_drv.c
new file mode 100644
index 000000000000..ac9c182f502b
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_drv.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <linux/component.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/soc/mediatek/mtk-mmsys.h>
+#include <linux/dma-mapping.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_dma_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 "starfive_drm_drv.h"
+#include "starfive_drm_gem.h"
+
+#define DRIVER_NAME "starfive"
+#define DRIVER_DESC "starfive Soc DRM"
+#define DRIVER_DATE "20210519"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static struct drm_framebuffer *
+starfive_drm_mode_fb_create(struct drm_device *dev, struct drm_file *file,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ return drm_gem_fb_create(dev, file, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs starfive_drm_mode_config_funcs = {
+ .fb_create = starfive_drm_mode_fb_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static const struct drm_mode_config_helper_funcs starfive_drm_mode_config_helpers = {
+ .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+static const struct file_operations starfive_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = starfive_drm_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .compat_ioctl = drm_compat_ioctl,
+ .release = drm_release,
+};
+
+static struct drm_driver starfive_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+ .dumb_create = starfive_drm_gem_dumb_create,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import_sg_table = starfive_drm_gem_prime_import_sg_table,
+ .gem_prime_mmap = starfive_drm_gem_mmap_buf,
+ .fops = &starfive_drm_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+static int compare_dev(struct device *dev, void *data)
+{
+ return dev == (struct device *)data;
+}
+
+static void starfive_drm_match_add(struct device *dev,
+ struct component_match **match,
+ struct platform_driver *const *drivers,
+ int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct device_driver *drv = &drivers[i]->driver;
+ struct device *p = NULL, *d;
+
+ while ((d = platform_find_device_by_driver(p, drv))) {
+ put_device(p);
+ component_match_add(dev, match, compare_dev, d);
+ p = d;
+ }
+ put_device(p);
+ }
+}
+
+static void starfive_cleanup(struct drm_device *ddev)
+{
+ struct starfive_drm_private *private = ddev->dev_private;
+
+ drm_kms_helper_poll_fini(ddev);
+ drm_atomic_helper_shutdown(ddev);
+ drm_mode_config_cleanup(ddev);
+ component_unbind_all(ddev->dev, ddev);
+ kfree(private);
+ ddev->dev_private = NULL;
+}
+
+static int starfive_drm_bind(struct device *dev)
+{
+ struct drm_device *drm_dev;
+ struct starfive_drm_private *private;
+ int ret;
+
+ drm_dev = drm_dev_alloc(&starfive_drm_driver, dev);
+ if (IS_ERR(drm_dev))
+ return PTR_ERR(drm_dev);
+
+ dev_set_drvdata(dev, drm_dev);
+
+ private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
+ if (!private) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ drm_dev->dev_private = private;
+
+ /*
+ ret = starfive_drm_init_iommu(drm_dev);
+ if (ret)
+ goto err_free;
+ */
+
+ ret = drmm_mode_config_init(drm_dev);
+ if (ret)
+ goto err_free;
+
+ drm_dev->mode_config.min_width = 64;
+ drm_dev->mode_config.min_height = 64;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ drm_dev->mode_config.max_width = 4096;
+ drm_dev->mode_config.max_height = 4096;
+ drm_dev->mode_config.funcs = &starfive_drm_mode_config_funcs;
+ drm_dev->mode_config.helper_private = &starfive_drm_mode_config_helpers;
+ drm_dev->mode_config.async_page_flip = 1;
+
+ ret = component_bind_all(dev, drm_dev);
+ if (ret)
+ goto err_free;
+
+ ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
+ if (ret)
+ goto err_cleanup;
+
+ drm_mode_config_reset(drm_dev);
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(drm_dev);
+
+ ret = drm_dev_register(drm_dev, 0);
+ if (ret)
+ goto err_cleanup;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+ drm_fbdev_generic_setup(drm_dev, 16);
+#endif
+ return 0;
+
+err_cleanup:
+ starfive_cleanup(drm_dev);
+err_free:
+ drm_dev_put(drm_dev);
+ return ret;
+}
+
+static void starfive_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ drm_dev_unregister(drm_dev);
+}
+
+static const struct component_master_ops starfive_drm_ops = {
+ .bind = starfive_drm_bind,
+ .unbind = starfive_drm_unbind,
+};
+
+static struct platform_driver * const starfive_component_drivers[] = {
+ &starfive_crtc_driver,
+#ifdef CONFIG_DRM_STARFIVE_MIPI_DSI
+ &starfive_dsi_platform_driver,
+#endif
+ &starfive_encoder_driver,
+};
+
+static int starfive_drm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct component_match *match = NULL;
+
+ starfive_drm_match_add(dev, &match,
+ starfive_component_drivers,
+ ARRAY_SIZE(starfive_component_drivers));
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+
+ return component_master_add_with_match(dev, &starfive_drm_ops, match);
+}
+
+static int starfive_drm_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &starfive_drm_ops);
+ return 0;
+}
+
+static const struct of_device_id starfive_drm_dt_ids[] = {
+ { .compatible = "starfive,display-subsystem" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, starfive_drm_dt_ids);
+
+static struct platform_driver starfive_drm_platform_driver = {
+ .probe = starfive_drm_probe,
+ .remove = starfive_drm_remove,
+ .driver = {
+ .name = "starfive-drm",
+ .of_match_table = starfive_drm_dt_ids,
+ //.pm = &starfive_drm_pm_ops,
+ },
+};
+
+static int __init starfive_drm_init(void)
+{
+ int ret;
+
+ ret = platform_register_drivers(starfive_component_drivers,
+ ARRAY_SIZE(starfive_component_drivers));
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&starfive_drm_platform_driver);
+}
+
+static void __exit starfive_drm_exit(void)
+{
+ platform_unregister_drivers(starfive_component_drivers,
+ ARRAY_SIZE(starfive_component_drivers));
+ platform_driver_unregister(&starfive_drm_platform_driver);
+}
+
+module_init(starfive_drm_init);
+module_exit(starfive_drm_exit);
+
+MODULE_AUTHOR("StarFive <StarFive@starfivetech.com>");
+MODULE_DESCRIPTION("StarFive SoC DRM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/starfive/starfive_drm_drv.h b/drivers/gpu/drm/starfive/starfive_drm_drv.h
new file mode 100644
index 000000000000..b965fa5f7252
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_drv.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef _STARFIVE_DRM_DRV_H
+#define _STARFIVE_DRM_DRV_H
+
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_gem.h>
+#include <linux/module.h>
+#include <linux/component.h>
+
+struct starfive_drm_private {
+ struct drm_fb_helper fbdev_helper;
+ struct drm_gem_object *fbdev_bo;
+ struct mutex mm_lock;
+ struct drm_mm mm;
+};
+
+extern struct platform_driver starfive_crtc_driver;
+extern struct platform_driver starfive_encoder_driver;
+extern struct platform_driver starfive_dsi_platform_driver;
+
+#endif /* _STARFIVE_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/starfive/starfive_drm_encoder.c b/drivers/gpu/drm/starfive/starfive_drm_encoder.c
new file mode 100644
index 000000000000..56c7f10239bc
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_encoder.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_of.h>
+#include "starfive_drm_drv.h"
+#include "starfive_drm_encoder.h"
+
+static void starfive_encoder_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+static const struct drm_encoder_funcs starfive_encoder_funcs = {
+ .destroy = starfive_encoder_destroy,
+};
+
+static int starfive_encoder_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct device_node *np = dev->of_node;
+ struct starfive_encoder *encoderp;
+ int ret;
+ struct drm_panel *tmp_panel;
+ struct drm_bridge *tmp_bridge;
+ u32 crtcs = 0;
+
+ encoderp = devm_kzalloc(dev, sizeof(*encoderp), GFP_KERNEL);
+ if (!encoderp)
+ return -ENOMEM;
+
+ encoderp->dev = dev;
+ encoderp->drm_dev = drm_dev;
+ dev_set_drvdata(dev, encoderp);
+
+ if (dev->of_node) {
+ crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+
+ if (of_property_read_u32(np, "encoder-type", &encoderp->encoder_type)) {
+ DRM_ERROR("Missing encoder-type property in the DT.\n");
+ encoderp->encoder_type = DRM_MODE_ENCODER_TMDS;
+ }
+ }
+
+ /* If no CRTCs were found, fall back to our old behaviour */
+ if (crtcs == 0) {
+ dev_warn(dev, "Falling back to first CRTC\n");
+ crtcs = 1 << 0;
+ }
+
+ encoderp->encoder.possible_crtcs = crtcs;
+
+ ret = drm_encoder_init(drm_dev, &encoderp->encoder,
+ &starfive_encoder_funcs,
+ encoderp->encoder_type, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "error initializing encoder\n");
+
+ ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
+ &tmp_panel, &tmp_bridge);
+ if (ret) {
+ dev_err_probe(dev, ret, "endpoint returns %d\n", ret);
+ goto err_bridge;
+ }
+
+ if (tmp_panel)
+ DRM_INFO("found panel on endpoint\n");
+ if (tmp_bridge)
+ DRM_INFO("found bridge on endpoint\n");
+
+ ret = drm_bridge_attach(&encoderp->encoder, tmp_bridge, NULL, 0);
+ if (ret)
+ goto err_bridge;
+
+ return 0;
+
+err_bridge:
+ drm_encoder_cleanup(&encoderp->encoder);
+ return ret;
+}
+
+static void starfive_encoder_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct starfive_encoder *encoderp = dev_get_drvdata(dev);
+
+ starfive_encoder_destroy(&encoderp->encoder);
+}
+
+static const struct component_ops starfive_encoder_component_ops = {
+ .bind = starfive_encoder_bind,
+ .unbind = starfive_encoder_unbind,
+};
+
+static const struct of_device_id starfive_encoder_driver_dt_match[] = {
+ { .compatible = "starfive,display-encoder" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, starfive_encoder_driver_dt_match);
+
+static int starfive_encoder_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &starfive_encoder_component_ops);
+}
+
+static int starfive_encoder_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &starfive_encoder_component_ops);
+ return 0;
+}
+
+struct platform_driver starfive_encoder_driver = {
+ .probe = starfive_encoder_probe,
+ .remove = starfive_encoder_remove,
+ .driver = {
+ .name = "display-encoder",
+ .of_match_table = of_match_ptr(starfive_encoder_driver_dt_match),
+ },
+};
diff --git a/drivers/gpu/drm/starfive/starfive_drm_encoder.h b/drivers/gpu/drm/starfive/starfive_drm_encoder.h
new file mode 100644
index 000000000000..5ea21441e502
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_encoder.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef _STARFIVE_DRM_ENCODER_H
+#define _STARFIVE_DRM_ENCODER_H
+
+struct starfive_encoder {
+ struct drm_encoder encoder;
+ struct device *dev;
+ struct drm_device *drm_dev;
+ bool is_enabled;
+ int encoder_type;
+};
+
+#define to_starfive_encoder(x) container_of(x, struct starfive_encoder, encoder)
+
+#endif /* _STARFIVE_DRM_CRTC_H */
diff --git a/drivers/gpu/drm/starfive/starfive_drm_gem.c b/drivers/gpu/drm/starfive/starfive_drm_gem.c
new file mode 100644
index 000000000000..4f0f68595da7
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_gem.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <linux/dma-buf.h>
+#include <linux/iommu.h>
+#include <linux/vmalloc.h>
+#include <drm/drm.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_prime.h>
+#include <drm/drm_vma_manager.h>
+#include <drm/drm_gem_dma_helper.h>
+#include "starfive_drm_drv.h"
+#include "starfive_drm_gem.h"
+
+static const struct drm_gem_object_funcs starfive_gem_object_funcs;
+static const struct vm_operations_struct mmap_mem_ops = {
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+ .access = generic_access_phys
+#endif
+};
+
+static int starfive_drm_gem_object_mmap_dma(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct starfive_drm_gem_obj *starfive_obj = to_starfive_gem_obj(obj);
+ struct drm_device *drm = obj->dev;
+
+ return dma_mmap_attrs(drm->dev, vma, starfive_obj->kvaddr,
+ starfive_obj->dma_addr, obj->size, starfive_obj->dma_attrs);
+}
+
+static int starfive_drm_gem_object_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ int ret;
+
+ /*
+ * We allocated a struct page table for rk_obj, so clear
+ * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
+ */
+ vma->vm_flags &= ~VM_PFNMAP;
+ ret = starfive_drm_gem_object_mmap_dma(obj, vma);
+ if (ret)
+ drm_gem_vm_close(vma);
+
+ return ret;
+}
+
+int starfive_drm_gem_mmap_buf(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ int ret = drm_gem_mmap_obj(obj, obj->size, vma);
+
+ if (ret)
+ return ret;
+
+ return starfive_drm_gem_object_mmap(obj, vma);
+}
+
+int starfive_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj;
+ int ret;
+
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+
+ obj = vma->vm_private_data;
+
+ /*
+ * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the
+ * whole buffer from the start.
+ */
+ vma->vm_pgoff = 0;
+
+ return starfive_drm_gem_object_mmap(obj, vma);
+}
+
+void starfive_drm_gem_free_object(struct drm_gem_object *obj)
+{
+ struct starfive_drm_gem_obj *starfive_gem = to_starfive_gem_obj(obj);
+ struct drm_device *drm_dev = obj->dev;
+
+ if (starfive_gem->sg)
+ drm_prime_gem_destroy(obj, starfive_gem->sg);
+ else
+ dma_free_attrs(drm_dev->dev, obj->size, starfive_gem->kvaddr,
+ starfive_gem->dma_addr, starfive_gem->dma_attrs);
+
+ /* release file pointer to gem object. */
+ drm_gem_object_release(obj);
+
+ kfree(starfive_gem);
+}
+
+static struct starfive_drm_gem_obj *
+starfive_drm_gem_alloc_object(struct drm_device *drm, unsigned int size)
+{
+ struct starfive_drm_gem_obj *starfive_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ starfive_obj = kzalloc(sizeof(*starfive_obj), GFP_KERNEL);
+ if (!starfive_obj)
+ return ERR_PTR(-ENOMEM);
+
+ obj = &starfive_obj->base;
+ ret = drm_gem_object_init(drm, obj, round_up(size, PAGE_SIZE));
+ if (ret)
+ return ERR_PTR(ret);
+
+ return starfive_obj;
+}
+
+static int starfive_drm_gem_alloc_dma(struct starfive_drm_gem_obj *starfive_obj,
+ bool alloc_kmap)
+{
+ struct drm_gem_object *obj = &starfive_obj->base;
+ struct drm_device *drm = obj->dev;
+
+ starfive_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
+ if (!alloc_kmap)
+ starfive_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
+
+ starfive_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
+ &starfive_obj->dma_addr, GFP_KERNEL,
+ starfive_obj->dma_attrs);
+
+ DRM_INFO("kvaddr = 0x%px\n", starfive_obj->kvaddr);
+ DRM_INFO("dma_addr = 0x%llx, size = %lu\n", starfive_obj->dma_addr, obj->size);
+ if (!starfive_obj->kvaddr) {
+ DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int starfive_drm_gem_alloc_buf(struct starfive_drm_gem_obj *starfive_obj,
+ bool alloc_kmap)
+{
+ return starfive_drm_gem_alloc_dma(starfive_obj, alloc_kmap);
+}
+
+static void starfive_drm_gem_release_object(struct starfive_drm_gem_obj *starfive_obj)
+{
+ drm_gem_object_release(&starfive_obj->base);
+ kfree(starfive_obj);
+}
+
+static struct starfive_drm_gem_obj *
+starfive_drm_gem_create_object(struct drm_device *drm, unsigned int size,
+ bool alloc_kmap)
+{
+ struct starfive_drm_gem_obj *starfive_obj;
+ int ret;
+
+ starfive_obj = starfive_drm_gem_alloc_object(drm, size);
+ if (IS_ERR(starfive_obj))
+ return starfive_obj;
+
+ ret = starfive_drm_gem_alloc_buf(starfive_obj, alloc_kmap);
+ if (ret)
+ goto err_free_obj;
+
+ starfive_obj->base.funcs = &starfive_gem_object_funcs;
+
+ return starfive_obj;
+
+err_free_obj:
+ starfive_drm_gem_release_object(starfive_obj);
+ return ERR_PTR(ret);
+}
+
+static struct starfive_drm_gem_obj *
+starfive_drm_gem_create_with_handle(struct drm_file *file_priv,
+ struct drm_device *drm,
+ unsigned int size,
+ unsigned int *handle)
+{
+ struct starfive_drm_gem_obj *starfive_gem;
+ struct drm_gem_object *gem;
+ int ret;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+ //config true, for console display
+ starfive_gem = starfive_drm_gem_create_object(drm, size, true);
+#else
+ starfive_gem = starfive_drm_gem_create_object(drm, size, false);
+#endif
+ if (IS_ERR(starfive_gem))
+ return ERR_CAST(starfive_gem);
+
+ gem = &starfive_gem->base;
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, gem, handle);
+ if (ret)
+ goto err_handle_create;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_put(gem);
+
+ return starfive_gem;
+
+err_handle_create:
+ starfive_drm_gem_free_object(gem);
+
+ return ERR_PTR(ret);
+}
+
+int starfive_drm_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct starfive_drm_gem_obj *starfive_gem;
+
+ args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+ args->size = args->pitch * args->height;
+
+ starfive_gem = starfive_drm_gem_create_with_handle(file_priv, dev,
+ args->size,
+ &args->handle);
+
+ return PTR_ERR_OR_ZERO(starfive_gem);
+}
+
+struct sg_table *starfive_drm_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct starfive_drm_gem_obj *starfive_obj = to_starfive_gem_obj(obj);
+ struct drm_device *drm = obj->dev;
+ struct sg_table *sgt;
+ int ret;
+
+ if (starfive_obj->pages)
+ return drm_prime_pages_to_sg(obj->dev, starfive_obj->pages,
+ starfive_obj->num_pages);
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ ret = dma_get_sgtable_attrs(drm->dev, sgt, starfive_obj->kvaddr,
+ starfive_obj->dma_addr, obj->size,
+ starfive_obj->dma_attrs);
+ if (ret) {
+ DRM_ERROR("failed to allocate sgt, %d\n", ret);
+ kfree(sgt);
+ return ERR_PTR(ret);
+ }
+
+ return sgt;
+}
+
+static int
+starfive_drm_gem_dma_map_sg(struct drm_device *drm,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sg,
+ struct starfive_drm_gem_obj *starfive_obj)
+{
+ int err;
+
+ err = dma_map_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
+ if (err)
+ return err;
+
+ if (drm_prime_get_contiguous_size(sg) < attach->dmabuf->size) {
+ DRM_ERROR("failed to map sg_table to contiguous linear address.\n");
+ dma_unmap_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
+ return -EINVAL;
+ }
+
+ starfive_obj->dma_addr = sg_dma_address(sg->sgl);
+ starfive_obj->sg = sg;
+
+ return 0;
+}
+
+struct drm_gem_object *
+starfive_drm_gem_prime_import_sg_table(struct drm_device *drm,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sg)
+{
+ struct starfive_drm_gem_obj *starfive_obj;
+ int ret;
+
+ starfive_obj = starfive_drm_gem_alloc_object(drm, attach->dmabuf->size);
+ if (IS_ERR(starfive_obj))
+ return ERR_CAST(starfive_obj);
+
+ ret = starfive_drm_gem_dma_map_sg(drm, attach, sg, starfive_obj);
+ if (ret < 0) {
+ DRM_ERROR("failed to import sg table: %d\n", ret);
+ goto err_free_obj;
+ }
+
+ return &starfive_obj->base;
+
+err_free_obj:
+ starfive_drm_gem_release_object(starfive_obj);
+ return ERR_PTR(ret);
+}
+
+int starfive_drm_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+ struct starfive_drm_gem_obj *starfive_obj = to_starfive_gem_obj(obj);
+
+ if (starfive_obj->pages) {
+ void *vaddr = vmap(starfive_obj->pages, starfive_obj->num_pages, VM_MAP,
+ pgprot_writecombine(PAGE_KERNEL));
+ if (!vaddr)
+ return -ENOMEM;
+ iosys_map_set_vaddr(map, vaddr);
+ return 0;
+ }
+
+ if (starfive_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING)
+ return -ENOMEM;
+
+ iosys_map_set_vaddr(map, starfive_obj->kvaddr);
+
+ return 0;
+}
+
+void starfive_drm_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+ struct starfive_drm_gem_obj *starfive_obj = to_starfive_gem_obj(obj);
+
+ if (starfive_obj->pages) {
+ vunmap(map->vaddr);
+ return;
+ }
+ /* Nothing to do if allocated by DMA mapping API. */
+}
+
+static const struct drm_gem_object_funcs starfive_gem_object_funcs = {
+ .free = starfive_drm_gem_free_object,
+ .get_sg_table = starfive_drm_gem_prime_get_sg_table,
+ .vmap = starfive_drm_gem_prime_vmap,
+ .vunmap = starfive_drm_gem_prime_vunmap,
+ .vm_ops = &drm_gem_dma_vm_ops,
+};
diff --git a/drivers/gpu/drm/starfive/starfive_drm_gem.h b/drivers/gpu/drm/starfive/starfive_drm_gem.h
new file mode 100644
index 000000000000..13a222a26e17
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_gem.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef _STARFIVE_DRM_GEM_H
+#define _STARFIVE_DRM_GEM_H
+
+#include <drm/drm_gem.h>
+
+struct starfive_drm_gem_obj {
+ struct drm_gem_object base;
+ //void *cookie; //mtk
+ void *kvaddr;
+ dma_addr_t dma_addr;
+ unsigned long dma_attrs;
+
+ /* Used when IOMMU is enabled */
+ unsigned long num_pages;
+ struct sg_table *sg;
+ struct page **pages;
+};
+
+#define to_starfive_gem_obj(x) container_of(x, struct starfive_drm_gem_obj, base)
+
+void starfive_drm_gem_free_object(struct drm_gem_object *obj);
+int starfive_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+int starfive_drm_gem_mmap_buf(struct drm_gem_object *obj,
+ struct vm_area_struct *vma);
+int starfive_drm_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+struct sg_table *starfive_drm_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+starfive_drm_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sg);
+int starfive_drm_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map);
+void starfive_drm_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map);
+
+#endif /* _STARFIVE_DRM_GEM_H */
diff --git a/drivers/gpu/drm/starfive/starfive_drm_lcdc.c b/drivers/gpu/drm/starfive/starfive_drm_lcdc.c
new file mode 100644
index 000000000000..7e9bcbde3092
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_lcdc.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/units.h>
+#include <drm/drm_crtc.h>
+#include "starfive_drm_lcdc.h"
+#include "starfive_drm_vpp.h"
+
+static u32 sf_fb_clkread32(struct starfive_crtc *sf_crtc, u32 reg)
+{
+ return ioread32(sf_crtc->base_clk + reg);
+}
+
+static void sf_fb_clkwrite32(struct starfive_crtc *sf_crtc, u32 reg, u32 val)
+{
+ iowrite32(val, sf_crtc->base_clk + reg);
+}
+
+static u32 sf_fb_lcdcread32(struct starfive_crtc *sf_crtc, u32 reg)
+{
+ return ioread32(sf_crtc->base_lcdc + reg);
+}
+
+static void sf_fb_lcdcwrite32(struct starfive_crtc *sf_crtc, u32 reg, u32 val)
+{
+ iowrite32(val, sf_crtc->base_lcdc + reg);
+}
+
+static u32 starfive_lcdc_rstread32(struct starfive_crtc *sf_crtc, u32 reg)
+{
+ return ioread32(sf_crtc->base_rst + reg);
+}
+
+static void starfive_lcdc_rstwrite32(struct starfive_crtc *sf_crtc, u32 reg, u32 val)
+{
+ iowrite32(val, sf_crtc->base_rst + reg);
+}
+
+static void lcdc_mode_cfg(struct starfive_crtc *sf_crtc, u32 work_mode, int dot_edge,
+ int sync_edge, int r2y_bypass, int src_sel, int int_src, int int_freq)
+{
+ u32 lcdc_en = 0x1;
+ u32 cfg = lcdc_en |
+ work_mode << LCDC_WORK_MODE |
+ dot_edge << LCDC_DOTCLK_P |
+ sync_edge << LCDC_HSYNC_P |
+ sync_edge << LCDC_VSYNC_P |
+ 0x0 << LCDC_DITHER_EN |
+ r2y_bypass << LCDC_R2Y_BPS |
+ src_sel << LCDC_TV_LCD_PATHSEL |
+ int_src << LCDC_INT_SEL |
+ int_freq << LCDC_INT_FREQ;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_GCTRL, cfg);
+ dev_dbg(sf_crtc->dev, "LCDC WorkMode: 0x%x, LCDC Path: %d\n", work_mode, src_sel);
+}
+
+static void lcdc_timing_cfg(struct starfive_crtc *sf_crtc,
+ struct drm_crtc_state *state, int vunit)
+{
+ int hpw, hbk, hfp, vpw, vbk, vfp;
+ u32 htiming, vtiming, hvwid;
+
+ //h-sync
+ int hsync_len = state->adjusted_mode.crtc_hsync_end -
+ state->adjusted_mode.crtc_hsync_start;
+ //h-bp
+ int left_margin = state->adjusted_mode.crtc_htotal -
+ state->adjusted_mode.crtc_hsync_end;
+ //h-fp
+ int right_margin = state->adjusted_mode.crtc_hsync_start -
+ state->adjusted_mode.crtc_hdisplay;
+ //v-sync
+ int vsync_len = state->adjusted_mode.crtc_vsync_end -
+ state->adjusted_mode.crtc_vsync_start;
+ //v-bp
+ int upper_margin = state->adjusted_mode.crtc_vtotal -
+ state->adjusted_mode.crtc_vsync_end;
+ //v-fp
+ int lower_margin = state->adjusted_mode.crtc_vsync_start -
+ state->adjusted_mode.crtc_vdisplay;
+
+ hpw = hsync_len - 1;
+ hbk = hsync_len + left_margin;
+ hfp = right_margin;
+ vpw = vsync_len - 1;
+ vbk = vsync_len + upper_margin;
+ vfp = lower_margin;
+
+ dev_dbg(sf_crtc->dev, "%s: h-sync = %d, h-bp = %d, h-fp = %d", __func__,
+ hsync_len, left_margin, right_margin);
+ dev_dbg(sf_crtc->dev, "%s: v-sync = %d, v-bp = %d, v-fp = %d", __func__,
+ vsync_len, upper_margin, lower_margin);
+
+ htiming = hbk | hfp << LCDC_RGB_HFP;
+ vtiming = vbk | vfp << LCDC_RGB_VFP;
+ hvwid = hpw | vpw << LCDC_RGB_VPW | vunit << LCDC_RGB_UNIT;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_RGB_H_TMG, htiming);
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_RGB_V_TMG, vtiming);
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_RGB_W_TMG, hvwid);
+ dev_dbg(sf_crtc->dev, "LCDC HPW: %d, HBK: %d, HFP: %d\n", hpw, hbk, hfp);
+ dev_dbg(sf_crtc->dev, "LCDC VPW: %d, VBK: %d, VFP: %d\n", vpw, vbk, vfp);
+ dev_dbg(sf_crtc->dev, "LCDC V-Unit: %d, 0-HSYNC and 1-dotClk period\n", vunit);
+}
+
+//? background size
+//lcdc_desize_cfg(sf_dev, sf_dev->display_info.xres-1, sf_dev->display_info.yres-1);
+static void lcdc_desize_cfg(struct starfive_crtc *sf_crtc, struct drm_crtc_state *state)
+{
+ int hsize = state->adjusted_mode.crtc_hdisplay - 1;
+ int vsize = state->adjusted_mode.crtc_vdisplay - 1;
+ u32 sizecfg = hsize | vsize << LCDC_BG_VSIZE;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_BACKGROUND, sizecfg);
+ dev_dbg(sf_crtc->dev, "LCDC Dest H-Size: %d, V-Size: %d\n", hsize, vsize);
+}
+
+static void lcdc_rgb_dclk_cfg(struct starfive_crtc *sf_crtc, int dot_clk_sel)
+{
+ u32 cfg = dot_clk_sel << 16;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_RGB_DCLK, cfg);
+ dev_dbg(sf_crtc->dev, "LCDC Dot_clock_output_sel: 0x%x\n", cfg);
+}
+
+// color table
+//win0, no lock transfer
+//win3, no src_sel and addr_mode, 0 assigned to them
+//lcdc_win_cfgA(sf_dev, win_num, sf_dev->display_info.xres-1, sf_dev->display_info.yres-1,
+// 0x1, 0x0, 0x0, 0x1, 0x0, 0x0);
+static void lcdc_win_cfgA(struct starfive_crtc *sf_crtc, struct drm_crtc_state *state,
+ int win_num, int lay_en, int clor_tab,
+ int color_en, int addr_mode, int lock)
+{
+ int hsize = state->adjusted_mode.crtc_hdisplay - 1;
+ int vsize = state->adjusted_mode.crtc_vdisplay - 1;
+ int src_sel_v = 1;
+ u32 cfg;
+
+ if (sf_crtc->pp_conn_lcdc < 0)
+ src_sel_v = 0;
+
+ cfg = hsize | vsize << LCDC_WIN_VSIZE | lay_en << LCDC_WIN_EN |
+ clor_tab << LCDC_CC_EN | color_en << LCDC_CK_EN |
+ src_sel_v << LCDC_WIN_ISSEL | addr_mode << LCDC_WIN_PM |
+ lock << LCDC_WIN_CLK;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_WIN0_CFG_A + win_num * 0xC, cfg);
+ dev_dbg(sf_crtc->dev,
+ "LCDC Win%d H-Size: %d, V-Size: %d, lay_en: %d, Src: %d, AddrMode: %d\n",
+ win_num, hsize, vsize, lay_en, src_sel_v, addr_mode);
+}
+
+static void lcdc_win_cfgB(struct starfive_crtc *sf_crtc,
+ int win_num, int xpos, int ypos, int argb_ord)
+{
+ int win_format = sf_crtc->lcdcfmt;
+ u32 cfg;
+
+#ifdef CONFIG_DRM_STARFIVE_MIPI_DSI
+ argb_ord = 0;
+#else
+ argb_ord = 1;
+#endif
+
+ cfg = xpos |
+ ypos << LCDC_WIN_VPOS |
+ win_format << LCDC_WIN_FMT |
+ argb_ord << LCDC_WIN_ARGB_ORDER;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_WIN0_CFG_B + win_num * 0xC, cfg);
+ dev_dbg(sf_crtc->dev,
+ "LCDC Win%d Xpos: %d, Ypos: %d, win_format: 0x%x, ARGB Order: 0x%x\n",
+ win_num, xpos, ypos, win_format, argb_ord);
+}
+
+//? Color key
+static void lcdc_win_cfgC(struct starfive_crtc *sf_crtc, int win_num, int color_key)
+{
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_WIN0_CFG_C + win_num * 0xC, color_key);
+ dev_dbg(sf_crtc->dev, "LCDC Win%d Color Key: 0x%6x\n", win_num, color_key);
+}
+
+//? hsize
+//lcdc_win_src_size(sf_dev, win_num, sf_dev->display_info.xres-1);
+static void lcdc_win_src_size(struct starfive_crtc *sf_crtc,
+ struct drm_crtc_state *state, int win_num)
+{
+ int addr, off, winsize, pre_cfg, cfg;
+ int hsize = state->adjusted_mode.crtc_hdisplay - 1;
+
+ switch (win_num) {
+ case 0:
+ addr = LCDC_WIN01_HSIZE;
+ off = 0xfffff000;
+ winsize = hsize;
+ break;
+ case 1:
+ addr = LCDC_WIN01_HSIZE;
+ off = 0xff000fff;
+ winsize = hsize << LCDC_IMG_HSIZE;
+ break;
+ case 2:
+ addr = LCDC_WIN23_HSIZE;
+ off = 0xfffff000;
+ winsize = hsize;
+ break;
+ case 3:
+ addr = LCDC_WIN23_HSIZE;
+ off = 0xff000fff;
+ winsize = hsize << LCDC_IMG_HSIZE;
+ break;
+ case 4:
+ addr = LCDC_WIN45_HSIZE;
+ off = 0xfffff000;
+ winsize = hsize;
+ break;
+ case 5:
+ addr = LCDC_WIN45_HSIZE;
+ off = 0xff000fff;
+ winsize = hsize << LCDC_IMG_HSIZE;
+ break;
+ case 6:
+ addr = LCDC_WIN67_HSIZE;
+ off = 0xfffff000;
+ winsize = hsize;
+ break;
+ case 7:
+ addr = LCDC_WIN67_HSIZE;
+ off = 0xff000fff;
+ winsize = hsize << LCDC_IMG_HSIZE;
+ break;
+ default:
+ addr = LCDC_WIN01_HSIZE;
+ off = 0xfffff000;
+ winsize = hsize;
+ break;
+ }
+ pre_cfg = sf_fb_lcdcread32(sf_crtc, addr) & off;
+ cfg = winsize | pre_cfg;
+ sf_fb_lcdcwrite32(sf_crtc, addr, cfg);
+ dev_dbg(sf_crtc->dev, "LCDC Win%d Src Hsize: %d\n", win_num, hsize);
+}
+
+static void lcdc_alpha_val_cfg(struct starfive_crtc *sf_crtc,
+ int val1, int val2, int val3, int val4, int sel)
+{
+ u32 val = val1 |
+ val2 << LCDC_ALPHA2 |
+ val3 << LCDC_ALPHA3 |
+ val4 << LCDC_ALPHA4 |
+ sel << LCDC_01_ALPHA_SEL;
+ u32 pre_val = sf_fb_lcdcread32(sf_crtc, LCDC_ALPHA_VALUE) & 0xfffb0000U;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_ALPHA_VALUE, pre_val | val);
+ dev_dbg(sf_crtc->dev, "LCDC Alpha 1: %x, 2: %x, 3: %x, 4: %x\n", val1, val2, val3, val4);
+}
+
+static void lcdc_panel_cfg(struct starfive_crtc *sf_crtc,
+ int buswid, int depth, int txcycle, int pixpcycle,
+ int rgb565sel, int rgb888sel)
+{
+ u32 cfg = buswid |
+ depth << LCDC_COLOR_DEP |
+ txcycle << LCDC_TCYCLES |
+ pixpcycle << LCDC_PIXELS |
+ rgb565sel << LCDC_565RGB_SEL |
+ rgb888sel << LCDC_888RGB_SEL;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_PANELDATAFMT, cfg);
+ dev_dbg(sf_crtc->dev, "LCDC bus bit: :%d, pixDep: 0x%x, txCyle: %d, %dpix/cycle, RGB565 2cycle_%d, RGB888 3cycle_%d\n",
+ buswid, depth, txcycle, pixpcycle, rgb565sel, rgb888sel);
+}
+
+//win_num: 0-2
+static void lcdc_win02_addr_cfg(struct starfive_crtc *sf_crtc, int addr0, int addr1)
+{
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_WIN0STARTADDR0 + sf_crtc->win_num * 0x8, addr0);
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_WIN0STARTADDR1 + sf_crtc->win_num * 0x8, addr1);
+ dev_dbg(sf_crtc->dev, "LCDC Win%d Start Addr0: 0x%8x, Addr1: 0x%8x\n",
+ sf_crtc->win_num, addr0, addr1);
+}
+
+void starfive_set_win_addr(struct starfive_crtc *sf_crtc, int addr)
+{
+ lcdc_win02_addr_cfg(sf_crtc, addr, 0x0);
+}
+
+void lcdc_enable_intr(struct starfive_crtc *sf_crtc)
+{
+ u32 cfg = ~(1U << LCDC_OUT_FRAME_END);
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_INT_MSK, cfg);
+}
+
+void lcdc_disable_intr(struct starfive_crtc *sf_crtc)
+{
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_INT_MSK, 0xff);
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_INT_CLR, 0xff);
+}
+
+int lcdc_win_sel(struct starfive_crtc *sf_crtc, enum lcdc_in_mode sel)
+{
+ int win_num;
+
+ switch (sel) {
+ case LCDC_IN_LCD_AXI:
+ win_num = LCDC_WIN_0;
+ break;
+ case LCDC_IN_VPP2:
+ win_num = LCDC_WIN_0;
+ break;
+ case LCDC_IN_VPP1:
+ win_num = LCDC_WIN_2;
+ break;
+ case LCDC_IN_VPP0:
+ win_num = LCDC_WIN_1;
+ //mapconv_pp0_sel(sf_dev, 0x0);
+ break;
+ case LCDC_IN_MAPCONVERT:
+ win_num = LCDC_WIN_1;
+ //mapconv_pp0_sel(sf_dev, 0x1);
+ break;
+ default:
+ win_num = 2;
+ }
+
+ return win_num;
+}
+
+void lcdc_dsi_sel(struct starfive_crtc *sf_crtc)
+{
+ int temp;
+ u32 lcdc_en = 0x1;
+ u32 work_mode = 0x1;
+ u32 cfg = lcdc_en | work_mode << LCDC_WORK_MODE;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_GCTRL, cfg);
+ temp = starfive_lcdc_rstread32(sf_crtc, SRST_ASSERT0);
+ temp &= ~(0x1 << BIT_RST_DSI_DPI_PIX);
+ starfive_lcdc_rstwrite32(sf_crtc, SRST_ASSERT0, temp);
+}
+
+irqreturn_t lcdc_isr_handler(int this_irq, void *dev_id)
+{
+ struct starfive_crtc *sf_crtc = dev_id;
+ //u32 intr_status = sf_fb_lcdcread32(sf_crtc, LCDC_INT_STATUS);
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_INT_CLR, 0xffffffff);
+
+ return IRQ_HANDLED;
+}
+
+void lcdc_int_cfg(struct starfive_crtc *sf_crtc, int mask)
+{
+ u32 cfg;
+
+ if (mask == 0x1)
+ cfg = 0xffffffff;
+ else
+ cfg = ~(1U << LCDC_OUT_FRAME_END); //only frame end interrupt mask
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_INT_MSK, cfg);
+}
+
+void lcdc_config(struct starfive_crtc *sf_crtc, struct drm_crtc_state *state, int win_num)
+{
+ lcdc_mode_cfg(sf_crtc, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0);
+ lcdc_timing_cfg(sf_crtc, state, 0);
+ lcdc_desize_cfg(sf_crtc, state);
+ lcdc_rgb_dclk_cfg(sf_crtc, 0x1);
+
+ if (sf_crtc->pp_conn_lcdc < 0) //ddr->lcdc
+ lcdc_win02_addr_cfg(sf_crtc, sf_crtc->dma_addr, 0x0);
+
+ lcdc_win_cfgA(sf_crtc, state, win_num, 0x1, 0x0, 0x0, 0x0, 0x0);
+ lcdc_win_cfgB(sf_crtc, win_num, 0x0, 0x0, 0x0);
+ lcdc_win_cfgC(sf_crtc, win_num, 0xffffff);
+
+ lcdc_win_src_size(sf_crtc, state, win_num);
+ lcdc_alpha_val_cfg(sf_crtc, 0xf, 0xf, 0xf, 0xf, 0x0);
+ lcdc_panel_cfg(sf_crtc, 0x3, 0x4, 0x0, 0x0, 0x0, 0x1); //rgb888sel?
+}
+
+void lcdc_run(struct starfive_crtc *sf_crtc, u32 win_mode, u32 lcd_trig)
+{
+ u32 runcfg = win_mode << LCDC_EN_CFG_MODE | lcd_trig;
+
+ sf_fb_lcdcwrite32(sf_crtc, LCDC_SWITCH, runcfg);
+ dev_dbg(sf_crtc->dev, "Start run LCDC\n");
+}
+
+static int sf_fb_lcdc_clk_cfg(struct starfive_crtc *sf_crtc, struct drm_crtc_state *state)
+{
+ u32 reg_val = clk_get_rate(sf_crtc->clk_vout_src) / (state->mode.clock * HZ_PER_KHZ);
+ u32 tmp_val;
+
+ dev_dbg(sf_crtc->dev, "%s: reg_val = %u\n", __func__, reg_val);
+
+ switch (state->adjusted_mode.crtc_hdisplay) {
+ case 640:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (59 & 0x3F);
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ case 840:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (54 & 0x3F);
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ case 1024:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (30 & 0x3F);
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ case 1280:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (30 & 0x3F);
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ case 1440:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (30 & 0x3F);
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ case 1680:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (24 & 0x3F); //24 30MHZ
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ case 1920:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (10 & 0x3F); //20 30MHz , 15 40Mhz, 10 60Mhz
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ case 2048:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (10 & 0x3F);
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ break;
+ default:
+ tmp_val = sf_fb_clkread32(sf_crtc, CLK_LCDC_OCLK_CTRL);
+ tmp_val &= ~(0x3F);
+ tmp_val |= (reg_val & 0x3F);
+ sf_fb_clkwrite32(sf_crtc, CLK_LCDC_OCLK_CTRL, tmp_val);
+ }
+
+ return 0;
+}
+
+static int sf_fb_lcdc_init(struct starfive_crtc *sf_crtc, struct drm_crtc_state *state)
+{
+ int pp_id;
+ int lcd_in_pp;
+ int win_num;
+
+ pp_id = sf_crtc->pp_conn_lcdc;
+ if (pp_id < 0) {
+ dev_dbg(sf_crtc->dev, "DDR to LCDC\n");
+ lcd_in_pp = LCDC_IN_LCD_AXI;
+ win_num = lcdc_win_sel(sf_crtc, lcd_in_pp);
+ sf_crtc->win_num = win_num;
+ lcdc_config(sf_crtc, state, win_num);
+ } else {
+ dev_dbg(sf_crtc->dev, "DDR to VPP to LCDC\n");
+ lcd_in_pp = (pp_id == 0) ? LCDC_IN_VPP0 :
+ ((pp_id == 1) ? LCDC_IN_VPP1 : LCDC_IN_VPP2);
+ win_num = lcdc_win_sel(sf_crtc, lcd_in_pp);
+ sf_crtc->win_num = win_num;
+ lcdc_config(sf_crtc, state, win_num);
+ }
+
+ return 0;
+}
+
+int starfive_lcdc_enable(struct starfive_crtc *sf_crtc)
+{
+ struct drm_crtc_state *state = sf_crtc->crtc.state;
+
+ lcdc_disable_intr(sf_crtc);
+
+ if (sf_fb_lcdc_clk_cfg(sf_crtc, state)) {
+ dev_err(sf_crtc->dev, "lcdc clock configure fail\n");
+ return -EINVAL;
+ }
+
+ if (sf_fb_lcdc_init(sf_crtc, state)) {
+ dev_err(sf_crtc->dev, "lcdc init fail\n");
+ return -EINVAL;
+ }
+
+ lcdc_run(sf_crtc, sf_crtc->win_num, LCDC_RUN);
+ lcdc_enable_intr(sf_crtc);
+
+ return 0;
+}
+
+MODULE_AUTHOR("StarFive Technology Co., Ltd.");
+MODULE_DESCRIPTION("loadable LCDC driver for StarFive");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/starfive/starfive_drm_lcdc.h b/drivers/gpu/drm/starfive/starfive_drm_lcdc.h
new file mode 100644
index 000000000000..6d03ef82debe
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_lcdc.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef __SF_FB_LCDC_H__
+#define __SF_FB_LCDC_H__
+
+#include "starfive_drm_crtc.h"
+
+enum lcdc_in_mode {
+ LCDC_IN_LCD_AXI = 0,
+ LCDC_IN_VPP2,
+ LCDC_IN_VPP1,
+ LCDC_IN_VPP0,
+ LCDC_IN_MAPCONVERT,
+};
+
+enum lcdc_win_num {
+ LCDC_WIN_0 = 0,
+ LCDC_WIN_1,
+ LCDC_WIN_2,
+ LCDC_WIN_3,
+ LCDC_WIN_4,
+ LCDC_WIN_5,
+};
+
+enum WIN_FMT {
+ WIN_FMT_RGB565 = 4,
+ WIN_FMT_XRGB1555,
+ WIN_FMT_XRGB4444,
+ WIN_FMT_XRGB8888,
+};
+
+#define LCDC_STOP 0
+#define LCDC_RUN 1
+
+//lcdc registers
+#define LCDC_SWITCH 0x0000
+#define LCDC_GCTRL 0x0004
+#define LCDC_INT_STATUS 0x0008
+#define LCDC_INT_MSK 0x000C
+#define LCDC_INT_CLR 0x0010
+#define LCDC_RGB_H_TMG 0x0014
+#define LCDC_RGB_V_TMG 0x0018
+#define LCDC_RGB_W_TMG 0x001C
+#define LCDC_RGB_DCLK 0x0020
+#define LCDC_M_CS_CTRL 0x0024
+#define LCDC_DeltaRGB_CFG 0x0028
+#define LCDC_BACKGROUND 0x002C
+#define LCDC_WIN0_CFG_A 0x0030
+#define LCDC_WIN0_CFG_B 0x0034
+#define LCDC_WIN0_CFG_C 0x0038
+#define LCDC_WIN1_CFG_A 0x003C
+#define LCDC_WIN1_CFG_B 0x0040
+#define LCDC_WIN1_CFG_C 0x0044
+#define LCDC_WIN2_CFG_A 0x0048
+#define LCDC_WIN2_CFG_B 0x004C
+#define LCDC_WIN2_CFG_C 0x0050
+#define LCDC_WIN3_CFG_A 0x0054
+#define LCDC_WIN3_CFG_B 0x0058
+#define LCDC_WIN3_CFG_C 0x005C
+#define LCDC_WIN01_HSIZE 0x0090
+#define LCDC_WIN23_HSIZE 0x0094
+#define LCDC_WIN45_HSIZE 0x0098
+#define LCDC_WIN67_HSIZE 0x009C
+#define LCDC_ALPHA_VALUE 0x00A0
+#define LCDC_PANELDATAFMT 0x00A4
+#define LCDC_WIN0STARTADDR0 0x00B8
+#define LCDC_WIN0STARTADDR1 0x00BC
+
+/* Definition controller bit for LCDC registers */
+//for LCDC_SWITCH
+#define LCDC_DTRANS_SWITCH 0
+#define LCDC_MPU_START 1
+#define LCDC_EN_CFG_MODE 2
+//for LCDC_GCTRL
+#define LCDC_EN 0
+#define LCDC_WORK_MODE 1
+#define LCDC_A0_P 4
+#define LCDC_ENABLE_P 5
+#define LCDC_DOTCLK_P 6
+#define LCDC_HSYNC_P 7
+#define LCDC_VSYNC_P 8
+#define LCDC_DITHER_EN 9
+#define LCDC_R2Y_BPS 10
+#define LCDC_MS_SEL 11
+#define LCDC_TV_LCD_PATHSEL 12
+#define LCDC_INTERLACE 13
+#define LCDC_CBCR_ORDER 14
+#define LCDC_INT_SEL 15
+#define LCDC_INT_FREQ 24
+//for LCDC_INT_MSK
+#define LCDC_OUT_FRAME_END 5
+//for RGB_H_TMG,RGB_V_TMG,RGB_W_TMG
+#define LCDC_RGB_HBK 0
+#define LCDC_RGB_HFP 16
+#define LCDC_RGB_VBK 0
+#define LCDC_RGB_VFP 16
+#define LCDC_RGB_HPW 0
+#define LCDC_RGB_VPW 8
+#define LCDC_RGB_UNIT 16
+//for BACKGROUND
+#define LCDC_BG_HSIZE 0
+#define LCDC_BG_VSIZE 12
+//for WINx_CFG_A/B/C
+#define LCDC_WIN_HSIZE 0
+#define LCDC_WIN_VSIZE 12
+#define LCDC_WIN_EN 24
+#define LCDC_CC_EN 25
+#define LCDC_CK_EN 26
+#define LCDC_WIN_ISSEL 27
+#define LCDC_WIN_PM 28
+#define LCDC_WIN_CLK 30
+#define LCDC_WIN_HPOS 0
+#define LCDC_WIN_VPOS 12
+#define LCDC_WIN_FMT 24
+#define LCDC_WIN_ARGB_ORDER 27
+#define LCDC_WIN_CC 0
+//for WINxx_HSIZE
+#define LCDC_IMG_HSIZE 12
+//for LCDC_ALPHA_VALUE
+#define LCDC_ALPHA1 0
+#define LCDC_ALPHA2 4
+#define LCDC_ALPHA3 8
+#define LCDC_ALPHA4 12
+#define LCDC_A_GLBL_ALPHA 16
+#define LCDC_B_GLBL_ALPHA 17
+#define LCDC_01_ALPHA_SEL 18
+//for LCDC_PANELDATAFMT
+#define LCDC_BUS_W 0
+#define LCDC_TCYCLES 2
+#define LCDC_COLOR_DEP 4
+#define LCDC_PIXELS 7
+#define LCDC_332RGB_SEL 8
+#define LCDC_444RGB_SEL 9
+#define LCDC_666RGB_SEL 12
+#define LCDC_565RGB_SEL 16
+#define LCDC_888RGB_SEL 18
+
+//sysrst registers
+#define SRST_ASSERT0 0x00
+#define SRST_STATUS0 0x04
+/* Definition controller bit for syd rst registers */
+#define BIT_RST_DSI_DPI_PIX 17
+
+void lcdc_enable_intr(struct starfive_crtc *sf_crtc);
+void lcdc_disable_intr(struct starfive_crtc *sf_crtc);
+irqreturn_t lcdc_isr_handler(int this_irq, void *dev_id);
+void lcdc_int_cfg(struct starfive_crtc *sf_crtc, int mask);
+void lcdc_config(struct starfive_crtc *sf_crtc,
+ struct drm_crtc_state *old_state,
+ int win_num);
+int lcdc_win_sel(struct starfive_crtc *sf_crtc, enum lcdc_in_mode sel);
+void lcdc_dsi_sel(struct starfive_crtc *sf_crtc);
+void lcdc_run(struct starfive_crtc *sf_crtc,
+ u32 win_mode, u32 lcd_trig);
+void starfive_set_win_addr(struct starfive_crtc *sf_crtc, int addr);
+int starfive_lcdc_enable(struct starfive_crtc *sf_crtc);
+
+#endif
diff --git a/drivers/gpu/drm/starfive/starfive_drm_plane.c b/drivers/gpu/drm/starfive/starfive_drm_plane.c
new file mode 100644
index 000000000000..119a9fe32769
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_plane.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <drm/drm.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include "starfive_drm_crtc.h"
+#include "starfive_drm_plane.h"
+#include "starfive_drm_gem.h"
+#include "starfive_drm_lcdc.h"
+#include "starfive_drm_vpp.h"
+
+static const u32 formats[] = {
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_VYUY,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVYU,
+
+ DRM_FORMAT_YUV420,
+ DRM_FORMAT_NV21,
+ DRM_FORMAT_NV12,
+
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+};
+
+static void starfive_plane_destroy(struct drm_plane *plane)
+{
+ drm_plane_cleanup(plane);
+}
+
+static const struct drm_plane_funcs starfive_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = starfive_plane_destroy,
+ .set_property = NULL,
+ .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 void starfive_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
+{
+}
+
+static int starfive_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *new_plane_state =
+ drm_atomic_get_new_plane_state(state, plane);
+ struct drm_framebuffer *fb = new_plane_state->fb;
+ struct drm_crtc_state *crtc_state;
+
+ if (!fb)
+ return 0;
+
+ if (WARN_ON(!new_plane_state->crtc))
+ return 0;
+
+ /*
+ ret = starfive_drm_plane_check(state->crtc, plane,
+ to_starfive_plane_state(state));
+ if (ret)
+ return ret;
+ */
+
+ //crtc_state = drm_atomic_get_crtc_state(new_plane_state->state, new_plane_state->crtc);
+ crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ true, true);
+}
+
+static void starfive_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(old_state,
+ plane);
+ struct drm_crtc *crtc = new_state->crtc;
+ struct drm_framebuffer *fb = new_state->fb;
+ //struct drm_plane_state *state = plane->state;
+ //struct drm_crtc *crtc = state->crtc;
+ //struct drm_framebuffer *fb = state->fb;
+
+ dma_addr_t dma_addr;
+ struct drm_gem_object *obj;
+ struct starfive_drm_gem_obj *starfive_obj;
+ unsigned int pitch, format;
+
+ struct starfive_crtc *sf_crtc = to_starfive_crtc(crtc);
+
+ if (!crtc || WARN_ON(!fb))
+ return;
+
+ //if (!plane->state->visible) {
+ if (!new_state->visible) {
+ starfive_plane_atomic_disable(plane, old_state);
+ return;
+ }
+
+ obj = fb->obj[0];
+ starfive_obj = to_starfive_gem_obj(obj);
+ dma_addr = starfive_obj->dma_addr;
+ pitch = fb->pitches[0];
+ format = fb->format->format;
+
+ //dma_addr += (plane->state->src.x1 >> 16) * fb->format->cpp[0];
+ //dma_addr += (plane->state->src.y1 >> 16) * pitch;
+ dma_addr += (new_state->src.x1 >> 16) * fb->format->cpp[0];
+ dma_addr += (new_state->src.y1 >> 16) * pitch;
+ if (sf_crtc->ddr_format != format) {
+ sf_crtc->ddr_format = format;
+ sf_crtc->ddr_format_change = true;
+ } else {
+ sf_crtc->ddr_format_change = false;
+ }
+
+ if (sf_crtc->dma_addr != dma_addr) {
+ sf_crtc->dma_addr = dma_addr;
+ sf_crtc->dma_addr_change = true;
+ } else {
+ sf_crtc->dma_addr_change = false;
+ }
+ sf_crtc->size = obj->size;
+}
+
+static int starfive_plane_atomic_async_check(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+ plane);
+
+ if (plane != new_plane_state->crtc->cursor)
+ return -EINVAL;
+
+ if (!plane->state)
+ return -EINVAL;
+
+ if (!plane->state->fb)
+ return -EINVAL;
+
+ //if (new_plane_state->state)
+ // crtc_state = drm_atomic_get_existing_crtc_state(new_plane_state->state,
+ // new_plane_state->crtc);
+ //else /* Special case for asynchronous cursor updates. */
+ // crtc_state = new_plane_state->crtc->state;
+
+ if (state)
+ crtc_state = drm_atomic_get_existing_crtc_state(state,
+ new_plane_state->crtc);
+ else /* Special case for asynchronous cursor updates. */
+ //crtc_state = plane->crtc->state;
+ crtc_state = new_plane_state->crtc->state;
+
+ return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ true, true);
+}
+
+static void starfive_plane_atomic_async_update(struct drm_plane *plane,
+ struct drm_atomic_state *new_state)
+{
+ struct drm_plane_state *new_plane_state =
+ drm_atomic_get_new_plane_state(new_state, plane);
+ struct starfive_crtc *crtcp = to_starfive_crtc(plane->state->crtc);
+
+ plane->state->crtc_x = new_plane_state->crtc_x;
+ plane->state->crtc_y = new_plane_state->crtc_y;
+ plane->state->crtc_h = new_plane_state->crtc_h;
+ plane->state->crtc_w = new_plane_state->crtc_w;
+ plane->state->src_x = new_plane_state->src_x;
+ plane->state->src_y = new_plane_state->src_y;
+ plane->state->src_h = new_plane_state->src_h;
+ plane->state->src_w = new_plane_state->src_w;
+ swap(plane->state->fb, new_plane_state->fb);
+
+ if (crtcp->is_enabled) {
+ starfive_plane_atomic_update(plane, new_state);
+ spin_lock(&crtcp->reg_lock);
+ starfive_crtc_hw_config_simple(crtcp);
+ spin_unlock(&crtcp->reg_lock);
+ }
+}
+
+static const struct drm_plane_helper_funcs starfive_plane_helper_funcs = {
+ .atomic_check = starfive_plane_atomic_check,
+ .atomic_update = starfive_plane_atomic_update,
+ //.prepare_fb = drm_gem_fb_prepare_fb,
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_disable = starfive_plane_atomic_disable,
+ .atomic_async_check = starfive_plane_atomic_async_check,
+ .atomic_async_update = starfive_plane_atomic_async_update,
+};
+
+int starfive_plane_init(struct drm_device *dev,
+ struct starfive_crtc *starfive_crtc,
+ enum drm_plane_type type)
+{
+ int ret;
+
+ ret = drm_universal_plane_init(dev, starfive_crtc->planes, 0,
+ &starfive_plane_funcs, formats,
+ ARRAY_SIZE(formats), NULL, type, NULL);
+ if (ret) {
+ dev_err(dev->dev, "failed to initialize plane\n");
+ return ret;
+ }
+
+ drm_plane_helper_add(starfive_crtc->planes, &starfive_plane_helper_funcs);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/starfive/starfive_drm_plane.h b/drivers/gpu/drm/starfive/starfive_drm_plane.h
new file mode 100644
index 000000000000..04c3637d4fa6
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_plane.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef _STARFIVE_DRM_PLANE_H
+#define _STARFIVE_DRM_PLANE_H
+
+int starfive_plane_init(struct drm_device *dev,
+ struct starfive_crtc *starfive_crtc,
+ enum drm_plane_type type);
+
+#endif /* _STARFIVE_DRM_PLANE_H */
diff --git a/drivers/gpu/drm/starfive/starfive_drm_vpp.c b/drivers/gpu/drm/starfive/starfive_drm_vpp.c
new file mode 100644
index 000000000000..5cada21d2651
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_vpp.c
@@ -0,0 +1,806 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/reset.h>
+#include <linux/delay.h>
+#include "starfive_drm_vpp.h"
+#include "starfive_drm_crtc.h"
+#include <soc/sifive/sifive_ccache.h>
+
+static inline void sf_set_clear(void __iomem *addr, u32 reg, u32 set, u32 clear)
+{
+ u32 value = ioread32(addr + reg);
+
+ value &= ~clear;
+ value |= set;
+ iowrite32(value, addr + reg);
+}
+
+static u32 sf_fb_sysread32(struct starfive_crtc *sf_crtc, u32 reg)
+{
+ return ioread32(sf_crtc->base_syscfg + reg);
+}
+
+static void sf_fb_syswrite32(struct starfive_crtc *sf_crtc, u32 reg, u32 val)
+{
+ iowrite32(val, sf_crtc->base_syscfg + reg);
+}
+
+static u32 sf_fb_vppread32(struct starfive_crtc *sf_crtc, int pp_num, u32 reg)
+{
+ void __iomem *base_vpp;
+
+ switch (pp_num) {
+ case 0:
+ base_vpp = sf_crtc->base_vpp0;
+ break;
+ case 1:
+ base_vpp = sf_crtc->base_vpp1;
+ break;
+ case 2:
+ base_vpp = sf_crtc->base_vpp2;
+ break;
+ default:
+ dev_err(sf_crtc->dev, "Errinvalid vpp Number!\n");
+ return 0;
+ }
+
+ return ioread32(base_vpp + reg);
+}
+
+static void sf_fb_vppwrite32(struct starfive_crtc *sf_crtc, int pp_num, u32 reg, u32 val)
+{
+ void __iomem *base_vpp;
+
+ switch (pp_num) {
+ case 0:
+ base_vpp = sf_crtc->base_vpp0;
+ break;
+ case 1:
+ base_vpp = sf_crtc->base_vpp1;
+ break;
+ case 2:
+ base_vpp = sf_crtc->base_vpp2;
+ break;
+ default:
+ dev_err(sf_crtc->dev, "Errinvalid vpp Number!\n");
+ return;
+ }
+ iowrite32(val, base_vpp + reg);
+}
+
+void mapconv_pp0_sel(struct starfive_crtc *sf_crtc, int sel)
+{
+ u32 temp;
+
+ temp = sf_fb_sysread32(sf_crtc, SYS_MAP_CONV);
+ temp &= ~(0x1);
+ temp |= (sel & 0x1);
+ sf_fb_syswrite32(sf_crtc, SYS_MAP_CONV, temp);
+}
+
+static void pp_output_cfg(struct starfive_crtc *sf_crtc,
+ int pp_num, int out_sel, int prog_inter, int desformat,
+ int pt_mode)
+{
+ int cfg = out_sel | prog_inter << PP_INTERLACE |
+ desformat << PP_DES_FORMAT |
+ pt_mode << PP_POINTER_MODE;
+ int pre_cfg = sf_fb_vppread32(sf_crtc, pp_num, PP_CTRL1) & 0xffff8f0U;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_CTRL1, cfg | pre_cfg);
+ dev_dbg(sf_crtc->dev, "PP%d out_sel: %d, outFormat: 0x%x, Out Interlace: %d, pt_mode: %d\n",
+ pp_num, out_sel, desformat, prog_inter, pt_mode);
+}
+
+static void pp_srcfmt_cfg(struct starfive_crtc *sf_crtc, int pp_num, int srcformat,
+ int yuv420_inter, int yuv422_mode, int yuv420_mode, int argb_ord)
+{
+ int cfg = srcformat << PP_SRC_FORMAT_N |
+ yuv420_inter << PP_420_ITLC |
+ yuv422_mode << PP_SRC_422_YUV_POS |
+ yuv420_mode << PP_SRC_420_YUV_POS |
+ argb_ord << PP_SRC_ARGB_ORDER;
+ int pre_cfg = sf_fb_vppread32(sf_crtc, pp_num, PP_CTRL1) & 0x83ffff0fU;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_CTRL1, cfg | pre_cfg);
+ dev_dbg(sf_crtc->dev, "PP%d Src Format: 0x%x, YUV420 Interlace: %d, YUV422: %d, YUV420: %d, ARGB Order: %d\n",
+ pp_num, srcformat, yuv420_inter, yuv422_mode, yuv420_mode, argb_ord);
+}
+
+static void pp_r2yscal_bypass(struct starfive_crtc *sf_crtc,
+ int pp_num, int r2y_byp, int scal_byp, int y2r_byp)
+{
+ int bypass = (r2y_byp | scal_byp << 1 | y2r_byp << 2) << PP_R2Y_BPS;
+ int pre_cfg = sf_fb_vppread32(sf_crtc, pp_num, PP_CTRL1) & 0xffff8fffU;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_CTRL1, bypass | pre_cfg);
+ dev_dbg(sf_crtc->dev, "PP%d Bypass R2Y: %d, Y2R: %d, MainSacle: %d\n",
+ pp_num, r2y_byp, y2r_byp, scal_byp);
+}
+
+static void pp_argb_alpha(struct starfive_crtc *sf_crtc, int pp_num, int alpha)
+{
+ int pre_cfg = sf_fb_vppread32(sf_crtc, pp_num, PP_CTRL1) & 0xff00ffffU;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_CTRL1, alpha << PP_ARGB_ALPHA | pre_cfg);
+ dev_dbg(sf_crtc->dev, "PP%d Alpha: 0x%4x\n", pp_num, alpha);
+}
+
+//rgbNum: 1-3
+static void pp_r2y_coeff(struct starfive_crtc *sf_crtc,
+ int pp_num, int coef_num, int rcoef, int gcoef, int bcoef, int off)
+{
+ int rgcoeff = rcoef | gcoef << PP_COEF_G1;
+ int bcoefoff = bcoef | off << PP_OFFSET_1;
+ u32 addr1 = (coef_num - 1) * 0x8 + PP_R2Y_COEF1;
+ u32 addr2 = (coef_num - 1) * 0x8 + PP_R2Y_COEF2;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, addr1, rgcoeff);
+ sf_fb_vppwrite32(sf_crtc, pp_num, addr2, bcoefoff);
+ dev_dbg(sf_crtc->dev, "PP%d coef_num: %d, rCoef: 0x%4x, gCoef: 0x%4x, bCoef: 0x%4x, off: 0x%4x\n",
+ pp_num, coef_num, rcoef, gcoef, bcoef, off);
+}
+
+static void pp_output_fmt_cfg(struct starfive_crtc *sf_crtc,
+ int pp_num, int yuv420_inter, int yuv422_mode)
+{
+ int pre_cfg = sf_fb_vppread32(sf_crtc, pp_num, PP_CTRL2) & 0xfffffffeU;
+
+ pre_cfg = pre_cfg |
+ yuv420_inter << PP_DES_420_ORDER |
+ yuv422_mode << PP_DES_422_ORDER;
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_CTRL2, pre_cfg);
+ dev_dbg(sf_crtc->dev, "PP%d Lock Transfer: %d\n", pp_num, yuv422_mode);
+}
+
+static void pp_lock_trans_cfg(struct starfive_crtc *sf_crtc, int pp_num, int lock_trans)
+{
+ int pre_cfg = sf_fb_vppread32(sf_crtc, pp_num, PP_CTRL2) & 0xfffffffeU;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_CTRL2, lock_trans | pre_cfg);
+ dev_dbg(sf_crtc->dev, "PP%d Lock Transfer: %d\n", pp_num, lock_trans);
+}
+
+static void pp_int_interval_cfg(struct starfive_crtc *sf_crtc, int pp_num, int interval)
+{
+ int pre_cfg = sf_fb_vppread32(sf_crtc, pp_num, PP_CTRL2) & 0xffff00ffU;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_CTRL2, interval << PP_INT_INTERVAL | pre_cfg);
+ dev_dbg(sf_crtc->dev, "PP%d Frame Interrupt interval: %d Frames\n", pp_num, interval);
+}
+
+static void pp_src_size_cfg(struct starfive_crtc *sf_crtc, int pp_num, int hsize, int vsize)
+{
+ int size = hsize | vsize << PP_SRC_VSIZE;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SRC_SIZE, size);
+ dev_dbg(sf_crtc->dev, "PP%d HSize: %d, VSize: %d\n", pp_num, hsize, vsize);
+}
+
+//0-no drop, 1-1/2, 2-1/4, down to 1/32
+static void pp_drop_cfg(struct starfive_crtc *sf_crtc, int pp_num, int hdrop, int vdrop)
+{
+ int drop = hdrop | vdrop << PP_DROP_VRATION;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DROP_CTRL, drop);
+ dev_dbg(sf_crtc->dev, "PP%d HDrop: %d, VDrop: %d\n", pp_num, hdrop, vdrop);
+}
+
+static void pp_des_size_cfg(struct starfive_crtc *sf_crtc, int pp_num, int hsize, int vsize)
+{
+ int size = hsize | vsize << PP_DES_VSIZE;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DES_SIZE, size);
+ dev_dbg(sf_crtc->dev, "PP%d HSize: %d, VSize: %d\n", pp_num, hsize, vsize);
+}
+
+static void pp_des_addr_cfg(struct starfive_crtc *sf_crtc,
+ int pp_num, int yaddr, int uaddr, int vaddr)
+{
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DES_Y_SA, yaddr);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DES_U_SA, uaddr);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DES_V_SA, vaddr);
+ dev_dbg(sf_crtc->dev, "PP%d des-Addr Y: 0x%8x, U: 0x%8x, V: 0x%8x\n",
+ pp_num, yaddr, uaddr, vaddr);
+}
+
+static void pp_des_offset_cfg(struct starfive_crtc *sf_crtc,
+ int pp_num, int yoff, int uoff, int voff)
+{
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DES_Y_OFS, yoff);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DES_U_OFS, uoff);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_DES_V_OFS, voff);
+ dev_dbg(sf_crtc->dev, "PP%d des-Offset Y: 0x%4x, U: 0x%4x, V: 0x%4x\n",
+ pp_num, yoff, uoff, voff);
+}
+
+void pp_intcfg(struct starfive_crtc *sf_crtc, int pp_num, int int_mask)
+{
+ int intcfg = ~(0x1 << 0);
+
+ if (int_mask)
+ intcfg = 0xf;
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_INT_MASK, intcfg);
+}
+
+//next source frame Y/RGB start address, ?
+void pp_src_addr_next(struct starfive_crtc *sf_crtc, int pp_num, int ysa, int usa, int vsa)
+{
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SRC_Y_SA_NXT, ysa);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SRC_U_SA_NXT, usa);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SRC_V_SA_NXT, vsa);
+ dev_dbg(sf_crtc->dev,
+ "PP%d next Y startAddr: 0x%8x, U startAddr: 0x%8x, V startAddr: 0x%8x\n",
+ pp_num, ysa, usa, vsa);
+}
+
+void pp_src_offset_cfg(struct starfive_crtc *sf_crtc, int pp_num, int yoff, int uoff, int voff)
+{
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SRC_Y_OFS, yoff);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SRC_U_OFS, uoff);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SRC_V_OFS, voff);
+ dev_dbg(sf_crtc->dev, "PP%d src-Offset Y: 0x%4x, U: 0x%4x, V: 0x%4x\n",
+ pp_num, yoff, uoff, voff);
+}
+
+void pp_nxt_addr_load(struct starfive_crtc *sf_crtc, int pp_num, int nxt_par, int nxt_pos)
+{
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_LOAD_NXT_PAR, nxt_par | nxt_pos);
+ dev_dbg(sf_crtc->dev, "PP%d next addrPointer: %d, %d set Regs\n", pp_num, nxt_par, nxt_pos);
+}
+
+void pp_run(struct starfive_crtc *sf_crtc, int pp_num, int start)
+{
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_SWITCH, start);
+ //if (start)
+ // dev_dbg(sf_crtc->dev, "Now start the PP%d\n\n", pp_num);
+}
+
+void pp1_enable_intr(struct starfive_crtc *sf_crtc)
+{
+ sf_fb_vppwrite32(sf_crtc, 1, PP_INT_MASK, 0x0);
+}
+
+void pp_enable_intr(struct starfive_crtc *sf_crtc, int pp_num)
+{
+ u32 cfg = 0xfffe;
+
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_INT_MASK, cfg);
+}
+
+void pp_disable_intr(struct starfive_crtc *sf_crtc, int pp_num)
+{
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_INT_MASK, 0xf);
+ sf_fb_vppwrite32(sf_crtc, pp_num, PP_INT_CLR, 0xf);
+}
+
+static void pp_srcfmt_set(struct starfive_crtc *sf_crtc, int pp_num, struct pp_video_mode *src)
+{
+ switch (src->format) {
+ case COLOR_YUV422_YVYU:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_YUV422, 0x0, COLOR_YUV422_YVYU, 0x0, 0x0);
+ break;
+ case COLOR_YUV422_VYUY:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_YUV422, 0x0, COLOR_YUV422_VYUY, 0x0, 0x0);
+ break;
+ case COLOR_YUV422_YUYV:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_YUV422, 0x0, COLOR_YUV422_YUYV, 0x0, 0x0);
+ break;
+ case COLOR_YUV422_UYVY:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_YUV422, 0x0, COLOR_YUV422_UYVY, 0x0, 0x0);
+ break;
+ case COLOR_YUV420P:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_YUV420P, 0x0, 0, 0x0, 0x0);
+ break;
+ case COLOR_YUV420_NV21:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_YUV420I, 0x1, 0,
+ COLOR_YUV420_NV21 - COLOR_YUV420_NV21, 0x0);
+ break;
+ case COLOR_YUV420_NV12:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_YUV420I, 0x1, 0,
+ COLOR_YUV420_NV12 - COLOR_YUV420_NV21, 0x0);
+ break;
+ case COLOR_RGB888_ARGB:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_GRB888, 0x0, 0x0,
+ 0x0, COLOR_RGB888_ARGB - COLOR_RGB888_ARGB);
+ break;
+ case COLOR_RGB888_ABGR:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_GRB888, 0x0, 0x0,
+ 0x0, COLOR_RGB888_ABGR - COLOR_RGB888_ARGB);
+ break;
+ case COLOR_RGB888_RGBA:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_GRB888, 0x0, 0x0,
+ 0x0, COLOR_RGB888_RGBA - COLOR_RGB888_ARGB);
+ break;
+ case COLOR_RGB888_BGRA:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_GRB888, 0x0, 0x0,
+ 0x0, COLOR_RGB888_BGRA - COLOR_RGB888_ARGB);
+ break;
+ case COLOR_RGB565:
+ pp_srcfmt_cfg(sf_crtc, pp_num, PP_SRC_RGB565, 0x0, 0x0, 0x0, 0x0);
+ break;
+ }
+}
+
+static void pp_dstfmt_set(struct starfive_crtc *sf_crtc, int pp_num, struct pp_video_mode *dst)
+{
+ unsigned int outsel = 1;
+
+ if (dst->addr)
+ outsel = 0;
+
+ switch (dst->format) {
+ case COLOR_YUV422_YVYU:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_YUV422, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, COLOR_YUV422_UYVY - COLOR_YUV422_YVYU);
+ break;
+ case COLOR_YUV422_VYUY:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_YUV422, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, COLOR_YUV422_UYVY - COLOR_YUV422_VYUY);
+ break;
+ case COLOR_YUV422_YUYV:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_YUV422, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, COLOR_YUV422_UYVY - COLOR_YUV422_YUYV);
+ break;
+ case COLOR_YUV422_UYVY:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_YUV422, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, COLOR_YUV422_UYVY - COLOR_YUV422_YVYU);
+ break;
+ case COLOR_YUV420P:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_YUV420P, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, 0);
+ break;
+ case COLOR_YUV420_NV21:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_YUV420I, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, COLOR_YUV420_NV21 - COLOR_YUV420_NV21, 0);
+ break;
+ case COLOR_YUV420_NV12:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_YUV420I, 0x0);///0x2, 0x0);
+ //pp_output_fmt_cfg(pp_num, COLOR_YUV420_NV12 - COLOR_YUV420_NV21, 0);
+ break;
+ case COLOR_RGB888_ARGB:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_ARGB888, 0x0);
+ //pp_output_fmt_cfg(pp_num, 0, 0);
+ break;
+ case COLOR_RGB888_ABGR:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_ABGR888, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, 0);
+ break;
+ case COLOR_RGB888_RGBA:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_RGBA888, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, 0);
+ break;
+ case COLOR_RGB888_BGRA:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_BGRA888, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, 0);
+ break;
+ case COLOR_RGB565:
+ pp_output_cfg(sf_crtc, pp_num, outsel, 0x0, PP_DST_RGB565, 0x0);
+ pp_output_fmt_cfg(sf_crtc, pp_num, 0, 0);
+ break;
+ }
+}
+
+static void pp_format_set(struct starfive_crtc *sf_crtc, int pp_num,
+ struct pp_video_mode *src, struct pp_video_mode *dst)
+{
+ /* 1:bypass, 0:not bypass */
+ unsigned int scale_byp = 1;
+
+ pp_srcfmt_set(sf_crtc, pp_num, src);
+ pp_dstfmt_set(sf_crtc, pp_num, dst);
+
+ if (src->height != dst->height || src->width != dst->width)
+ scale_byp = 0;
+
+ if (src->format >= COLOR_RGB888_ARGB && dst->format <= COLOR_YUV420_NV12) {
+ /* rgb -> yuv-420 */
+ pp_r2yscal_bypass(sf_crtc, pp_num, NOT_BYPASS, scale_byp, BYPASS);
+ pp_r2y_coeff(sf_crtc, pp_num, 1, R2Y_COEF_R1, R2Y_COEF_G1,
+ R2Y_COEF_B1, R2Y_OFFSET1);
+ pp_r2y_coeff(sf_crtc, pp_num, 2, R2Y_COEF_R2, R2Y_COEF_G2,
+ R2Y_COEF_B2, R2Y_OFFSET2);
+ pp_r2y_coeff(sf_crtc, pp_num, 3, R2Y_COEF_R3, R2Y_COEF_G3,
+ R2Y_COEF_B3, R2Y_OFFSET3);
+ } else if (src->format <= COLOR_YUV420_NV12 && dst->format >= COLOR_RGB888_ARGB) {
+ /* yuv-420 -> rgb */
+ pp_r2yscal_bypass(sf_crtc, pp_num, BYPASS, scale_byp, NOT_BYPASS);
+ } else if (src->format <= COLOR_YUV422_YVYU && dst->format <= COLOR_YUV420_NV12) {
+ /* yuv422 -> yuv420 */
+ pp_r2yscal_bypass(sf_crtc, pp_num, BYPASS, scale_byp, BYPASS);
+ } else {
+ /* rgb565->argb888 */
+ pp_r2yscal_bypass(sf_crtc, pp_num, BYPASS, scale_byp, BYPASS);
+ } //else if ((src->format >= COLOR_RGB888_ARGB) && (dst->format >= COLOR_RGB888_ARGB)) {
+ /* rgb -> rgb */
+ // pp_r2yscal_bypass(pp_num, BYPASS, scale_byp, BYPASS);
+ //}
+ pp_argb_alpha(sf_crtc, pp_num, 0xff);
+
+ if (dst->addr)
+ pp_lock_trans_cfg(sf_crtc, pp_num, SYS_BUS_OUTPUT);
+ else
+ pp_lock_trans_cfg(sf_crtc, pp_num, FIFO_OUTPUT);
+
+ pp_int_interval_cfg(sf_crtc, pp_num, 0x1);
+}
+
+static void pp_size_set(struct starfive_crtc *sf_crtc, int pp_num,
+ struct pp_video_mode *src, struct pp_video_mode *dst)
+{
+ u32 src_addr, dstaddr;
+ unsigned int size, y_rgb_ofst, uofst;
+ unsigned int v_uvofst = 0, next_y_rgb_addr = 0, next_u_addr = 0, next_v_addr = 0;
+ unsigned int i = 0;
+
+ pp_src_size_cfg(sf_crtc, pp_num, src->width - 1, src->height - 1);
+ pp_drop_cfg(sf_crtc, pp_num, 0x0, 0x0);///0:no drop
+ pp_des_size_cfg(sf_crtc, pp_num, dst->width - 1, dst->height - 1);
+
+ src_addr = src->addr + (i << 30); //PP_SRC_BASE_ADDR + (i << 30);
+ size = src->width * src->height;
+
+ if (src->format >= COLOR_RGB888_ARGB) {
+ next_y_rgb_addr = src_addr;
+ next_u_addr = 0;
+ next_v_addr = 0;
+
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ //pp_src_addr_next(pp_num, src_addr, 0, 0);
+ //pp_src_offset_cfg(pp_num, 0x0, 0x0, 0x0);
+ } else {
+ if (src->format == COLOR_YUV420_NV21) { //ok
+ next_y_rgb_addr = src_addr;
+ next_u_addr = src_addr + size + 1;
+ next_v_addr = src_addr + size;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = size;
+ } else if (src->format == COLOR_YUV420_NV12) {
+ next_y_rgb_addr = src_addr;
+ next_u_addr = src_addr + size;
+ next_v_addr = src_addr + size + 1;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = size;
+ } else if (src->format == COLOR_YUV420P) {
+ next_y_rgb_addr = src_addr;
+ next_u_addr = src_addr + size;
+ next_v_addr = src_addr + size * 5 / 4;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (src->format == COLOR_YUV422_YVYU) { //ok
+ next_y_rgb_addr = src_addr;
+ next_u_addr = src_addr + 1;
+ next_v_addr = src_addr + 3;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (src->format == COLOR_YUV422_VYUY) { //ok
+ next_y_rgb_addr = src_addr + 1;
+ next_u_addr = src_addr + 2;
+ next_v_addr = src_addr;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (src->format == COLOR_YUV422_YUYV) { //ok
+ next_y_rgb_addr = src_addr;
+ next_u_addr = src_addr + 1;
+ next_v_addr = src_addr + 2;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (src->format == COLOR_YUV422_UYVY) { //ok
+ next_y_rgb_addr = src_addr + 1;
+ next_u_addr = src_addr;
+ next_v_addr = src_addr + 2;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ }
+ }
+ pp_src_addr_next(sf_crtc, pp_num, next_y_rgb_addr, next_u_addr, next_v_addr);
+ pp_src_offset_cfg(sf_crtc, pp_num, y_rgb_ofst, uofst, v_uvofst);
+ /* source addr not change */
+ pp_nxt_addr_load(sf_crtc, pp_num, 0x1, (i & 0x1));
+
+ if (dst->addr) {
+ dstaddr = dst->addr;
+ size = dst->height * dst->width;
+ if (dst->format >= COLOR_RGB888_ARGB) {
+ next_y_rgb_addr = dstaddr;
+ next_u_addr = 0;
+ next_v_addr = 0;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else {
+ if (dst->format == COLOR_YUV420_NV21) {
+ /* yyyyvuvuvu */
+ next_y_rgb_addr = dstaddr;
+ next_u_addr = dstaddr + size;
+ next_v_addr = 0;//dstaddr + size;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (dst->format == COLOR_YUV420_NV12) {
+ /* yyyyuvuvuv */
+ next_y_rgb_addr = dstaddr;
+ next_u_addr = dstaddr + size;
+ next_v_addr = dstaddr + size + 1;
+ y_rgb_ofst = 0;
+ uofst = size;
+ v_uvofst = 0;
+ } else if (dst->format == COLOR_YUV420P) {
+ next_y_rgb_addr = dstaddr;
+ next_u_addr = dstaddr + size;
+ next_v_addr = dstaddr + size * 5 / 4;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (dst->format == COLOR_YUV422_YVYU) {
+ next_y_rgb_addr = dstaddr;
+ next_u_addr = dstaddr + 1;
+ next_v_addr = dstaddr + 3;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (dst->format == COLOR_YUV422_VYUY) {
+ next_y_rgb_addr = dstaddr + 1;
+ next_u_addr = dstaddr + 2;
+ next_v_addr = dstaddr;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (dst->format == COLOR_YUV422_YUYV) {
+ next_y_rgb_addr = dstaddr;
+ next_u_addr = dstaddr + 1;
+ next_v_addr = dstaddr + 2;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ } else if (dst->format == COLOR_YUV422_UYVY) {
+ next_y_rgb_addr = dstaddr + 1;
+ next_u_addr = dstaddr;
+ next_v_addr = dstaddr + 2;
+ y_rgb_ofst = 0;
+ uofst = 0;
+ v_uvofst = 0;
+ }
+ }
+ pp_des_addr_cfg(sf_crtc, pp_num, next_y_rgb_addr, next_u_addr, next_v_addr);
+ pp_des_offset_cfg(sf_crtc, pp_num, y_rgb_ofst, uofst, v_uvofst);
+ }
+}
+
+static void pp_config(struct starfive_crtc *sf_crtc, int pp_num,
+ struct pp_video_mode *src, struct pp_video_mode *dst)
+{
+ //pp_disable_intr(sf_dev, pp_num);
+ pp_format_set(sf_crtc, pp_num, src, dst);
+ pp_size_set(sf_crtc, pp_num, src, dst);
+}
+
+irqreturn_t vpp1_isr_handler(int this_irq, void *dev_id)
+{
+ struct starfive_crtc *sf_crtc = dev_id;
+
+ sf_fb_vppread32(sf_crtc, 1, PP_INT_STATUS);
+ sf_fb_vppwrite32(sf_crtc, 1, PP_INT_CLR, 0xf);
+ sifive_ccache_flush_range(sf_crtc->dma_addr, sf_crtc->size);
+
+ return IRQ_HANDLED;
+}
+
+static void starfive_pp_enable_intr(struct starfive_crtc *sf_crtc, int enable)
+{
+ int pp_id;
+
+ for (pp_id = 0; pp_id < PP_NUM; pp_id++) {
+ if (sf_crtc->pp[pp_id].inited == 1) {
+ if (enable)
+ pp_enable_intr(sf_crtc, pp_id);
+ else
+ pp_disable_intr(sf_crtc, pp_id);
+ }
+ }
+}
+
+static int starfive_pp_video_mode_init(struct starfive_crtc *sf_crtc,
+ struct pp_video_mode *src,
+ struct pp_video_mode *dst,
+ int pp_id)
+{
+ if (!src || !dst) {
+ dev_err(sf_crtc->dev, "Invalid argument!\n");
+ return -EINVAL;
+ }
+
+ if (pp_id < PP_NUM && pp_id >= 0) {
+ src->format = sf_crtc->vpp_format;
+ src->width = sf_crtc->crtc.state->adjusted_mode.hdisplay;
+ src->height = sf_crtc->crtc.state->adjusted_mode.vdisplay;
+ src->addr = sf_crtc->dma_addr;
+ //src->addr = 0xa0000000;
+ dst->format = sf_crtc->pp[pp_id].dst.format;
+ dst->width = sf_crtc->crtc.state->adjusted_mode.hdisplay;
+ dst->height = sf_crtc->crtc.state->adjusted_mode.vdisplay;
+ if (sf_crtc->pp[pp_id].bus_out) /*out to ddr*/
+ dst->addr = 0xfc000000;
+ else if (sf_crtc->pp[pp_id].fifo_out) /*out to lcdc*/
+ dst->addr = 0;
+ } else {
+ dev_err(sf_crtc->dev, "pp_id %d is not support\n", pp_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int starfive_pp_init(struct starfive_crtc *sf_crtc)
+{
+ int pp_id;
+ int ret = 0;
+ struct pp_video_mode src, dst;
+
+ for (pp_id = 0; pp_id < PP_NUM; pp_id++) {
+ if (sf_crtc->pp[pp_id].inited == 1) {
+ ret = starfive_pp_video_mode_init(sf_crtc, &src, &dst, pp_id);
+ if (!ret)
+ pp_config(sf_crtc, pp_id, &src, &dst);
+ }
+ }
+
+ return ret;
+}
+
+static int starfive_pp_run(struct starfive_crtc *sf_crtc)
+{
+ int pp_id;
+ int ret = 0;
+
+ for (pp_id = 0; pp_id < PP_NUM; pp_id++) {
+ if (sf_crtc->pp[pp_id].inited == 1)
+ pp_run(sf_crtc, pp_id, PP_RUN);
+ }
+
+ return ret;
+}
+
+int starfive_pp_enable(struct starfive_crtc *sf_crtc)
+{
+ starfive_pp_enable_intr(sf_crtc, PP_INTR_DISABLE);
+
+ if (starfive_pp_init(sf_crtc))
+ return -ENODEV;
+
+ starfive_pp_run(sf_crtc);
+ starfive_pp_enable_intr(sf_crtc, PP_INTR_ENABLE);
+
+ return 0;
+}
+
+int starfive_pp_update(struct starfive_crtc *sf_crtc)
+{
+ int pp_id;
+ int ret = 0;
+ struct pp_video_mode src, dst;
+
+ for (pp_id = 0; pp_id < PP_NUM; pp_id++) {
+ if (sf_crtc->pp[pp_id].inited == 1) {
+ ret = starfive_pp_video_mode_init(sf_crtc, &src, &dst, pp_id);
+ if (!ret) {
+ if (sf_crtc->ddr_format_change)
+ pp_format_set(sf_crtc, pp_id, &src, &dst);
+
+ if (sf_crtc->dma_addr_change)
+ pp_size_set(sf_crtc, pp_id, &src, &dst);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int starfive_pp_get_2lcdc_id(struct starfive_crtc *sf_crtc)
+{
+ int pp_id;
+
+ for (pp_id = 0; pp_id < PP_NUM; pp_id++) {
+ if (sf_crtc->pp[pp_id].inited == 1) {
+ if (sf_crtc->pp[pp_id].fifo_out == 1 && !sf_crtc->pp[pp_id].bus_out)
+ return pp_id;
+ }
+ }
+
+ if (pp_id == PP_NUM - 1)
+ dev_warn(sf_crtc->dev, "NO pp connect to LCDC\n");
+
+ return -ENODEV;
+}
+
+void dsitx_vout_init(struct starfive_crtc *sf_crtc)
+{
+ u32 temp;
+
+ reset_control_assert(sf_crtc->rst_vout_src);
+ reset_control_assert(sf_crtc->rst_disp_axi);
+ clk_prepare_enable(sf_crtc->clk_disp_axi);
+ clk_prepare_enable(sf_crtc->clk_vout_src);
+ reset_control_deassert(sf_crtc->rst_vout_src);
+ reset_control_deassert(sf_crtc->rst_disp_axi);
+
+ sf_set_clear(sf_crtc->base_clk, clk_disp0_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_disp1_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_lcdc_oclk_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_lcdc_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_vpp0_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_vpp1_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_vpp2_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_ppi_tx_esc_clk_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_dsi_apb_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_dsi_sys_clk_ctrl_REG, BIT(31), BIT(31));
+
+ sf_set_clear(sf_crtc->base_rst, vout_rstgen_assert0_REG, ~0x1981ec, 0x1981ec);
+
+ do {
+ temp = ioread32(sf_crtc->base_rst + vout_rstgen_status0_REG);
+ temp &= 0x1981ec;
+ } while (temp != 0x1981ec);
+}
+
+void vout_reset(struct starfive_crtc *sf_crtc)
+{
+ u32 temp;
+
+ iowrite32(0xFFFFFFFF, sf_crtc->base_rst);
+
+ clk_prepare_enable(sf_crtc->clk_disp_axi);
+ clk_prepare_enable(sf_crtc->clk_vout_src);
+ reset_control_deassert(sf_crtc->rst_vout_src);
+ reset_control_deassert(sf_crtc->rst_disp_axi);
+
+ sf_set_clear(sf_crtc->base_clk, clk_disp0_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_disp1_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_lcdc_oclk_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_lcdc_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_vpp0_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_vpp1_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_vpp2_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_mapconv_apb_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_mapconv_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_pixrawout_apb_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_pixrawout_axi_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_csi2tx_strm0_apb_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_csi2tx_strm0_pixclk_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_ppi_tx_esc_clk_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_dsi_apb_ctrl_REG, BIT(31), BIT(31));
+ sf_set_clear(sf_crtc->base_clk, clk_dsi_sys_clk_ctrl_REG, BIT(31), BIT(31));
+
+ sf_set_clear(sf_crtc->base_rst, vout_rstgen_assert0_REG, ~0x19bfff, 0x19bfff);
+ do {
+ temp = ioread32(sf_crtc->base_rst + vout_rstgen_status0_REG);
+ temp &= 0x19bfff;
+ } while (temp != 0x19bfff);
+}
+
+void vout_disable(struct starfive_crtc *sf_crtc)
+{
+ iowrite32(0xFFFFFFFF, sf_crtc->base_rst);
+
+ clk_disable_unprepare(sf_crtc->clk_disp_axi);
+ clk_disable_unprepare(sf_crtc->clk_vout_src);
+ reset_control_assert(sf_crtc->rst_vout_src);
+ reset_control_assert(sf_crtc->rst_disp_axi);
+}
+
+MODULE_AUTHOR("StarFive Technology Co., Ltd.");
+MODULE_DESCRIPTION("loadable VPP driver for StarFive");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/starfive/starfive_drm_vpp.h b/drivers/gpu/drm/starfive/starfive_drm_vpp.h
new file mode 100644
index 000000000000..12a1928987c3
--- /dev/null
+++ b/drivers/gpu/drm/starfive/starfive_drm_vpp.h
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef __SF_FB_VPP_H__
+#define __SF_FB_VPP_H__
+
+#include "starfive_drm_crtc.h"
+
+#define PP_ID_0 0
+#define PP_ID_1 1
+#define PP_ID_2 2
+
+#define PP_NUM 3
+
+#define PP_STOP 0
+#define PP_RUN 1
+
+#define PP_INTR_ENABLE 1
+#define PP_INTR_DISABLE 0
+//PP coefficients
+#define R2Y_COEF_R1 77
+#define R2Y_COEF_G1 150
+#define R2Y_COEF_B1 29
+#define R2Y_OFFSET1 0
+#define R2Y_COEF_R2 (0x400 | 43)
+#define R2Y_COEF_G2 (0x400 | 85)
+#define R2Y_COEF_B2 128
+#define R2Y_OFFSET2 128
+#define R2Y_COEF_R3 128
+#define R2Y_COEF_G3 (0x400 | 107)
+#define R2Y_COEF_B3 (0x400 | 21)
+#define R2Y_OFFSET3 128
+
+//sys registers
+#define SYS_CONF_LCDC 0x00
+#define SYS_CONF_PP 0x04
+#define SYS_MAP_CONV 0x08
+
+//vout clk registers
+#define CLK_LCDC_OCLK_CTRL 0x14
+
+enum PP_LCD_PATH {
+ SYS_BUS_OUTPUT = 0,
+ FIFO_OUTPUT = 1,
+};
+
+enum PP_COLOR_CONVERT_SCALE {
+ NOT_BYPASS = 0,
+ BYPASS,
+};
+
+enum PP_SRC_FORMAT {
+ PP_SRC_YUV420P = 0,
+ PP_SRC_YUV422,
+ PP_SRC_YUV420I,
+ PP_RESERVED,
+ PP_SRC_GRB888,
+ PP_SRC_RGB565,
+};
+
+enum PP_DST_FORMAT {
+ PP_DST_YUV420P = 0,
+ PP_DST_YUV422,
+ PP_DST_YUV420I,
+ PP_DST_RGBA888,
+ PP_DST_ARGB888,
+ PP_DST_RGB565,
+ PP_DST_ABGR888,
+ PP_DST_BGRA888,
+};
+
+struct pp_video_mode {
+ enum COLOR_FORMAT format;
+ unsigned int height;
+ unsigned int width;
+ unsigned int addr;
+};
+
+struct pp_mode {
+ char pp_id;
+ bool bus_out; /*out to ddr*/
+ bool fifo_out; /*out to lcdc*/
+ bool inited;
+ struct pp_video_mode src;
+ struct pp_video_mode dst;
+};
+
+//vpp registers
+#define PP_SWITCH 0x0000
+#define PP_CTRL1 0x0004
+#define PP_CTRL2 0x0008
+#define PP_SRC_SIZE 0x000C
+#define PP_DROP_CTRL 0x0010
+#define PP_DES_SIZE 0x0014
+#define PP_Scale_Hratio 0x0018
+#define PP_Scale_Vratio 0x001C
+#define PP_Scale_limit 0x0020
+#define PP_SRC_Y_SA_NXT 0x0024
+#define PP_SRC_U_SA_NXT 0x0028
+#define PP_SRC_V_SA_NXT 0x002c
+#define PP_LOAD_NXT_PAR 0x0030
+#define PP_SRC_Y_SA0 0x0034
+#define PP_SRC_U_SA0 0x0038
+#define PP_SRC_V_SA0 0x003c
+#define PP_SRC_Y_OFS 0x0040
+#define PP_SRC_U_OFS 0x0044
+#define PP_SRC_V_OFS 0x0048
+#define PP_SRC_Y_SA1 0x004C
+#define PP_SRC_U_SA1 0x0050
+#define PP_SRC_V_SA1 0x0054
+#define PP_DES_Y_SA 0x0058
+#define PP_DES_U_SA 0x005C
+#define PP_DES_V_SA 0x0060
+#define PP_DES_Y_OFS 0x0064
+#define PP_DES_U_OFS 0x0068
+#define PP_DES_V_OFS 0x006C
+#define PP_INT_STATUS 0x0070
+#define PP_INT_MASK 0x0074
+#define PP_INT_CLR 0x0078
+#define PP_R2Y_COEF1 0x007C
+#define PP_R2Y_COEF2 0x0080
+
+/* Definition controller bit for LCDC registers */
+//for PP_SWITCH
+#define PP_TRIG 0
+//for PP_CTRL1
+#define PP_LCDPATH_EN 0
+#define PP_INTERLACE 1
+#define PP_POINTER_MODE 2
+#define PP_SRC_FORMAT_N 4
+#define PP_420_ITLC 7
+#define PP_DES_FORMAT 8
+#define PP_R2Y_BPS 12
+#define PP_MSCALE_BPS 13
+#define PP_Y2R_BPS 14
+#define PP_ARGB_ALPHA 16
+#define PP_UV_IN_ADD_128 24
+#define PP_UV_OUT_ADD_128 25
+#define PP_SRC_422_YUV_POS 26
+#define PP_SRC_420_YUV_POS 28
+#define PP_SRC_ARGB_ORDER 29
+//for PP_CTRL2
+#define PP_LOCK_EN 0
+#define PP_INT_INTERVAL 8
+#define PP_DES_422_ORDER 16
+#define PP_DES_420_ORDER 18
+//for PP_SRC_SIZE
+#define PP_SRC_HSIZE 0
+#define PP_SRC_VSIZE 16
+//for PP_DROP_CTRL
+#define PP_DROP_HRATION 0
+#define PP_DROP_VRATION 4
+//for PP_DES_SIZE
+#define PP_DES_HSIZE 0
+#define PP_DES_VSIZE 16
+//for PP_R2Y_COEF1
+#define PP_COEF_R1 0
+#define PP_COEF_G1 16
+//for PP_R2Y_COEF2
+#define PP_COEF_B1 0
+#define PP_OFFSET_1 16
+
+#define vout_rstgen_assert0_REG 0x0
+#define vout_rstgen_status0_REG 0x4
+#define clk_vout_apb_ctrl_REG 0x0
+#define clk_mapconv_apb_ctrl_REG 0x4
+#define clk_mapconv_axi_ctrl_REG 0x8
+#define clk_disp0_axi_ctrl_REG 0xC
+#define clk_disp1_axi_ctrl_REG 0x10
+#define clk_lcdc_oclk_ctrl_REG 0x14
+#define clk_lcdc_axi_ctrl_REG 0x18
+#define clk_vpp0_axi_ctrl_REG 0x1C
+#define clk_vpp1_axi_ctrl_REG 0x20
+#define clk_vpp2_axi_ctrl_REG 0x24
+#define clk_pixrawout_apb_ctrl_REG 0x28
+#define clk_pixrawout_axi_ctrl_REG 0x2C
+#define clk_csi2tx_strm0_pixclk_ctrl_REG 0x30
+#define clk_csi2tx_strm0_apb_ctrl_REG 0x34
+#define clk_dsi_sys_clk_ctrl_REG 0x38
+#define clk_dsi_apb_ctrl_REG 0x3C
+#define clk_ppi_tx_esc_clk_ctrl_REG 0x40
+
+void mapconv_pp0_sel(struct starfive_crtc *sf_crtc, int sel);
+void pp_src_addr_next(struct starfive_crtc *sf_crtc, int pp_num, int ysa, int usa, int vsa);
+void pp_src_offset_cfg(struct starfive_crtc *sf_crtc, int pp_num, int yoff, int uoff, int voff);
+void pp_nxt_addr_load(struct starfive_crtc *sf_crtc, int pp_num, int nxt_par, int nxt_pos);
+void pp_intcfg(struct starfive_crtc *sf_crtc, int pp_num, int int_mask);
+irqreturn_t vpp1_isr_handler(int this_irq, void *dev_id);
+void pp1_enable_intr(struct starfive_crtc *sf_crtc);
+void pp_enable_intr(struct starfive_crtc *sf_crtc, int pp_num);
+void pp_disable_intr(struct starfive_crtc *sf_crtc, int pp_num);
+void pp_run(struct starfive_crtc *sf_crtc, int pp_num, int start);
+int starfive_pp_enable(struct starfive_crtc *sf_crtc);
+int starfive_pp_get_2lcdc_id(struct starfive_crtc *sf_crtc);
+int starfive_pp_update(struct starfive_crtc *sf_crtc);
+void vout_disable(struct starfive_crtc *sf_crtc);
+void vout_reset(struct starfive_crtc *sf_crtc);
+void dsitx_vout_init(struct starfive_crtc *sf_crtc);
+
+#endif
--
Armbian