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-07-22 02:21:00 +0300 |
commit | 54a67c08000a4e0b3bb39280cddd05be22a51d20 (patch) | |
tree | 3d2c603f960c8a311594fb9e210e78b3906a7609 /drivers/gpu/drm/starfive/starfive_drm_crtc.c | |
parent | ca5428c8c5fee56ae6e20eeb25fcd455a524a039 (diff) | |
download | linux-54a67c08000a4e0b3bb39280cddd05be22a51d20.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/drm/starfive/starfive_drm_crtc.c')
-rw-r--r-- | drivers/gpu/drm/starfive/starfive_drm_crtc.c | 511 |
1 files changed, 511 insertions, 0 deletions
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), + }, +}; |