diff options
author | sw.multimedia <sw.multimedia@starfivetech.com> | 2021-08-31 11:48:57 +0300 |
---|---|---|
committer | Emil Renner Berthing <emil.renner.berthing@canonical.com> | 2024-05-28 13:42:34 +0300 |
commit | 3ed9e53c6cebc27ef8e61fa2c431b4337b7dea3a (patch) | |
tree | 8231f47f7d8a970f71fa508d195f55058b79a076 /drivers/gpu | |
parent | 74bcbf7f350ce1d396829bec6455289e78aaf01d (diff) | |
download | linux-3ed9e53c6cebc27ef8e61fa2c431b4337b7dea3a.tar.xz |
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: José Expósito <jose.exposito89@gmail.com>
Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
Diffstat (limited to 'drivers/gpu')
19 files changed, 3386 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 026444eeb5c6..2a7ddb80fa13 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -347,6 +347,8 @@ source "drivers/gpu/drm/renesas/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 f9ca4f8fa6c5..3e1dd7b8d347 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -182,6 +182,7 @@ obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ obj-y += renesas/ 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..99181d43c8f0 --- /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 && RISCV_NONSTANDARD_CACHE_OPS + 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 + +Precautions:when 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..64907cc8f66d --- /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/platform_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..11171a6cf804 --- /dev/null +++ b/drivers/gpu/drm/starfive/starfive_drm_drv.c @@ -0,0 +1,262 @@ +// 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_fbdev_generic.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, + .gem_prime_import_sg_table = starfive_drm_gem_prime_import_sg_table, + .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..225781a7df91 --- /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/platform_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..fc6da9275fc9 --- /dev/null +++ b/drivers/gpu/drm/starfive/starfive_drm_gem.c @@ -0,0 +1,348 @@ +// 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; +#if 0 +static const struct vm_operations_struct mmap_mem_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys +#endif +}; +#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(). + */ + vm_flags_clear(vma, 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..7a118222bd93 --- /dev/null +++ b/drivers/gpu/drm/starfive/starfive_drm_vpp.c @@ -0,0 +1,766 @@ +// 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, "Err:invalid 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, "Err:invalid 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 y_rgb_ofst = 0, uofst = 0, v_uvofst = 0; + unsigned int next_y_rgb_addr = 0, next_u_addr = 0, next_v_addr = 0; + unsigned int i = 0; + unsigned int size; + + 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; + + //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; + 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; + 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; + } 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; + } 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; + } 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; + } 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; + } + } + 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; + y_rgb_ofst = 0; + uofst = 0; + v_uvofst = 0; + + if (dst->format >= COLOR_RGB888_ARGB) { + next_y_rgb_addr = dstaddr; + next_u_addr = 0; + next_v_addr = 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; + } else if (dst->format == COLOR_YUV420_NV12) { + /* yyyyuvuvuv */ + next_y_rgb_addr = dstaddr; + next_u_addr = dstaddr + size; + next_v_addr = dstaddr + size + 1; + uofst = size; + } else if (dst->format == COLOR_YUV420P) { + next_y_rgb_addr = dstaddr; + next_u_addr = dstaddr + size; + next_v_addr = dstaddr + size * 5 / 4; + } else if (dst->format == COLOR_YUV422_YVYU) { + next_y_rgb_addr = dstaddr; + next_u_addr = dstaddr + 1; + next_v_addr = dstaddr + 3; + } else if (dst->format == COLOR_YUV422_VYUY) { + next_y_rgb_addr = dstaddr + 1; + next_u_addr = dstaddr + 2; + next_v_addr = dstaddr; + } else if (dst->format == COLOR_YUV422_YUYV) { + next_y_rgb_addr = dstaddr; + next_u_addr = dstaddr + 1; + next_v_addr = dstaddr + 2; + } else if (dst->format == COLOR_YUV422_UYVY) { + next_y_rgb_addr = dstaddr + 1; + next_u_addr = dstaddr; + next_v_addr = dstaddr + 2; + } + } + 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 |