From efe0578ec8a891e3953c6d57ec7470332e696065 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 6 Jul 2020 09:56:42 +0800 Subject: [PATCH] drm/rockchip: Add support for vop2 Rockchip vop2 build on a unified architecture with multi video output ports support. For RK3568: * 3 Video Port, every video port can drive a display timing independently. * 6 graphic window: Cluster win x 2, Esmart win x 2, Smart wind x 2. * 6 windows can be divided into 3 groups for independent overlay for 3 Video Ports. * RGB/eDP/HDMI/MIPI get display timing from 1 of the 3 Video Ports. +----------+ +-------------+ | Cluster | | Sel 1 from 6| +--------------------+ | window0 | | Layer0 | |n from 6| | +----------+ +-------------+ | |Video Port0| +----------+ +-------------+ |Overlay | | | Cluster | | Sel 1 from 6| +--------+-----------+ | window1 | | Layer1 | +----------+ +-------------+ +----------+ +-------------+ | Esmart | | Sel 1 from 6| | window0 | | Layer2 | +--------------------+ +----------+ +-------------+ |n from 6| | +----------+ +-------------+ | |Video Port1| | Esmart | | Sel 1 from 6| |Overlay | | | Window1 | | Layer3 | +--------+-----------+ +----------+ +-------------+ +----------+ +-------------+ | Smart | | Sel 1 from 6| | Window0 | | Layer4 | +--------------------+ +----------+ +-------------+ |n from 6| | +----------+ +-------------+ | |Video Port2| | Smart | | Sel 1 from 6| |Overlay | | | Window1 | | Layer5 | +--------+-----------+ +----------+ +-------------+ Change-Id: I4c42d655f75903066888b6aea92e926192b000c2 Signed-off-by: Andy Yan --- drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 3 +- drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 320 ++ drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 3971 ++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 950 +++++ drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 379 ++ 6 files changed, 5624 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_vop2_reg.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 67bba02d5056..a17aa6d82a84 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -5,7 +5,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchip_drm_gem.o rockchip_drm_psr.o \ - rockchip_drm_vop.o rockchip_vop_reg.o + rockchip_drm_vop.o rockchip_vop_reg.o \ + rockchip_drm_vop2.o rockchip_vop2_reg.o rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index c3e4db8936a1..ab63a503d5dc 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -28,7 +28,8 @@ #define ROCKCHIP_MAX_FB_BUFFER 3 #define ROCKCHIP_MAX_CONNECTOR 2 -#define ROCKCHIP_MAX_CRTC 2 +#define ROCKCHIP_MAX_CRTC 4 +#define ROCKCHIP_MAX_LAYER 16 struct drm_device; struct drm_connector; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 4a23c12f8fc6..d34455b82dad 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -292,6 +292,26 @@ struct vop_scl_regs { struct vop_reg scale_cbcr_y; }; +struct vop_afbc { + struct vop_reg enable; + struct vop_reg win_sel; + struct vop_reg format; + struct vop_reg rotate; + struct vop_reg block_split_en; + struct vop_reg pic_vir_width; + struct vop_reg tile_num; + struct vop_reg pic_offset; + struct vop_reg pic_size; + struct vop_reg dsp_offset; + struct vop_reg hdr_ptr; + struct vop_reg half_block_en; + struct vop_reg xmirror; + struct vop_reg ymirror; + struct vop_reg rotate_270; + struct vop_reg rotate_90; + struct vop_reg rstn; +}; + struct vop_csc_table { const uint32_t *y2r_bt601; const uint32_t *y2r_bt601_12_235; @@ -411,6 +431,115 @@ struct vop_win_phy { struct vop_reg key_en; }; +struct vop2_scl_regs { + struct vop_reg scale_yrgb_x; + struct vop_reg scale_yrgb_y; + struct vop_reg scale_cbcr_x; + struct vop_reg scale_cbcr_y; + struct vop_reg yrgb_hor_scl_mode; + struct vop_reg yrgb_hscl_filter_mode; + struct vop_reg yrgb_ver_scl_mode; + struct vop_reg yrgb_vscl_filter_mode; + struct vop_reg cbcr_ver_scl_mode; + struct vop_reg cbcr_hscl_filter_mode; + struct vop_reg cbcr_hor_scl_mode; + struct vop_reg cbcr_vscl_filter_mode; + struct vop_reg vsd_cbcr_gt2; + struct vop_reg vsd_cbcr_gt4; + struct vop_reg vsd_yrgb_gt2; + struct vop_reg vsd_yrgb_gt4; + struct vop_reg bic_coe_sel; +}; + +struct vop2_win_regs { + const struct vop2_scl_regs *scl; + const struct vop_afbc *afbc; + + struct vop_reg gate; + struct vop_reg enable; + struct vop_reg format; + struct vop_reg csc_mode; + struct vop_reg xmirror; + struct vop_reg ymirror; + struct vop_reg rb_swap; + struct vop_reg act_info; + struct vop_reg dsp_info; + struct vop_reg dsp_st; + struct vop_reg yrgb_mst; + struct vop_reg uv_mst; + struct vop_reg yrgb_vir; + struct vop_reg uv_vir; + struct vop_reg lb_mode; + struct vop_reg y2r_en; + struct vop_reg r2y_en; + struct vop_reg channel; + struct vop_reg dst_alpha_ctl; + struct vop_reg src_alpha_ctl; + struct vop_reg alpha_mode; + struct vop_reg alpha_en; + struct vop_reg global_alpha_val; + struct vop_reg key_color; + struct vop_reg key_en; +}; + +struct vop2_video_port_regs { + struct vop_reg cfg_done; + struct vop_reg overlay_mode; + struct vop_reg port_mux; + struct vop_reg out_mode; + struct vop_reg standby; + struct vop_reg dsp_interlace; + struct vop_reg pre_scan_htiming; + struct vop_reg htotal_pw; + struct vop_reg hact_st_end; + struct vop_reg vtotal_pw; + struct vop_reg vact_st_end; + struct vop_reg vact_st_end_f1; + struct vop_reg vs_st_end_f1; + struct vop_reg hpost_st_end; + struct vop_reg vpost_st_end; + struct vop_reg vpost_st_end_f1; + struct vop_reg post_scl_factor; + struct vop_reg post_scl_ctrl; + struct vop_reg dither_down_sel; + struct vop_reg dither_down_mode; + struct vop_reg dither_down_en; + struct vop_reg pre_dither_down_en; + struct vop_reg dither_up_en; + struct vop_reg bg_dly; + + struct vop_reg core_dclk_div; + struct vop_reg p2i_en; + struct vop_reg mipi_dual_en; + struct vop_reg mipi_dual_channel_swap; + + struct vop_reg hdr_lut_update_en; + struct vop_reg hdr_lut_mode; + struct vop_reg hdr_lut_mst; + struct vop_reg sdr2hdr_eotf_en; + struct vop_reg sdr2hdr_r2r_en; + struct vop_reg sdr2hdr_r2r_mode; + struct vop_reg sdr2hdr_oetf_en; + struct vop_reg sdr2hdr_bypass_en; + struct vop_reg sdr2hdr_gating_en; + struct vop_reg hdr2sdr_en; + struct vop_reg hdr2sdr_src_min; + struct vop_reg hdr2sdr_src_max; + struct vop_reg hdr2sdr_normfaceetf; + struct vop_reg hdr2sdr_dst_min; + struct vop_reg hdr2sdr_dst_max; + struct vop_reg hdr2sdr_normfacgamma; + struct vop_reg hdr2sdr_eetf_oetf_y0_offset; + struct vop_reg hdr2sdr_sat_y0_offset; + struct vop_reg sdr2hdr_eotf_oetf_y0_offset; + struct vop_reg sdr2hdr_oetf_dx_pow1_offset; + struct vop_reg sdr2hdr_oetf_xn1_offset; + struct vop_reg hdr_src_color_ctrl; + struct vop_reg hdr_dst_color_ctrl; + struct vop_reg hdr_src_alpha_ctrl; + struct vop_reg hdr_dst_alpha_ctrl; +}; + struct vop_win_data { uint32_t base; enum drm_plane_type type; @@ -421,6 +550,81 @@ struct vop_win_data { u64 feature; }; +struct vop2_win_data { + const char *name; + uint8_t phys_id; + + uint32_t base; + enum drm_plane_type type; + + uint32_t nformats; + const uint32_t *formats; + const unsigned int supported_rotations; + + const struct vop2_win_regs *regs; + const struct vop2_win_regs **area; + unsigned int area_size; + + /* + * vertical/horizontal scale up/down filter mode + */ + const u8 hsu_filter_mode; + const u8 hsd_filter_mode; + const u8 vsu_filter_mode; + const u8 vsd_filter_mode; + /** + * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2 + */ + int layer_sel_id; + uint64_t feature; + + unsigned int max_upscale_factor; + unsigned int max_downscale_factor; +}; + +struct vop2_video_port_data { + char id; + uint32_t feature; + uint64_t soc_id; + const u8 pre_scan_max_dly[4]; + const struct vop_intr *intr; + const struct vop_hdr_table *hdr_table; + const struct vop2_video_port_regs *regs; +}; + +struct vop2_layer_regs { + struct vop_reg layer_sel; +}; + +/** + * struct vop2_layer_data - The logic graphic layer in vop2 + * + * The zorder: + * LAYERn + * LAYERn-1 + * . + * . + * . + * LAYER5 + * LAYER4 + * LAYER3 + * LAYER2 + * LAYER1 + * LAYER0 + * + * Each layer can select a unused window as input than feed to + * mixer for overlay. + * + * The pipeline in vop2: + * + * win-->layer-->mixer-->vp--->connector(RGB/LVDS/HDMI/MIPI) + * + */ +struct vop2_layer_data { + char id; + const struct vop2_layer_regs *regs; +}; + struct vop_grf_ctrl { struct vop_reg grf_dclk_inv; }; @@ -433,6 +637,8 @@ struct vop_grf_ctrl { #define WIN_FEATURE_SDR2HDR BIT(1) #define WIN_FEATURE_PRE_OVERLAY BIT(2) #define WIN_FEATURE_AFBDC BIT(3) +#define WIN_FEATURE_CLUSTER_MAIN BIT(4) +#define WIN_FEATURE_CLUSTER_SUB BIT(5) struct vop_rect { int width; @@ -455,6 +661,106 @@ struct vop_data { u64 feature; }; +struct vop2_ctrl { + struct vop_reg cfg_done_en; + struct vop_reg ovl_cfg_done_port; + struct vop_reg ovl_port_mux_cfg_done_imd; + struct vop_reg if_ctrl_cfg_done_imd; + struct vop_reg version; + struct vop_reg standby; + struct vop_reg dma_stop; + struct vop_reg axi_outstanding_max_num; + struct vop_reg axi_max_outstanding_en; + struct vop_reg hdmi_dclk_out_en; + struct vop_reg rgb_en; + struct vop_reg hdmi0_en; + struct vop_reg hdmi1_en; + struct vop_reg dp0_en; + struct vop_reg dp1_en; + struct vop_reg edp0_en; + struct vop_reg edp1_en; + struct vop_reg mipi0_en; + struct vop_reg mipi1_en; + struct vop_reg lvds0_en; + struct vop_reg lvds1_en; + struct vop_reg bt656_en; + struct vop_reg bt1120_en; + struct vop_reg dclk_pol; + struct vop_reg pin_pol; + struct vop_reg rgb_dclk_pol; + struct vop_reg rgb_pin_pol; + struct vop_reg lvds_dclk_pol; + struct vop_reg lvds_pin_pol; + struct vop_reg hdmi_dclk_pol; + struct vop_reg hdmi_pin_pol; + struct vop_reg edp_dclk_pol; + struct vop_reg edp_pin_pol; + struct vop_reg mipi_dclk_pol; + struct vop_reg mipi_pin_pol; + struct vop_reg dp_dclk_pol; + struct vop_reg dp_pin_pol; + + struct vop_reg win_vp_id[8]; + + /* connector mux */ + struct vop_reg rgb_mux; + struct vop_reg hdmi0_mux; + struct vop_reg hdmi1_mux; + struct vop_reg dp0_mux; + struct vop_reg dp1_mux; + struct vop_reg edp0_mux; + struct vop_reg edp1_mux; + struct vop_reg mipi0_mux; + struct vop_reg mipi1_mux; + struct vop_reg lvds0_mux; + struct vop_reg lvds1_mux; + + struct vop_reg lvds_dual_en; + struct vop_reg lvds_dual_mode; + struct vop_reg lvds_dual_channel_swap; + + struct vop_reg cluster0_src_color_ctrl; + struct vop_reg cluster0_dst_color_ctrl; + struct vop_reg cluster0_src_alpha_ctrl; + struct vop_reg cluster0_dst_alpha_ctrl; + struct vop_reg src_color_ctrl; + struct vop_reg dst_color_ctrl; + struct vop_reg src_alpha_ctrl; + struct vop_reg dst_alpha_ctrl; + + struct vop_reg bt1120_yc_swap; + + struct vop_reg reg_done_frm; + struct vop_reg cfg_done; +}; + +/** + * VOP2 data structe + * + * @version: VOP IP version + * @win_size: hardware win number + */ +struct vop2_data { + uint32_t version; + uint32_t feature; + uint8_t nr_vps; + uint8_t nr_mixers; + uint8_t nr_layers; + uint8_t nr_axi_intr; + const struct vop_intr *axi_intr; + const struct vop2_ctrl *ctrl; + const struct vop2_win_data *win; + const struct vop2_video_port_data *vp; + const struct vop2_layer_data *layer; + const struct vop_csc_table *csc_table; + const struct vop_hdr_table *hdr_table; + const struct vop_grf_ctrl *grf_ctrl; + struct vop_rect max_input; + struct vop_rect max_output; + + unsigned int win_size; +}; + #define CVBS_PAL_VDISPLAY 288 /* interrupt define */ @@ -559,6 +865,7 @@ enum factor_mode { ALPHA_SRC, ALPHA_SRC_INVERSE, ALPHA_SRC_GLOBAL, + ALPHA_DST_GLOBAL, }; enum scale_mode { @@ -586,6 +893,18 @@ enum scale_down_mode { SCALE_DOWN_AVG = 0x1 }; +enum vop2_scale_up_mode { + VOP2_SCALE_UP_NRST_NBOR, + VOP2_SCALE_UP_BIL, + VOP2_SCALE_UP_BIC, +}; + +enum vop2_scale_down_mode { + VOP2_SCALE_DOWN_NRST_NBOR, + VOP2_SCALE_DOWN_BIL, + VOP2_SCALE_DOWN_AVG, +}; + enum dither_down_mode { RGB888_TO_RGB565 = 0x0, RGB888_TO_RGB666 = 0x1 @@ -688,4 +1007,5 @@ static inline int interpolate(int x1, int y1, int x2, int y2, int x) } extern const struct component_ops vop_component_ops; +extern const struct component_ops vop2_component_ops; #endif /* _ROCKCHIP_DRM_VOP_H */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c new file mode 100644 index 000000000000..3676eea603ca --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -0,0 +1,3971 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * Author: Andy Yan + */ +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DRM_ANALOGIX_DP +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../drm_internal.h" + +#include "rockchip_drm_drv.h" +#include "rockchip_drm_gem.h" +#include "rockchip_drm_fb.h" +#include "rockchip_drm_psr.h" +#include "rockchip_drm_vop.h" + +#define _REG_SET(vop2, name, off, reg, mask, v, relaxed) \ + vop2_mask_write(vop2, off + reg.offset, mask, reg.shift, v, reg.write_mask, relaxed) + +#define REG_SET(x, name, off, reg, v, relaxed) \ + _REG_SET(x, name, off, reg, reg.mask, v, relaxed) +#define REG_SET_MASK(x, name, off, reg, mask, v, relaxed) \ + _REG_SET(x, name, off, reg, reg.mask & mask, v, relaxed) + +#define VOP_AFBC_SET(x, win, name, v) \ + do { \ + if (win->regs->afbc) \ + REG_SET(x, name, win->offset, win->regs->afbc->name, v, true); \ + } while (0) + +#define VOP_WIN_SET(x, win, name, v) \ + REG_SET(x, name, win->offset, VOP_WIN_NAME(win, name), v, true) + +#define VOP_SCL_SET(x, win, name, v) \ + REG_SET(x, name, win->offset, win->regs->scl->name, v, true) + +#define VOP_CTRL_SET(x, name, v) \ + REG_SET(x, name, 0, (x)->data->ctrl->name, v, false) + +#define VOP_INTR_GET(vop2, name) \ + vop2_read_reg(vop2, 0, &vop2->data->ctrl->name) + +#define VOP_INTR_SET(vop2, intr, name, v) \ + REG_SET(vop2, name, 0, intr->name, v, false) + +#define VOP_MODULE_SET(vop2, module, name, v) \ + REG_SET(vop2, name, 0, module->regs->name, v, false) + +#define VOP_REG_SET(vop2, group, name, v) \ + vop2_reg_set(vop2, &vop2->data->group->name, 0, ~0, v, #name) + +#define VOP_INTR_SET_MASK(vop2, intr, name, mask, v) \ + REG_SET_MASK(vop2, name, 0, intr->name, mask, v, false) + +#define VOP_INTR_SET_TYPE(vop2, intr, name, type, v) \ + do { \ + int i, reg = 0, mask = 0; \ + for (i = 0; i < intr->nintrs; i++) { \ + if (intr->intrs[i] & type) { \ + reg |= (v) << i; \ + mask |= 1 << i; \ + } \ + } \ + VOP_INTR_SET_MASK(vop2, intr, name, mask, reg); \ + } while (0) + +#define VOP_INTR_GET_TYPE(vop2, intr, name, type) \ + vop2_get_intr_type(vop2, intr, &intr->name, type) + +#define VOP_MODULE_GET(x, module, name) \ + vop2_read_reg(x, 0, &module->regs->name) + +#define VOP_WIN_GET(vop2, win, name) \ + vop2_read_reg(vop2, win->offset, &VOP_WIN_NAME(win, name)) + +#define VOP_WIN_NAME(win, name) \ + (vop2_get_win_regs(win, &win->regs->name)->name) + +#define VOP_WIN_TO_INDEX(vop2_win) \ + ((vop2_win) - (vop2_win)->vop2->win) + +#define VOP_GRF_SET(vop2, reg, v) \ + do { \ + if (vop2->data->grf_ctrl) { \ + vop2_grf_writel(vop2, vop2->data->grf_ctrl->reg, v); \ + } \ + } while (0) + +#define to_vop2_video_port(c) container_of(c, struct vop2_video_port, crtc) +#define to_vop2_win(x) container_of(x, struct vop2_win, base) +#define to_vop2_plane_state(x) container_of(x, struct vop2_plane_state, base) + +#define ROCKCHIP_AFBC_MOD DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16) + +#define VOP2_SYS_AXI_BUS_NUM 2 + +enum vop2_data_format { + VOP2_FMT_ARGB8888 = 0, + VOP2_FMT_RGB888, + VOP2_FMT_RGB565, + VOP2_FMT_XRGB101010, + VOP2_FMT_YUV420SP, + VOP2_FMT_YUV422SP, + VOP2_FMT_YUV444SP, + VOP2_FMT_YUYV422 = 8, + VOP2_FMT_YUYV420, + VOP2_FMT_VYUY422, + VOP2_FMT_VYUY420, + VOP2_FMT_YUV420SP_TILE_8x4, + VOP2_FMT_YUV420SP_TILE_16x2, + VOP2_FMT_YUV422SP_TILE_8x4, + VOP2_FMT_YUV422SP_TILE_16x2, + VOP2_FMT_YUV420SP_10, + VOP2_FMT_YUV422SP_10, + VOP2_FMT_YUV444SP_10, +}; + +enum vop2_afbc_format { + VOP2_AFBC_FMT_RGB565, + VOP2_AFBC_FMT_ARGB2101010 = 2, + VOP2_AFBC_FMT_YUV420_10BIT, + VOP2_AFBC_FMT_RGB888, + VOP2_AFBC_FMT_ARGB8888, + VOP2_AFBC_FMT_YUV420 = 9, + VOP2_AFBC_FMT_YUV422 = 0xb, + VOP2_AFBC_FMT_YUV422_10BIT = 0xe, +}; + +enum vop2_pending { + VOP_PENDING_FB_UNREF, +}; + +struct vop2_zpos { + int win_phys_id; + int zpos; +}; + +union vop2_alpha_ctrl { + uint32_t val; + struct { + /* [0:1] */ + uint32_t color_mode:1; + uint32_t alpha_mode:1; + /* [2:3] */ + uint32_t blend_mode:2; + uint32_t alpha_cal_mode:1; + /* [5:7] */ + uint32_t factor_mode:3; + /* [8:9] */ + uint32_t alpha_en:1; + uint32_t src_dst_swap:1; + uint32_t reserved:6; + /* [16:23] */ + uint32_t glb_alpha:8; + } bits; +}; + +struct vop2_alpha { + union vop2_alpha_ctrl src_color_ctrl; + union vop2_alpha_ctrl dst_color_ctrl; + union vop2_alpha_ctrl src_alpha_ctrl; + union vop2_alpha_ctrl dst_alpha_ctrl; +}; + +struct vop2_plane_state { + struct drm_plane_state base; + int format; + int zpos; + struct drm_rect src; + struct drm_rect dest; + dma_addr_t yrgb_mst; + dma_addr_t uv_mst; + bool afbc_en; + bool r2y_en; + bool y2r_en; + uint32_t csc_mode; + uint8_t xmirror_en; + uint8_t ymirror_en; + uint8_t rotate_90_en; + uint8_t rotate_270_en; + int eotf; + int color_space; + int global_alpha; + int blend_mode; + void *yrgb_kvaddr; + unsigned long offset; + int pdaf_data_type; + bool async_commit; + struct vop2_dump_list *planlist; +}; + +struct vop2_win { + const char *name; + struct vop2 *vop2; + struct vop2_win *parent; + struct drm_plane base; + + /** + * @phys_id: physical id for cluster0/1, esmart0/1, smart0/1 + * Will be used as a identification for some register + * configuration such as OVL_LAYER_SEL/OVL_PORT_SEL. + */ + uint8_t phys_id; + /** + * @win_id: graphic window id, a cluster maybe split into two + * graphics windows. + */ + uint8_t win_id; + /** + * @area_id: multi display region id in a graphic window, they + * share the same win_id. + */ + uint8_t area_id; + /** + * @layer_id: id of the layer which the window attached to + */ + uint8_t layer_id; + int layer_sel_id; + uint8_t zpos; + uint32_t offset; + enum drm_plane_type type; + unsigned int max_upscale_factor; + unsigned int max_downscale_factor; + unsigned int supported_rotations; + const struct vop2_win_regs *regs; + const uint32_t *formats; + uint32_t nformats; + uint64_t feature; + struct drm_property *feature_prop; + struct drm_property *input_width_prop; + struct drm_property *input_height_prop; + struct drm_property *output_width_prop; + struct drm_property *output_height_prop; + struct drm_property *scale_prop; + struct drm_property *name_prop; +}; + +struct vop2_layer { + uint8_t id; + /* + * @win_phys_id: window id of the layer selected. + * Every layer must make sure to select different + * windows of others. + */ + uint8_t win_phys_id; + const struct vop2_layer_regs *regs; +}; + +struct vop2_video_port { + struct drm_crtc crtc; + struct vop2 *vop2; + struct clk *dclk; + uint8_t id; + const struct vop2_video_port_regs *regs; + + struct completion dsp_hold_completion; + struct completion line_flag_completion; + + /* protected by dev->event_lock */ + struct drm_pending_vblank_event *event; + + struct drm_flip_work fb_unref_work; + unsigned long pending; + + /** + * @hdr_in: Indicate we have a hdr plane input. + * + */ + bool hdr_in; + /** + * @hdr_out: Indicate the screen want a hdr output + * from video port. + * + */ + bool hdr_out; + + /** + * @bg_ovl_dly: The timing delay from background layer + * to overlay module. + */ + u8 bg_ovl_dly; + + /** + * @hdr_en: Set when has a hdr video input. + */ + int hdr_en; + + /** + * @win_mask: Bitmask of active wins attached to the video port; + */ + uint32_t win_mask; + /** + * @nr_wins: active wins attached to the video port; + */ + uint8_t nr_wins; +}; + +struct vop2 { + u32 version; + struct device *dev; + struct drm_device *drm_dev; + struct vop2_video_port vps[ROCKCHIP_MAX_CRTC]; + struct dentry *debugfs; + struct drm_info_list *debugfs_files; + struct drm_property *soc_id_prop; + struct drm_property *vp_id_prop; + struct drm_prop_enum_list *plane_name_list; + bool is_iommu_enabled; + bool is_iommu_needed; + bool is_enabled; + bool support_multi_area; + + bool loader_protect; + + const struct vop2_data *data; + /* Number of win that registered as plane, + * maybe less than the total number of hardware + * win. + */ + uint32_t registered_num_wins; + uint8_t used_mixers; + + uint32_t *regsbak; + void __iomem *regs; + struct regmap *grf; + + /* physical map length of vop2 register */ + uint32_t len; + + void __iomem *lut_regs; + u32 *lut; + u32 lut_len; + bool lut_active; + /* one time only one process allowed to config the register */ + spinlock_t reg_lock; + /* lock vop2 irq reg */ + spinlock_t irq_lock; + /* protects crtc enable/disable */ + struct mutex vop2_lock; + + int irq; + + /* + * Some globle resource are shared between all + * the vidoe ports(crtcs), so we need a ref counter here. + */ + unsigned int enable_count; + struct clk *hclk; + struct clk *aclk; + + struct vop2_layer layers[ROCKCHIP_MAX_LAYER]; + /* must put at the end of the struct */ + struct vop2_win win[]; +}; + +/* + * bus-format types. + */ +struct drm_bus_format_enum_list { + int type; + const char *name; +}; + +static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = { + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, + { MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" }, + { MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" }, + { MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" }, + { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" }, + { MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA, "RGB666_1X7X3_JEIDA" }, + { MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" }, + { MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" }, + { MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" }, + { MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" }, + { MEDIA_BUS_FMT_SRGB888_3X8, "SRGB888_3X8" }, + { MEDIA_BUS_FMT_SRGB888_DUMMY_4X8, "SRGB888_DUMMY_4X8" }, + { MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" }, + { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" }, + { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" }, +}; + +static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list) + +/* + * VOP2 architecture + * + +----------+ +-------------+ + | Cluster | | Sel 1 from 6 + | window0 | | Layer0 | +---------------+ +-------------+ +-----------+ + +----------+ +-------------+ |N from 6 layers| | | | 1 from 3 | + +----------+ +-------------+ | Overlay0 | | Video Port0 | | RGB | + | Cluster | | Sel 1 from 6| | | | | +-----------+ + | window1 | | Layer1 | +---------------+ +-------------+ + +----------+ +-------------+ +-----------+ + +----------+ +-------------+ +--> | 1 from 3 | + | Esmart | | Sel 1 from 6| +---------------+ +-------------+ | LVDS | + | window0 | | Layer2 | |N from 6 Layers | | +-----------+ + +----------+ +-------------+ | Overlay1 + | Video Port1 | +---> + +----------+ +-------------+ --------> | | | | +-----------+ + | Esmart | | Sel 1 from 6| --------> +---------------+ +-------------+ | 1 from 3 | + | Window1 | | Layer3 | +--> | MIPI | + +----------+ +-------------+ +-----------+ + +----------+ +-------------+ +---------------+ +-------------+ + | Smart | | Sel 1 from 6| |N from 6 Layers| | | +-----------+ + | Window0 | | Layer4 | | Overlay2 | | Video Port2 | | 1 from 3 | + +----------+ +-------------+ | | | | | HDMI | + +----------+ +-------------+ +---------------+ +-------------+ +-----------+ + | Smart | | Sel 1 from 6| +-----------+ + | Window1 | | Layer5 | | 1 from 3 | + +----------+ +-------------+ | eDP | + * +-----------+ + * + */ +static void vop2_lock(struct vop2 *vop2) +{ + mutex_lock(&vop2->vop2_lock); + rockchip_dmcfreq_lock(); +} + +static void vop2_unlock(struct vop2 *vop2) +{ + rockchip_dmcfreq_unlock(); + mutex_unlock(&vop2->vop2_lock); +} + +static inline void vop2_grf_writel(struct vop2 *vop2, struct vop_reg reg, u32 v) +{ + u32 val = 0; + + if (IS_ERR_OR_NULL(vop2->grf)) + return; + + if (reg.mask) { + val = (v << reg.shift) | (reg.mask << (reg.shift + 16)); + regmap_write(vop2->grf, reg.offset, val); + } +} + +static inline void vop2_writel(struct vop2 *vop2, uint32_t offset, uint32_t v) +{ + writel(v, vop2->regs + offset); + vop2->regsbak[offset >> 2] = v; +} + +static inline uint32_t vop2_readl(struct vop2 *vop2, uint32_t offset) +{ + return readl(vop2->regs + offset); +} + +static inline uint32_t vop2_read_reg(struct vop2 *vop2, uint32_t base, + const struct vop_reg *reg) +{ + return (vop2_readl(vop2, base + reg->offset) >> reg->shift) & reg->mask; +} + +static inline void vop2_mask_write(struct vop2 *vop2, uint32_t offset, + uint32_t mask, uint32_t shift, uint32_t v, + bool write_mask, bool relaxed) +{ + uint32_t cached_val; + + if (!mask) + return; + + if (write_mask) { + v = ((v & mask) << shift) | (mask << (shift + 16)); + } else { + cached_val = vop2->regsbak[offset >> 2]; + + v = (cached_val & ~(mask << shift)) | ((v & mask) << shift); + vop2->regsbak[offset >> 2] = v; + } + + if (relaxed) + writel_relaxed(v, vop2->regs + offset); + else + writel(v, vop2->regs + offset); +} + +static inline const struct vop2_win_regs *vop2_get_win_regs(struct vop2_win *win, + const struct vop_reg *reg) +{ + if (!reg->mask && win->parent) + return win->parent->regs; + + return win->regs; +} + +static inline uint32_t vop2_get_intr_type(struct vop2 *vop2, const struct vop_intr *intr, + const struct vop_reg *reg, int type) +{ + uint32_t val, i; + uint32_t ret = 0; + + val = vop2_read_reg(vop2, 0, reg); + + for (i = 0; i < intr->nintrs; i++) { + if ((type & intr->intrs[i]) && (val & 1 << i)) + ret |= intr->intrs[i]; + } + + return ret; +} + +/** + * phys_id is used to identify a main window(Cluster Win/Smart Win, not + * include the sub win of a cluster or the multi area) that can do + * overlay in main overlay stage. + */ +static struct vop2_win *vop2_find_win_by_phys_id(struct vop2 *vop2, uint8_t phys_id) +{ + struct vop2_win *win; + int i; + + for (i = 0; i < vop2->registered_num_wins; i++) { + win = &vop2->win[i]; + if (win->phys_id == phys_id) + return win; + } + + return NULL; +} + +static void vop2_load_hdr2sdr_table(struct vop2_video_port *vp) +{ + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_hdr_table *table = vp_data->hdr_table; + uint32_t hdr2sdr_eetf_oetf_yn[33]; + int i; + + for (i = 0; i < 33; i++) + hdr2sdr_eetf_oetf_yn[i] = table->hdr2sdr_eetf_yn[i] + + (table->hdr2sdr_bt1886oetf_yn[i] << 16); + + vop2_writel(vop2, table->hdr2sdr_eetf_oetf_y0_offset, + hdr2sdr_eetf_oetf_yn[0]); + for (i = 1; i < 33; i++) + vop2_writel(vop2, + table->hdr2sdr_eetf_oetf_y1_offset + (i - 1) * 4, + hdr2sdr_eetf_oetf_yn[i]); + + vop2_writel(vop2, table->hdr2sdr_sat_y0_offset, + table->hdr2sdr_sat_yn[0]); + for (i = 1; i < 9; i++) + vop2_writel(vop2, table->hdr2sdr_sat_y1_offset + (i - 1) * 4, + table->hdr2sdr_sat_yn[i]); +} + +static void vop2_load_sdr2hdr_table(struct vop2_video_port *vp, int sdr2hdr_tf) +{ + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_hdr_table *table = vp_data->hdr_table; + uint32_t sdr2hdr_eotf_oetf_yn[65]; + uint32_t sdr2hdr_oetf_dx_dxpow[64]; + int i; + + for (i = 0; i < 65; i++) { + if (sdr2hdr_tf == SDR2HDR_FOR_BT2020) + sdr2hdr_eotf_oetf_yn[i] = + table->sdr2hdr_bt1886eotf_yn_for_bt2020[i] + + (table->sdr2hdr_st2084oetf_yn_for_bt2020[i] << 18); + else if (sdr2hdr_tf == SDR2HDR_FOR_HDR) + sdr2hdr_eotf_oetf_yn[i] = + table->sdr2hdr_bt1886eotf_yn_for_hdr[i] + + (table->sdr2hdr_st2084oetf_yn_for_hdr[i] << 18); + else if (sdr2hdr_tf == SDR2HDR_FOR_HLG_HDR) + sdr2hdr_eotf_oetf_yn[i] = + table->sdr2hdr_bt1886eotf_yn_for_hlg_hdr[i] + + (table->sdr2hdr_st2084oetf_yn_for_hlg_hdr[i] << 18); + } + vop2_writel(vop2, table->sdr2hdr_eotf_oetf_y0_offset, + sdr2hdr_eotf_oetf_yn[0]); + for (i = 1; i < 65; i++) + vop2_writel(vop2, table->sdr2hdr_eotf_oetf_y1_offset + + (i - 1) * 4, sdr2hdr_eotf_oetf_yn[i]); + + for (i = 0; i < 64; i++) { + sdr2hdr_oetf_dx_dxpow[i] = table->sdr2hdr_st2084oetf_dxn[i] + + (table->sdr2hdr_st2084oetf_dxn_pow2[i] << 16); + vop2_writel(vop2, table->sdr2hdr_oetf_dx_dxpow1_offset + i * 4, + sdr2hdr_oetf_dx_dxpow[i]); + } + + for (i = 0; i < 63; i++) + vop2_writel(vop2, table->sdr2hdr_oetf_xn1_offset + i * 4, + table->sdr2hdr_st2084oetf_xn[i]); +} + +static inline void vop2_cfg_done(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + VOP_MODULE_SET(vop2, vp, cfg_done, 1); +} + +static void vop2_win_disable(struct vop2_win *win) +{ + struct vop2 *vop2 = win->vop2; + + VOP_WIN_SET(vop2, win, enable, 0); + if (win->feature & WIN_FEATURE_CLUSTER_MAIN) + VOP_WIN_SET(vop2, win, gate, 0); +} + +static inline void vop2_write_lut(struct vop2 *vop2, uint32_t offset, uint32_t v) +{ + writel(v, vop2->lut_regs + offset); +} + +static inline uint32_t vop2_read_lut(struct vop2 *vop2, uint32_t offset) +{ + return readl(vop2->lut_regs + offset); +} + +static enum vop2_data_format vop2_convert_format(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return VOP2_FMT_ARGB8888; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + return VOP2_FMT_RGB888; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + return VOP2_FMT_RGB565; + case DRM_FORMAT_NV12: + return VOP2_FMT_YUV420SP; + case DRM_FORMAT_NV12_10: + return VOP2_FMT_YUV420SP_10; + case DRM_FORMAT_NV16: + return VOP2_FMT_YUV422SP; + case DRM_FORMAT_NV16_10: + return VOP2_FMT_YUV422SP_10; + case DRM_FORMAT_NV24: + return VOP2_FMT_YUV444SP; + case DRM_FORMAT_NV24_10: + return VOP2_FMT_YUV444SP_10; + case DRM_FORMAT_YUYV: + return VOP2_FMT_YUYV422; + default: + DRM_ERROR("unsupported format[%08x]\n", format); + return -EINVAL; + } +} + +static enum vop2_afbc_format vop2_convert_afbc_format(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return VOP2_AFBC_FMT_ARGB8888; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + return VOP2_AFBC_FMT_RGB888; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + return VOP2_AFBC_FMT_RGB565; + case DRM_FORMAT_NV12: + return VOP2_AFBC_FMT_YUV420; + case DRM_FORMAT_NV12_10: + return VOP2_AFBC_FMT_YUV420_10BIT; + case DRM_FORMAT_NV16: + return VOP2_AFBC_FMT_YUV422; + case DRM_FORMAT_NV16_10: + return VOP2_AFBC_FMT_YUV422_10BIT; + + /* either of the below should not be reachable */ + default: + DRM_WARN_ONCE("unsupported AFBC format[%08x]\n", format); + return -EINVAL; + } + + return -EINVAL; +} + +static bool is_yuv_support(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV12_10: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV16_10: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV24_10: + case DRM_FORMAT_YUYV: + return true; + default: + return false; + } +} + +static bool is_yuv_output(uint32_t bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YUYV8_1X16: + return true; + default: + return false; + } +} + +static bool is_alpha_support(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + return true; + default: + return false; + } +} + +static inline bool rockchip_afbc(u64 modifier) +{ + return modifier == ROCKCHIP_AFBC_MOD; +} + +static uint16_t scl_vop2_cal_scale(enum scale_mode mode, uint32_t src, + uint32_t dst, bool is_horizontal, + int vsu_mode, int *vskiplines) +{ + uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT; + + if (vskiplines) + *vskiplines = 0; + + if (is_horizontal) { + if (mode == SCALE_UP) + val = GET_SCL_FT_BIC(src, dst); + else if (mode == SCALE_DOWN) + val = GET_SCL_FT_BILI_DN(src, dst); + } else { + if (mode == SCALE_UP) { + if (vsu_mode == SCALE_UP_BIL) + val = GET_SCL_FT_BILI_UP(src, dst); + else + val = GET_SCL_FT_BIC(src, dst); + } else if (mode == SCALE_DOWN) { + if (vskiplines) { + *vskiplines = scl_get_vskiplines(src, dst); + val = scl_get_bili_dn_vskip(src, dst, + *vskiplines); + } else { + val = GET_SCL_FT_BILI_DN(src, dst); + } + } + } + + return val; +} + +static int vop2_afbc_half_block_enable(struct vop2_plane_state *vpstate) +{ + if (vpstate->rotate_270_en || vpstate->rotate_90_en) + return 0; + else + return 1; +} + +/* + * A Cluster window has 2048 x 16 line buffer, which can + * works at 2048 x 16(Full) or 4096 x 8 (Half) mode. + * for Cluster_lb_mode register: + * 0: half mode, for plane input width range 2048 ~ 4096 + * 1: half mode, for cluster work at 2 * 2048 plane mode + * 2: half mode, for rotate_90/270 mode + * + */ +static int vop2_get_cluster_lb_mode(struct vop2_win *win, struct vop2_plane_state *vpstate) +{ + if (vpstate->rotate_270_en || vpstate->rotate_90_en) + return 2; + else if (win->feature & WIN_FEATURE_CLUSTER_SUB) + return 1; + else + return 0; +} + +static void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win, + uint32_t src_w, uint32_t src_h, uint32_t dst_w, + uint32_t dst_h, uint32_t pixel_format) +{ + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_win_data *win_data = &vop2_data->win[win->win_id]; + const struct drm_format_info *info; + int hsub = drm_format_horz_chroma_subsampling(pixel_format); + int vsub = drm_format_vert_chroma_subsampling(pixel_format); + uint16_t cbcr_src_w = src_w / hsub; + uint16_t cbcr_src_h = src_h / vsub; + uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode; + uint16_t cbcr_hor_scl_mode, cbcr_ver_scl_mode; + uint16_t hscl_filter_mode, vscl_filter_mode; + uint32_t val; + int vskiplines; + + info = drm_format_info(pixel_format); + + yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w); + yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h); + + cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w); + cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h); + + if (yrgb_hor_scl_mode == SCALE_UP) + hscl_filter_mode = win_data->hsu_filter_mode; + else + hscl_filter_mode = win_data->hsd_filter_mode; + + if (yrgb_ver_scl_mode == SCALE_UP) + vscl_filter_mode = win_data->vsu_filter_mode; + else + vscl_filter_mode = win_data->vsd_filter_mode; + + val = scl_vop2_cal_scale(yrgb_hor_scl_mode, src_w, dst_w, true, 0, NULL); + VOP_SCL_SET(vop2, win, scale_yrgb_x, val); + val = scl_vop2_cal_scale(yrgb_ver_scl_mode, src_h, dst_h, false, + vscl_filter_mode, &vskiplines); + VOP_SCL_SET(vop2, win, scale_yrgb_y, val); + + VOP_SCL_SET(vop2, win, vsd_yrgb_gt4, vskiplines == 4); + VOP_SCL_SET(vop2, win, vsd_yrgb_gt2, vskiplines == 2); + + VOP_SCL_SET(vop2, win, yrgb_hor_scl_mode, yrgb_hor_scl_mode); + VOP_SCL_SET(vop2, win, yrgb_ver_scl_mode, yrgb_ver_scl_mode); + + VOP_SCL_SET(vop2, win, yrgb_hscl_filter_mode, hscl_filter_mode); + VOP_SCL_SET(vop2, win, yrgb_vscl_filter_mode, vscl_filter_mode); + + if (info->is_yuv) { + val = scl_vop2_cal_scale(cbcr_hor_scl_mode, cbcr_src_w, dst_w, true, 0, NULL); + VOP_SCL_SET(vop2, win, scale_cbcr_x, val); + val = scl_vop2_cal_scale(cbcr_ver_scl_mode, cbcr_src_h, dst_h, false, + vscl_filter_mode, &vskiplines); + VOP_SCL_SET(vop2, win, scale_cbcr_y, val); + VOP_SCL_SET(vop2, win, vsd_cbcr_gt4, vskiplines == 4); + VOP_SCL_SET(vop2, win, vsd_cbcr_gt2, vskiplines == 2); + VOP_SCL_SET(vop2, win, cbcr_hor_scl_mode, cbcr_hor_scl_mode); + VOP_SCL_SET(vop2, win, cbcr_ver_scl_mode, cbcr_ver_scl_mode); + VOP_SCL_SET(vop2, win, cbcr_hscl_filter_mode, hscl_filter_mode); + VOP_SCL_SET(vop2, win, cbcr_vscl_filter_mode, vscl_filter_mode); + } +} + +static int vop2_convert_csc_mode(int csc_mode) +{ + switch (csc_mode) { + case V4L2_COLORSPACE_SMPTE170M: + return CSC_BT601L; + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_DEFAULT: + return CSC_BT709L; + case V4L2_COLORSPACE_JPEG: + return CSC_BT601F; + case V4L2_COLORSPACE_BT2020: + return CSC_BT2020; + default: + return CSC_BT709L; + } +} + +static bool vop2_is_allwin_disabled(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_plane *plane; + struct vop2_win *win; + + drm_atomic_crtc_for_each_plane(plane, crtc) { + win = to_vop2_win(plane); + if (VOP_WIN_GET(vop2, win, enable) != 0) + return false; + } + + return true; +} + +static void vop2_disable_all_planes_for_crtc(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_plane *plane; + struct vop2_win *win; + bool active; + int ret; + + drm_atomic_crtc_for_each_plane(plane, crtc) { + win = to_vop2_win(plane); + vop2_win_disable(win); + } + vop2_cfg_done(crtc); + ret = readx_poll_timeout_atomic(vop2_is_allwin_disabled, crtc, + active, active, 0, 500 * 1000); + if (ret) + DRM_DEV_ERROR(vop2->dev, "wait win close timeout\n"); +} + +/* + * colorspace path: + * Input Win csc Output + * 1. YUV(2020) --> Y2R->2020To709->R2Y --> YUV_OUTPUT(601/709) + * RGB --> R2Y __/ + * + * 2. YUV(2020) --> bypasss --> YUV_OUTPUT(2020) + * RGB --> 709To2020->R2Y __/ + * + * 3. YUV(2020) --> Y2R->2020To709 --> RGB_OUTPUT(709) + * RGB --> R2Y __/ + * + * 4. YUV(601/709)-> Y2R->709To2020->R2Y --> YUV_OUTPUT(2020) + * RGB --> 709To2020->R2Y __/ + * + * 5. YUV(601/709)-> bypass --> YUV_OUTPUT(709) + * RGB --> R2Y __/ + * + * 6. YUV(601/709)-> bypass --> YUV_OUTPUT(601) + * RGB --> R2Y(601) __/ + * + * 7. YUV --> Y2R(709) --> RGB_OUTPUT(709) + * RGB --> bypass __/ + * + * 8. RGB --> 709To2020->R2Y --> YUV_OUTPUT(2020) + * + * 9. RGB --> R2Y(709) --> YUV_OUTPUT(709) + * + * 10. RGB --> R2Y(601) --> YUV_OUTPUT(601) + * + * 11. RGB --> bypass --> RGB_OUTPUT(709) + */ + +static void vop2_setup_csc_mode(struct rockchip_crtc_state *vcstate, + struct vop2_plane_state *vpstate) +{ + struct drm_plane_state *pstate = &vpstate->base; + int is_input_yuv = is_yuv_support(pstate->fb->format->format); + int is_output_yuv = is_yuv_output(vcstate->bus_format); + int input_csc = vpstate->color_space; + int output_csc = vcstate->color_space; + + vpstate->y2r_en = 0; + vpstate->r2y_en = 0; + vpstate->csc_mode = 0; + if (is_input_yuv && !is_output_yuv) { + vpstate->y2r_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(input_csc); + } else if (!is_input_yuv && is_output_yuv) { + vpstate->r2y_en = 1; + vpstate->csc_mode = vop2_convert_csc_mode(output_csc); + } +} + +static void vop2_axi_irqs_enable(struct vop2 *vop2) +{ + const struct vop2_data *vop2_data = vop2->data; + const struct vop_intr *intr; + uint32_t irqs = BUS_ERROR_INTR; + uint32_t i; + + for (i = 0; i < vop2_data->nr_axi_intr; i++) { + intr = &vop2_data->axi_intr[i]; + VOP_INTR_SET_TYPE(vop2, intr, clear, irqs, 1); + VOP_INTR_SET_TYPE(vop2, intr, enable, irqs, 1); + } +} + +static uint32_t vop2_read_and_clear_axi_irqs(struct vop2 *vop2, int index) +{ + const struct vop2_data *vop2_data = vop2->data; + const struct vop_intr *intr = &vop2_data->axi_intr[index]; + uint32_t irqs = BUS_ERROR_INTR; + uint32_t val; + + val = VOP_INTR_GET_TYPE(vop2, intr, status, irqs); + if (val) + VOP_INTR_SET_TYPE(vop2, intr, clear, val, 1); + + return val; +} + +static void vop2_dsp_hold_valid_irq_enable(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_intr *intr = vp_data->intr; + + unsigned long flags; + + if (WARN_ON(!vop2->is_enabled)) + return; + + spin_lock_irqsave(&vop2->irq_lock, flags); + + VOP_INTR_SET_TYPE(vop2, intr, clear, DSP_HOLD_VALID_INTR, 1); + VOP_INTR_SET_TYPE(vop2, intr, enable, DSP_HOLD_VALID_INTR, 1); + + spin_unlock_irqrestore(&vop2->irq_lock, flags); +} + +static void vop2_dsp_hold_valid_irq_disable(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_intr *intr = vp_data->intr; + unsigned long flags; + + if (WARN_ON(!vop2->is_enabled)) + return; + + spin_lock_irqsave(&vop2->irq_lock, flags); + + VOP_INTR_SET_TYPE(vop2, intr, enable, DSP_HOLD_VALID_INTR, 0); + + spin_unlock_irqrestore(&vop2->irq_lock, flags); +} + +/* + * (1) each frame starts at the start of the Vsync pulse which is signaled by + * the "FRAME_SYNC" interrupt. + * (2) the active data region of each frame ends at dsp_vact_end + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num, + * to get "LINE_FLAG" interrupt at the end of the active on screen data. + * + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end + * Interrupts + * LINE_FLAG -------------------------------+ + * FRAME_SYNC ----+ | + * | | + * v v + * | Vsync | Vbp | Vactive | Vfp | + * ^ ^ ^ ^ + * | | | | + * | | | | + * dsp_vs_end ------------+ | | | VOP_DSP_VTOTAL_VS_END + * dsp_vact_start --------------+ | | VOP_DSP_VACT_ST_END + * dsp_vact_end ----------------------------+ | VOP_DSP_VACT_ST_END + * dsp_total -------------------------------------+ VOP_DSP_VTOTAL_VS_END + */ + +static int vop2_core_clks_enable(struct vop2 *vop2) +{ + int ret; + + ret = clk_enable(vop2->hclk); + if (ret < 0) + return ret; + + ret = clk_enable(vop2->aclk); + if (ret < 0) + goto err_disable_hclk; + + return 0; + +err_disable_hclk: + clk_disable(vop2->hclk); + return ret; +} + +static void vop2_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static void rockchip_vop2_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, + u16 green, u16 blue, int regno) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + u32 lut_len = vop2->lut_len; + u32 r, g, b; + + if (regno >= lut_len || !vop2->lut) + return; + + r = red * (lut_len - 1) / 0xffff; + g = green * (lut_len - 1) / 0xffff; + b = blue * (lut_len - 1) / 0xffff; + vop2->lut[regno] = r * lut_len * lut_len + g * lut_len + b; +} + +static int vop2_core_clks_prepare_enable(struct vop2 *vop2) +{ + int ret; + + ret = clk_prepare_enable(vop2->hclk); + if (ret < 0) { + dev_err(vop2->dev, "failed to enable hclk - %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(vop2->aclk); + if (ret < 0) { + dev_err(vop2->dev, "failed to enable aclk - %d\n", ret); + goto err; + } + + return 0; +err: + clk_disable_unprepare(vop2->hclk); + + return ret; +} + +static void vop2_layer_map_initial(struct vop2 *vop2) +{ + const struct vop2_data *vop2_data = vop2->data; + struct vop2_layer *layer = &vop2->layers[0]; + struct vop2_win *win; + const struct vop2_win_data *win_data = NULL; + uint32_t layer_map, sel; + int i, j; + + layer_map = vop2_readl(vop2, layer->regs->layer_sel.offset); + + for (i = 0; i < vop2->data->nr_layers; i++) { + sel = (layer_map >> (4 * i)) & 0xf; + layer = &vop2->layers[i]; + for (j = 0; j < vop2_data->win_size; j++) { + win_data = &vop2_data->win[j]; + if (sel == win_data->layer_sel_id) + break; + win_data = NULL; + } + + if (!win_data) { + DRM_DEV_ERROR(vop2->dev, "invalid layer map :0x%x\n", layer_map); + return; + } + win = vop2_find_win_by_phys_id(vop2, win_data->phys_id); + layer->win_phys_id = win->phys_id; + win->layer_id = layer->id; + DRM_DEV_DEBUG(vop2->dev, "layer%d select %s\n", layer->id, win->name); + } +} + +static void vop2_initial(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + int ret; + + if (vop2->enable_count == 0) { + struct vop2_video_port *port; + int i; + + ret = pm_runtime_get_sync(vop2->dev); + if (ret < 0) { + DRM_DEV_ERROR(vop2->dev, "failed to get pm runtime: %d\n", ret); + return; + } + + ret = vop2_core_clks_prepare_enable(vop2); + if (ret) { + pm_runtime_put_sync(vop2->dev); + return; + } + + memcpy(vop2->regsbak, vop2->regs, vop2->len); + + VOP_CTRL_SET(vop2, cfg_done_en, 1); + /* + * Register OVERLAY_LAYER_SEL and OVERLAY_PORT_SEL should take effect immediately, + * than windows configuration(CLUSTER/ESMART/SMART) can take effect according the + * video port mux configuration as we wished. + */ + VOP_CTRL_SET(vop2, ovl_port_mux_cfg_done_imd, 1); + /* + * Let SYS_DSP_INFACE_EN/SYS_DSP_INFACE_CTRL/SYS_DSP_INFACE_POL take effect + * immediately. + */ + VOP_CTRL_SET(vop2, if_ctrl_cfg_done_imd, 1); + /* + * Default set all layers to the last Video Port, it will + * make things easier when we do layer/port mux independently + * for each crtc. + */ + for (i = 0; i < vop2->data->nr_vps - 1; i++) { + port = &vop2->vps[i]; + VOP_MODULE_SET(vop2, port, port_mux, 8); + } + + vop2_layer_map_initial(vop2); + + vop2_axi_irqs_enable(vop2); + + vop2->is_enabled = true; + } + + vop2->enable_count++; + + ret = clk_prepare_enable(vp->dclk); + if (ret < 0) + DRM_DEV_ERROR(vop2->dev, "failed to enable dclk for video port%d - %d\n", + vp->id, ret); +} + +static void vop2_disable(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + clk_disable_unprepare(vp->dclk); + + if (--vop2->enable_count > 0) + return; + + clk_disable_unprepare(vop2->aclk); + clk_disable_unprepare(vop2->hclk); +} + +static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + int sys_status = SYS_STATUS_LCDC0; + + WARN_ON(vp->event); + + vop2_lock(vop2); + drm_crtc_vblank_off(crtc); + vop2_disable_all_planes_for_crtc(crtc); + + /* + * Vop standby will take effect at end of current frame, + * if dsp hold valid irq happen, it means standby complete. + * + * we must wait standby complete when we want to disable aclk, + * if not, memory bus maybe dead. + */ + reinit_completion(&vp->dsp_hold_completion); + vop2_dsp_hold_valid_irq_enable(crtc); + + spin_lock(&vop2->reg_lock); + + VOP_MODULE_SET(vop2, vp, standby, 1); + + spin_unlock(&vop2->reg_lock); + + WARN_ON(!wait_for_completion_timeout(&vp->dsp_hold_completion, msecs_to_jiffies(50))); + + vop2_dsp_hold_valid_irq_disable(crtc); + + vop2->is_enabled = false; + if (vop2->is_iommu_enabled) { + /* + * vop2 standby complete, so iommu detach is safe. + */ + VOP_CTRL_SET(vop2, dma_stop, 1); + rockchip_drm_dma_detach_device(vop2->drm_dev, vop2->dev); + vop2->is_iommu_enabled = false; + } + + pm_runtime_put_sync(vop2->dev); + vop2_disable(crtc); + vop2_unlock(vop2); + + rockchip_clear_system_status(sys_status); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } +} + +static int vop2_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + if (plane->state->fb) + drm_framebuffer_get(plane->state->fb); + + return 0; +} + +static void vop2_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + if (old_state->fb) + drm_framebuffer_put(old_state->fb); +} + +static int vop2_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) +{ + struct vop2_plane_state *vpstate = to_vop2_plane_state(state); + struct vop2_win *win = to_vop2_win(plane); + struct drm_framebuffer *fb = state->fb; + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *cstate; + struct vop2_video_port *vp; + const struct vop2_data *vop2_data; + struct drm_rect *dest = &vpstate->dest; + struct drm_rect *src = &vpstate->src; + int min_scale = win->regs->scl ? FRAC_16_16(1, 8) : DRM_PLANE_HELPER_NO_SCALING; + int max_scale = win->regs->scl ? FRAC_16_16(8, 1) : DRM_PLANE_HELPER_NO_SCALING; + unsigned long offset; + dma_addr_t dma_addr; + void *kvaddr; + int ret; + + crtc = crtc ? crtc : plane->state->crtc; + if (!crtc || !fb) { + plane->state->visible = false; + return 0; + } + + vp = to_vop2_video_port(crtc); + vop2_data = vp->vop2->data; + + cstate = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!cstate)) + return -EINVAL; + + vpstate->xmirror_en = (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0; + vpstate->ymirror_en = (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0; + vpstate->rotate_270_en = (state->rotation & DRM_MODE_ROTATE_270) ? 1 : 0; + vpstate->rotate_90_en = (state->rotation & DRM_MODE_ROTATE_90) ? 1 : 0; + + if (vpstate->rotate_270_en && vpstate->rotate_90_en) { + DRM_ERROR("Can't rotate 90 and 270 at the same time\n"); + return -EINVAL; + } + + src->x1 = state->src_x; + src->y1 = state->src_y; + src->x2 = state->src_x + state->src_w; + src->y2 = state->src_y + state->src_h; + dest->x1 = state->crtc_x; + dest->y1 = state->crtc_y; + + dest->x2 = state->crtc_x + state->crtc_w; + dest->y2 = state->crtc_y + state->crtc_h; + + ret = drm_atomic_helper_check_plane_state(state, cstate, + min_scale, max_scale, + true, true); + if (ret) + return ret; + + if (!state->visible) + return 0; + + vpstate->zpos = state->zpos; + vpstate->global_alpha = state->alpha >> 8; + vpstate->blend_mode = state->pixel_blend_mode; + vpstate->format = vop2_convert_format(fb->format->format); + if (vpstate->format < 0) + return vpstate->format; + + if (drm_rect_width(src) >> 16 > vop2_data->max_input.width || + drm_rect_height(src) >> 16 > vop2_data->max_input.height) { + DRM_ERROR("Invalid source: %dx%d. max input: %dx%d\n", + drm_rect_width(src) >> 16, + drm_rect_height(src) >> 16, + vop2_data->max_input.width, + vop2_data->max_input.height); + return -EINVAL; + } + + if (rockchip_afbc(fb->modifier)) + vpstate->afbc_en = true; + else + vpstate->afbc_en = false; + + /* + * Src.x1 can be odd when do clip, but yuv plane start point + * need align with 2 pixel. + */ + if (fb->format->is_yuv && ((state->src.x1 >> 16) % 2)) { + DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n"); + return -EINVAL; + } + + offset = (src->x1 >> 16) * fb->format->bpp[0] / 8; + vpstate->offset = offset + fb->offsets[0]; + + /* + * DRM_MODE_REFLECT_Y by afbc don't need to add the offset. + */ + if (vpstate->ymirror_en && !vpstate->afbc_en) + offset += ((src->y2 >> 16) - 1) * fb->pitches[0]; + else + offset += (src->y1 >> 16) * fb->pitches[0]; + + dma_addr = rockchip_fb_get_dma_addr(fb, 0); + kvaddr = rockchip_fb_get_kvaddr(fb, 0); + vpstate->yrgb_mst = dma_addr + offset + fb->offsets[0]; + vpstate->yrgb_kvaddr = kvaddr + offset + fb->offsets[0]; + if (fb->format->is_yuv) { + int hsub = drm_format_horz_chroma_subsampling(fb->format->format); + int vsub = drm_format_vert_chroma_subsampling(fb->format->format); + + offset = (src->x1 >> 16) * fb->format->bpp[1] / hsub / 8; + offset += (src->y1 >> 16) * fb->pitches[1] / vsub; + + dma_addr = rockchip_fb_get_dma_addr(fb, 1); + dma_addr += offset + fb->offsets[1]; + vpstate->uv_mst = dma_addr; + } + + return 0; +} + +static void vop2_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) +{ + struct vop2_win *win = to_vop2_win(plane); + struct vop2 *vop2 = win->vop2; +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + struct vop2_plane_state *vpstate = to_vop2_plane_state(plane->state); +#endif + + DRM_DEV_DEBUG(vop2->dev, "%s disable\n", win->name); + + if (!old_state->crtc) + return; + + spin_lock(&vop2->reg_lock); + + vop2_win_disable(win); + +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + kfree(vpstate->planlist); + vpstate->planlist = NULL; +#endif + + spin_unlock(&vop2->reg_lock); +} + +static void vop2_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) +{ + struct drm_plane_state *pstate = plane->state; + struct drm_crtc *crtc = pstate->crtc; + struct vop2_win *win = to_vop2_win(plane); + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop2 *vop2 = win->vop2; + struct drm_framebuffer *fb = pstate->fb; + uint32_t actual_w, actual_h, dsp_w, dsp_h; + uint32_t dsp_stx, dsp_sty; + uint32_t act_info, dsp_info, dsp_st; + uint32_t format; + uint32_t afbc_format; + struct drm_rect *src = &vpstate->src; + struct drm_rect *dest = &vpstate->dest; + uint32_t afbc_tile_num; + uint32_t afbc_half_block_en; + uint32_t lb_mode; + +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + bool AFBC_flag = false; + struct vop2_dump_list *planlist; + unsigned long num_pages; + struct page **pages; + struct rockchip_drm_fb *rk_fb; + struct drm_gem_object *obj; + struct rockchip_gem_object *rk_obj; + + num_pages = 0; + pages = NULL; + rk_fb = to_rockchip_fb(fb); + obj = rk_fb->obj[0]; + rk_obj = to_rockchip_obj(obj); + if (rk_obj) { + num_pages = rk_obj->num_pages; + pages = rk_obj->pages; + } + if (fb->modifier == DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16)) + AFBC_flag = true; + else + AFBC_flag = false; +#endif + + /* + * can't update plane when vop2 is disabled. + */ + if (WARN_ON(!crtc)) + return; + + if (WARN_ON(!vop2->is_enabled)) + return; + + if (!pstate->visible) { + vop2_plane_atomic_disable(plane, old_state); + return; + } + + actual_w = drm_rect_width(src) >> 16; + actual_h = drm_rect_height(src) >> 16; + dsp_w = drm_rect_width(dest); + dsp_h = drm_rect_height(dest); + act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); + dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff); + + dsp_stx = dest->x1; + dsp_sty = dest->y1; + dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); + + format = vop2_convert_format(fb->format->format); + + vop2_setup_csc_mode(vcstate, vpstate); + + spin_lock(&vop2->reg_lock); + + if (vpstate->afbc_en) { + /* the afbc superblock is 16 x 16 */ + afbc_format = vop2_convert_afbc_format(fb->format->format); + afbc_tile_num = actual_w >> 4; + afbc_half_block_en = vop2_afbc_half_block_enable(vpstate); + VOP_AFBC_SET(vop2, win, enable, 1); + VOP_AFBC_SET(vop2, win, format, afbc_format); + VOP_AFBC_SET(vop2, win, block_split_en, 0); + VOP_AFBC_SET(vop2, win, half_block_en, afbc_half_block_en); + VOP_AFBC_SET(vop2, win, hdr_ptr, vpstate->yrgb_mst); + VOP_AFBC_SET(vop2, win, pic_size, act_info); + VOP_AFBC_SET(vop2, win, pic_vir_width, fb->width); + VOP_AFBC_SET(vop2, win, tile_num, afbc_tile_num); + VOP_AFBC_SET(vop2, win, xmirror, vpstate->xmirror_en); + VOP_AFBC_SET(vop2, win, ymirror, vpstate->ymirror_en); + VOP_AFBC_SET(vop2, win, rotate_270, vpstate->rotate_270_en); + VOP_AFBC_SET(vop2, win, rotate_90, vpstate->rotate_90_en); + } else { + VOP_AFBC_SET(vop2, win, enable, 0); + VOP_WIN_SET(vop2, win, ymirror, vpstate->xmirror_en); + VOP_WIN_SET(vop2, win, xmirror, vpstate->ymirror_en); + } + + if (vpstate->rotate_90_en || vpstate->rotate_270_en) { + act_info = swahw32(act_info); + actual_w = drm_rect_height(src) >> 16; + actual_h = drm_rect_width(src) >> 16; + } + + VOP_WIN_SET(vop2, win, format, format); + VOP_WIN_SET(vop2, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); + VOP_WIN_SET(vop2, win, yrgb_mst, vpstate->yrgb_mst); + + if (fb->format->is_yuv) { + VOP_WIN_SET(vop2, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); + VOP_WIN_SET(vop2, win, uv_mst, vpstate->uv_mst); + } + + vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format); + + lb_mode = vop2_get_cluster_lb_mode(win, vpstate); + VOP_WIN_SET(vop2, win, lb_mode, lb_mode); + + VOP_WIN_SET(vop2, win, act_info, act_info); + VOP_WIN_SET(vop2, win, dsp_info, dsp_info); + VOP_WIN_SET(vop2, win, dsp_st, dsp_st); + + VOP_WIN_SET(vop2, win, y2r_en, vpstate->y2r_en); + VOP_WIN_SET(vop2, win, r2y_en, vpstate->r2y_en); + VOP_WIN_SET(vop2, win, csc_mode, vpstate->csc_mode); + VOP_WIN_SET(vop2, win, enable, 1); + VOP_WIN_SET(vop2, win, gate, 1); + spin_unlock(&vop2->reg_lock); + + vop2->is_iommu_needed = true; +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + kfree(vop2_plane_state->planlist); + vop2_plane_state->planlist = NULL; + + planlist = kmalloc(sizeof(*planlist), GFP_KERNEL); + if (planlist) { + planlist->dump_info.AFBC_flag = AFBC_flag; + planlist->dump_info.area_id = win->area_id; + planlist->dump_info.win_id = win->win_id; + planlist->dump_info.yuv_format = + is_yuv_support(fb->format->format); + planlist->dump_info.num_pages = num_pages; + planlist->dump_info.pages = pages; + planlist->dump_info.offset = vop2_plane_state->offset; + planlist->dump_info.pitches = fb->pitches[0]; + planlist->dump_info.height = actual_h; + planlist->dump_info.pixel_format = fb->format->format; + list_add_tail(&planlist->entry, &crtc->vop2_dump_list_head); + vop2_plane_state->planlist = planlist; + } else { + DRM_ERROR("can't alloc a node of planlist %p\n", planlist); + return; + } + if (crtc->vop2_dump_status == DUMP_KEEP || + crtc->vop2_dump_times > 0) { + vop2_plane_dump(&planlist->dump_info, crtc->frame_count); + crtc->vop2_dump_times--; + } +#endif +} + +static const struct drm_plane_helper_funcs vop2_plane_helper_funcs = { + .prepare_fb = vop2_plane_prepare_fb, + .cleanup_fb = vop2_plane_cleanup_fb, + .atomic_check = vop2_plane_atomic_check, + .atomic_update = vop2_plane_atomic_update, + .atomic_disable = vop2_plane_atomic_disable, +}; + +/** + * rockchip_atomic_helper_update_plane copy from drm_atomic_helper_update_plane + * be designed to support async commit at ioctl DRM_IOCTL_MODE_SETPLANE. + * @plane: plane object to update + * @crtc: owning CRTC of owning plane + * @fb: framebuffer to flip onto plane + * @crtc_x: x offset of primary plane on crtc + * @crtc_y: y offset of primary plane on crtc + * @crtc_w: width of primary plane rectangle on crtc + * @crtc_h: height of primary plane rectangle on crtc + * @src_x: x offset of @fb for panning + * @src_y: y offset of @fb for panning + * @src_w: width of source rectangle in @fb + * @src_h: height of source rectangle in @fb + * @ctx: lock acquire context + * + * Provides a default plane update handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +static int __maybe_unused +rockchip_atomic_helper_update_plane(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_plane_state *pstate; + struct vop2_plane_state *vpstate; + int ret = 0; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = ctx; + pstate = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(pstate)) { + ret = PTR_ERR(pstate); + goto fail; + } + + vpstate = to_vop2_plane_state(pstate); + + ret = drm_atomic_set_crtc_for_plane(pstate, crtc); + if (ret != 0) + goto fail; + drm_atomic_set_fb_for_plane(pstate, fb); + pstate->crtc_x = crtc_x; + pstate->crtc_y = crtc_y; + pstate->crtc_w = crtc_w; + pstate->crtc_h = crtc_h; + pstate->src_x = src_x; + pstate->src_y = src_y; + pstate->src_w = src_w; + pstate->src_h = src_h; + + if (plane == crtc->cursor || vpstate->async_commit) + state->legacy_cursor_update = true; + + ret = drm_atomic_commit(state); +fail: + drm_atomic_state_put(state); + return ret; +} + +/** + * drm_atomic_helper_disable_plane copy from drm_atomic_helper_disable_plane + * be designed to support async commit at ioctl DRM_IOCTL_MODE_SETPLANE. + * + * @plane: plane to disable + * @ctx: lock acquire context + * + * Provides a default plane disable handler using the atomic driver interface. + * + * RETURNS: + * Zero on success, error code on failure + */ +static int __maybe_unused +rockchip_atomic_helper_disable_plane(struct drm_plane *plane, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_plane_state *pstate; + struct vop2_plane_state *vpstate; + int ret = 0; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = ctx; + pstate = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(pstate)) { + ret = PTR_ERR(pstate); + goto fail; + } + vpstate = to_vop2_plane_state(pstate); + + if ((pstate->crtc && pstate->crtc->cursor == plane) || + vpstate->async_commit) + pstate->state->legacy_cursor_update = true; + + ret = __drm_atomic_helper_disable_plane(plane, pstate); + if (ret != 0) + goto fail; + + ret = drm_atomic_commit(state); +fail: + drm_atomic_state_put(state); + return ret; +} + +static void vop2_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); +} + +static void vop2_atomic_plane_reset(struct drm_plane *plane) +{ + struct vop2_plane_state *vpstate = to_vop2_plane_state(plane->state); + struct vop2_win *win = to_vop2_win(plane); + + if (plane->state && plane->state->fb) + __drm_atomic_helper_plane_destroy_state(plane->state); + kfree(vpstate); + vpstate = kzalloc(sizeof(*vpstate), GFP_KERNEL); + if (!vpstate) + return; + + plane->state = &vpstate->base; + plane->state->plane = plane; + plane->state->zpos = win->zpos; + plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE; +} + +static struct drm_plane_state *vop2_atomic_plane_duplicate_state(struct drm_plane *plane) +{ + struct vop2_plane_state *old_vpstate; + struct vop2_plane_state *vpstate; + + if (WARN_ON(!plane->state)) + return NULL; + + old_vpstate = to_vop2_plane_state(plane->state); + vpstate = kmemdup(old_vpstate, sizeof(*vpstate), GFP_KERNEL); + if (!vpstate) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, &vpstate->base); + + return &vpstate->base; +} + +static void vop2_atomic_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vop2_plane_state *vpstate = to_vop2_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + + kfree(vpstate); +} + +static int vop2_atomic_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t val) +{ + struct rockchip_drm_private *private = plane->dev->dev_private; + struct vop2_plane_state *vpstate = to_vop2_plane_state(state); + + if (property == private->eotf_prop) { + vpstate->eotf = val; + return 0; + } + + if (property == private->color_space_prop) { + vpstate->color_space = val; + return 0; + } + + if (property == private->async_commit_prop) { + vpstate->async_commit = val; + return 0; + } + + DRM_ERROR("failed to set vop2 plane property id:%d, name:%s\n", + property->base.id, property->name); + + return -EINVAL; +} + +static int vop2_atomic_plane_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct rockchip_drm_private *private = plane->dev->dev_private; + struct vop2_plane_state *vpstate = to_vop2_plane_state(state); + + if (property == private->eotf_prop) { + *val = vpstate->eotf; + return 0; + } + + if (property == private->color_space_prop) { + *val = vpstate->color_space; + return 0; + } + + if (property == private->async_commit_prop) { + *val = vpstate->async_commit; + return 0; + } + + if (property == private->share_id_prop) { + int i; + struct drm_mode_object *obj = &plane->base; + + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->properties[i] == property) { + *val = obj->properties->values[i]; + return 0; + } + } + } + + DRM_ERROR("failed to get vop2 plane property id:%d, name:%s\n", + property->base.id, property->name); + + return -EINVAL; +} + +static const struct drm_plane_funcs vop2_plane_funcs = { + .update_plane = rockchip_atomic_helper_update_plane, + .disable_plane = rockchip_atomic_helper_disable_plane, + .destroy = vop2_plane_destroy, + .reset = vop2_atomic_plane_reset, + .atomic_duplicate_state = vop2_atomic_plane_duplicate_state, + .atomic_destroy_state = vop2_atomic_plane_destroy_state, + .atomic_set_property = vop2_atomic_plane_set_property, + .atomic_get_property = vop2_atomic_plane_get_property, +}; + +static int vop2_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_intr *intr = vp_data->intr; + unsigned long flags; + + if (WARN_ON(!vop2->is_enabled)) + return -EPERM; + + spin_lock_irqsave(&vop2->irq_lock, flags); + + VOP_INTR_SET_TYPE(vop2, intr, clear, FS_FIELD_INTR, 1); + VOP_INTR_SET_TYPE(vop2, intr, enable, FS_FIELD_INTR, 1); + + spin_unlock_irqrestore(&vop2->irq_lock, flags); + + return 0; +} + +static void vop2_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_intr *intr = vp_data->intr; + unsigned long flags; + + if (WARN_ON(!vop2->is_enabled)) + return; + + spin_lock_irqsave(&vop2->irq_lock, flags); + + VOP_INTR_SET_TYPE(vop2, intr, enable, FS_FIELD_INTR, 0); + + spin_unlock_irqrestore(&vop2->irq_lock, flags); +} + +static void vop2_crtc_cancel_pending_vblank(struct drm_crtc *crtc, + struct drm_file *file_priv) +{ + struct drm_device *drm = crtc->dev; + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_pending_vblank_event *e; + unsigned long flags; + + spin_lock_irqsave(&drm->event_lock, flags); + e = vp->event; + if (e && e->base.file_priv == file_priv) { + vp->event = NULL; + + //e->base.destroy(&e->base);//todo + file_priv->event_space += sizeof(e->event); + } + spin_unlock_irqrestore(&drm->event_lock, flags); +} + +static int vop2_crtc_loader_protect(struct drm_crtc *crtc, bool on) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + int sys_status = SYS_STATUS_LCDC0; + + if (on == vop2->loader_protect) + return 0; + + if (on) { + rockchip_set_system_status(sys_status); + vop2_initial(crtc); + drm_crtc_vblank_on(crtc); + vop2->loader_protect = true; + } else { + vop2_crtc_atomic_disable(crtc, NULL); + vop2->loader_protect = false; + } + + return 0; +} + +#define DEBUG_PRINT(args...) \ + do { \ + if (s) \ + seq_printf(s, args); \ + else \ + pr_err(args); \ + } while (0) + +static int vop2_plane_info_dump(struct seq_file *s, struct drm_plane *plane) +{ + struct vop2_win *win = to_vop2_win(plane); + struct drm_plane_state *pstate = plane->state; + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + struct drm_rect *src, *dest; + struct drm_framebuffer *fb = pstate->fb; + struct drm_format_name_buf format_name; + int i; + + DEBUG_PRINT(" %s: %s\n", win->name, pstate->crtc ? "ACTIVE" : "DISABLED"); + if (!fb) + return 0; + + src = &vpstate->src; + dest = &vpstate->dest; + + drm_get_format_name(fb->format->format, &format_name); + DEBUG_PRINT("\tformat: %s%s%s[%d] color_space[%d]\n", + format_name.str, + rockchip_afbc(fb->modifier) ? "[AFBC]" : "", + vpstate->eotf ? " HDR" : " SDR", vpstate->eotf, + vpstate->color_space); + DEBUG_PRINT("\tcsc: y2r[%d] r2y[%d] csc mode[%d]\n", + vpstate->y2r_en, vpstate->r2y_en, + vpstate->csc_mode); + DEBUG_PRINT("\tzpos: %d\n", vpstate->zpos); + DEBUG_PRINT("\tsrc: pos[%dx%d] rect[%dx%d]\n", src->x1 >> 16, + src->y1 >> 16, drm_rect_width(src) >> 16, + drm_rect_height(src) >> 16); + DEBUG_PRINT("\tdst: pos[%dx%d] rect[%dx%d]\n", dest->x1, dest->y1, + drm_rect_width(dest), drm_rect_height(dest)); + + for (i = 0; i < drm_format_num_planes(fb->format->format); i++) { + dma_addr_t fb_addr = rockchip_fb_get_dma_addr(fb, i); + + DEBUG_PRINT("\tbuf[%d]: addr: %pad pitch: %d offset: %d\n", + i, &fb_addr, fb->pitches[i], fb->offsets[i]); + } + + return 0; +} + +static int vop2_crtc_debugfs_dump(struct drm_crtc *crtc, struct seq_file *s) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_crtc_state *crtc_state = crtc->state; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct rockchip_crtc_state *state = to_rockchip_crtc_state(crtc->state); + bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + struct drm_plane *plane; + + DEBUG_PRINT("Video Port%d: %s\n", vp->id, crtc_state->active ? "ACTIVE" : "DISABLED"); + + if (!crtc_state->active) + return 0; + + DEBUG_PRINT(" Connector: %s\n", + drm_get_connector_name(state->output_type)); + DEBUG_PRINT("\tbus_format[%x]: %s\n", state->bus_format, + drm_get_bus_format_name(state->bus_format)); + DEBUG_PRINT("\toverlay_mode[%d] output_mode[%x]", + state->yuv_overlay, state->output_mode); + DEBUG_PRINT(" color_space[%d]\n", + state->color_space); + DEBUG_PRINT(" Display mode: %dx%d%s%d\n", + mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p", + drm_mode_vrefresh(mode)); + DEBUG_PRINT("\tclk[%d] real_clk[%d] type[%x] flag[%x]\n", + mode->clock, mode->crtc_clock, mode->type, mode->flags); + DEBUG_PRINT("\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal); + DEBUG_PRINT("\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal); + + drm_atomic_crtc_for_each_plane(plane, crtc) { + vop2_plane_info_dump(s, plane); + } + + return 0; +} + +static void vop2_crtc_regs_dump(struct drm_crtc *crtc, struct seq_file *s) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_crtc_state *crtc_state = crtc->state; + int dump_len = vop2->len > 0x400 ? 0x400 : vop2->len; + int i; + + if (!crtc_state->active) + return; + + for (i = 0; i < dump_len; i += 4) { + if (i % 16 == 0) + DEBUG_PRINT("\n0x%08x: ", i); + DEBUG_PRINT("%08x ", vop2_readl(vop2, i)); + } +} + +static int vop2_gamma_show(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct vop2 *vop2 = node->info_ent->data; + int i; + + if (!vop2->lut || !vop2->lut_active || !vop2->lut_regs) + return 0; + + for (i = 0; i < vop2->lut_len; i++) { + if (i % 8 == 0) + DEBUG_PRINT("\n"); + DEBUG_PRINT("0x%08x ", vop2->lut[i]); + } + DEBUG_PRINT("\n"); + + return 0; +} + +#undef DEBUG_PRINT + +static struct drm_info_list vop2_debugfs_files[] = { + { "gamma_lut", vop2_gamma_show, 0, NULL }, +}; + +static int vop2_crtc_debugfs_init(struct drm_minor *minor, struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + int ret, i; + + vop2->debugfs = debugfs_create_dir(dev_name(vop2->dev), + minor->debugfs_root); + + if (!vop2->debugfs) + return -ENOMEM; + + vop2->debugfs_files = kmemdup(vop2_debugfs_files, + sizeof(vop2_debugfs_files), + GFP_KERNEL); + if (!vop2->debugfs_files) { + ret = -ENOMEM; + goto remove; + } +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + drm_debugfs_vop2_add(crtc, vop2->debugfs); +#endif + for (i = 0; i < ARRAY_SIZE(vop2_debugfs_files); i++) + vop2->debugfs_files[i].data = vop2; + + ret = drm_debugfs_create_files(vop2->debugfs_files, + ARRAY_SIZE(vop2_debugfs_files), + vop2->debugfs, + minor); + if (ret) { + dev_err(vop2->dev, "could not install rockchip_debugfs_list\n"); + goto free; + } + + return 0; +free: + kfree(vop2->debugfs_files); + vop2->debugfs_files = NULL; +remove: + debugfs_remove(vop2->debugfs); + vop2->debugfs = NULL; + return ret; +} + +static enum drm_mode_status +vop2_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode, + int output_type) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + int request_clock = mode->clock; + int clock; + + if (mode->hdisplay > vop2_data->max_output.width) + return MODE_BAD_HVALUE; + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && + VOP_MAJOR(vop2->version) == 3 && + VOP_MINOR(vop2->version) <= 2) + return MODE_BAD; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + request_clock *= 2; + clock = clk_round_rate(vp->dclk, request_clock * 1000) / 1000; + + /* + * Hdmi or DisplayPort request a Accurate clock. + */ + if (output_type == DRM_MODE_CONNECTOR_HDMIA || + output_type == DRM_MODE_CONNECTOR_DisplayPort) + if (clock != request_clock) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + +struct vop2_bandwidth { + size_t bandwidth; + int y1; + int y2; +}; + +static int vop2_bandwidth_cmp(const void *a, const void *b) +{ + struct vop2_bandwidth *pa = (struct vop2_bandwidth *)a; + struct vop2_bandwidth *pb = (struct vop2_bandwidth *)b; + + return pa->y1 - pb->y2; +} + +static size_t vop2_plane_line_bandwidth(struct drm_plane_state *pstate) +{ + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + struct drm_framebuffer *fb = pstate->fb; + struct drm_rect *dst = &vpstate->dest; + struct drm_rect *src = &vpstate->src; + int bpp = fb->format->bpp[0]; + int src_width = drm_rect_width(src) >> 16; + int src_height = drm_rect_height(src) >> 16; + int dst_width = drm_rect_width(dst); + int dst_height = drm_rect_height(dst); + int vskiplines = scl_get_vskiplines(src_height, dst_height); + size_t bandwidth; + + if (src_width <= 0 || src_height <= 0 || dst_width <= 0 || + dst_height <= 0) + return 0; + + bandwidth = src_width * bpp / 8; + + bandwidth = bandwidth * src_width / dst_width; + bandwidth = bandwidth * src_height / dst_height; + if (vskiplines == 2) + bandwidth /= 2; + else if (vskiplines == 4) + bandwidth /= 4; + + return bandwidth; +} + +static u64 vop2_calc_max_bandwidth(struct vop2_bandwidth *bw, int start, + int count, int y2) +{ + u64 max_bandwidth = 0; + int i; + + for (i = start; i < count; i++) { + u64 bandwidth = 0; + + if (bw[i].y1 > y2) + continue; + bandwidth = bw[i].bandwidth; + bandwidth += vop2_calc_max_bandwidth(bw, i + 1, count, + min(bw[i].y2, y2)); + + if (bandwidth > max_bandwidth) + max_bandwidth = bandwidth; + } + + return max_bandwidth; +} + +static size_t vop2_crtc_bandwidth(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state, + unsigned int *plane_num_total) +{ + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + uint16_t htotal = adjusted_mode->crtc_htotal; + uint16_t vdisplay = adjusted_mode->crtc_vdisplay; + int clock = adjusted_mode->crtc_clock; + struct drm_atomic_state *state = crtc_state->state; + struct vop2_plane_state *vpstate; + struct drm_plane_state *pstate; + struct vop2_bandwidth *pbandwidth; + struct drm_plane *plane; + uint64_t bandwidth; + int8_t cnt = 0, plane_num = 0; +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + struct vop2_dump_list *pos, *n; +#endif + + if (!htotal || !vdisplay) + return 0; + +#if defined(CONFIG_ROCKCHIP_DRM_DEBUG) + if (!crtc->vop2_dump_list_init_flag) { + INIT_LIST_HEAD(&crtc->vop2_dump_list_head); + crtc->vop2_dump_list_init_flag = true; + } + list_for_each_entry_safe(pos, n, &crtc->vop2_dump_list_head, entry) { + list_del(&pos->entry); + } + if (crtc->vop2_dump_status == DUMP_KEEP || + crtc->vop2_dump_times > 0) { + crtc->frame_count++; + } +#endif + + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) + plane_num++; + + if (plane_num_total) + *plane_num_total += plane_num; + pbandwidth = kmalloc_array(plane_num, sizeof(*pbandwidth), + GFP_KERNEL); + if (!pbandwidth) + return -ENOMEM; + drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { + pstate = drm_atomic_get_plane_state(state, plane); + if (pstate->crtc != crtc || !pstate->fb) + continue; + + vpstate = to_vop2_plane_state(pstate); + pbandwidth[cnt].y1 = vpstate->dest.y1; + pbandwidth[cnt].y2 = vpstate->dest.y2; + pbandwidth[cnt++].bandwidth = vop2_plane_line_bandwidth(pstate); + } + + sort(pbandwidth, cnt, sizeof(pbandwidth[0]), vop2_bandwidth_cmp, NULL); + + bandwidth = vop2_calc_max_bandwidth(pbandwidth, 0, cnt, vdisplay); + kfree(pbandwidth); + /* + * bandwidth(MB/s) + * = line_bandwidth / line_time + * = line_bandwidth(Byte) * clock(KHZ) / 1000 / htotal + */ + bandwidth *= clock; + do_div(bandwidth, htotal * 1000); + + return bandwidth; +} + +static void vop2_crtc_close(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + if (!crtc) + return; + + mutex_lock(&vop2->vop2_lock); + if (!vop2->is_enabled) { + mutex_unlock(&vop2->vop2_lock); + return; + } + + vop2_disable_all_planes_for_crtc(crtc); + mutex_unlock(&vop2->vop2_lock); +} + +static const struct rockchip_crtc_funcs private_crtc_funcs = { + .loader_protect = vop2_crtc_loader_protect, + .cancel_pending_vblank = vop2_crtc_cancel_pending_vblank, + .debugfs_init = vop2_crtc_debugfs_init, + .debugfs_dump = vop2_crtc_debugfs_dump, + .regs_dump = vop2_crtc_regs_dump, + .mode_valid = vop2_crtc_mode_valid, + .bandwidth = vop2_crtc_bandwidth, + .crtc_close = vop2_crtc_close, +}; + +static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + + drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + adj_mode->crtc_clock *= 2; + + adj_mode->crtc_clock = DIV_ROUND_UP(clk_round_rate(vp->dclk, + adj_mode->crtc_clock * 1000), 1000); + + return true; +} + +static void vop2_dither_setup(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + switch (vcstate->bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + VOP_MODULE_SET(vop2, vp, dither_down_en, 1); + VOP_MODULE_SET(vop2, vp, dither_down_mode, RGB888_TO_RGB565); + break; + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA: + VOP_MODULE_SET(vop2, vp, dither_down_en, 1); + VOP_MODULE_SET(vop2, vp, dither_down_mode, RGB888_TO_RGB666); + break; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + VOP_MODULE_SET(vop2, vp, dither_down_en, 0); + VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 1); + break; + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + VOP_MODULE_SET(vop2, vp, dither_down_en, 0); + VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 0); + break; + case MEDIA_BUS_FMT_SRGB888_3X8: + case MEDIA_BUS_FMT_SRGB888_DUMMY_4X8: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: + default: + VOP_MODULE_SET(vop2, vp, dither_down_en, 0); + VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 0); + break; + } + + VOP_MODULE_SET(vop2, vp, pre_dither_down_en, + vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA ? 0 : 1); + VOP_MODULE_SET(vop2, vp, dither_down_sel, DITHER_DOWN_ALLEGRO); +} + +/* + * if adjusted mode update, return true, else return false + */ +static bool vop2_crtc_mode_update(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + u16 hsync_len = adjusted_mode->crtc_hsync_end - + adjusted_mode->crtc_hsync_start; + u16 hdisplay = adjusted_mode->crtc_hdisplay; + u16 htotal = adjusted_mode->crtc_htotal; + u16 hact_st = adjusted_mode->crtc_htotal - + adjusted_mode->crtc_hsync_start; + u16 hact_end = hact_st + hdisplay; + u16 vdisplay = adjusted_mode->crtc_vdisplay; + u16 vtotal = adjusted_mode->crtc_vtotal; + u16 vsync_len = adjusted_mode->crtc_vsync_end - + adjusted_mode->crtc_vsync_start; + u16 vact_st = adjusted_mode->crtc_vtotal - + adjusted_mode->crtc_vsync_start; + u16 vact_end = vact_st + vdisplay; + u32 htotal_sync = htotal << 16 | hsync_len; + u32 hactive_st_end = hact_st << 16 | hact_end; + u32 vtotal_sync = vtotal << 16 | vsync_len; + u32 vactive_st_end = vact_st << 16 | vact_end; + u32 crtc_clock = adjusted_mode->crtc_clock * 100; + + if (htotal_sync != VOP_MODULE_GET(vop2, vp, htotal_pw) || + hactive_st_end != VOP_MODULE_GET(vop2, vp, hact_st_end) || + vtotal_sync != VOP_MODULE_GET(vop2, vp, vtotal_pw) || + vactive_st_end != VOP_MODULE_GET(vop2, vp, vact_st_end) || + crtc_clock != clk_get_rate(vp->dclk)) + return true; + + return false; +} + +static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_intr *intr = vp_data->intr; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + u16 hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + u16 hdisplay = adjusted_mode->crtc_hdisplay; + u16 htotal = adjusted_mode->crtc_htotal; + u16 hact_st = adjusted_mode->crtc_htotal - adjusted_mode->crtc_hsync_start; + u16 hact_end = hact_st + hdisplay; + u16 vdisplay = adjusted_mode->crtc_vdisplay; + u16 vtotal = adjusted_mode->crtc_vtotal; + u16 vsync_len = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; + u16 vact_st = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start; + u16 vact_end = vact_st + vdisplay; + bool interlaced = !!(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE); + int sys_status = SYS_STATUS_LCDC0; + int for_ddr_freq = 0; + bool dclk_inv; + int act_end; + uint32_t val; + + rockchip_set_system_status(sys_status); + vop2_lock(vop2); + DRM_DEV_INFO(vop2->dev, "Update mode to %dx%d%s%d, type: %d for vp%d\n", + hdisplay, vdisplay, interlaced ? "i" : "p", + adjusted_mode->vrefresh, vcstate->output_type, vp->id); + vop2_initial(crtc); + VOP_MODULE_SET(vop2, vp, standby, 0); + vcstate->mode_update = vop2_crtc_mode_update(crtc); + if (vcstate->mode_update) + vop2_disable_all_planes_for_crtc(crtc); + /* + * restore the lut table. + */ + if (vop2->lut_active) + vop2_crtc_load_lut(crtc); + + dclk_inv = (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0; + val = (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : BIT(HSYNC_POSITIVE); + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : BIT(VSYNC_POSITIVE); + + if (vcstate->output_if & VOP_OUTPUT_IF_RGB) { + VOP_CTRL_SET(vop2, rgb_en, 1); + VOP_CTRL_SET(vop2, rgb_mux, vp_data->id); + VOP_GRF_SET(vop2, grf_dclk_inv, dclk_inv); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_BT1120) { + VOP_CTRL_SET(vop2, bt1120_en, 1); + VOP_CTRL_SET(vop2, rgb_mux, vp_data->id); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_BT656) { + VOP_CTRL_SET(vop2, bt656_en, 1); + VOP_CTRL_SET(vop2, rgb_mux, vp_data->id); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_LVDS0) { + VOP_CTRL_SET(vop2, lvds0_en, 1); + VOP_CTRL_SET(vop2, lvds0_mux, vp_data->id); + VOP_CTRL_SET(vop2, lvds_pin_pol, val); + VOP_CTRL_SET(vop2, lvds_dclk_pol, dclk_inv); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_LVDS1) { + VOP_CTRL_SET(vop2, lvds1_en, 1); + VOP_CTRL_SET(vop2, lvds1_mux, vp_data->id); + VOP_CTRL_SET(vop2, lvds_pin_pol, val); + VOP_CTRL_SET(vop2, lvds_dclk_pol, dclk_inv); + } + + if (vcstate->output_flags & (ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE | + ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE)) { + VOP_CTRL_SET(vop2, lvds_dual_en, 1); + if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE) + VOP_CTRL_SET(vop2, lvds_dual_mode, 1); + if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) + VOP_CTRL_SET(vop2, lvds_dual_channel_swap, 1); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_MIPI0) { + VOP_CTRL_SET(vop2, mipi0_en, 1); + VOP_CTRL_SET(vop2, mipi0_mux, vp_data->id); + VOP_CTRL_SET(vop2, mipi_pin_pol, val); + VOP_CTRL_SET(vop2, mipi_dclk_pol, dclk_inv); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_MIPI1) { + VOP_CTRL_SET(vop2, mipi1_en, 1); + VOP_CTRL_SET(vop2, mipi1_mux, vp_data->id); + VOP_CTRL_SET(vop2, mipi_pin_pol, val); + VOP_CTRL_SET(vop2, mipi_dclk_pol, dclk_inv); + } + + if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE) { + VOP_MODULE_SET(vop2, vp, mipi_dual_en, 1); + if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) + VOP_MODULE_SET(vop2, vp, mipi_dual_channel_swap, 1); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_eDP0) { + VOP_CTRL_SET(vop2, edp0_en, 1); + VOP_CTRL_SET(vop2, edp0_mux, vp_data->id); + VOP_CTRL_SET(vop2, edp_pin_pol, val); + VOP_CTRL_SET(vop2, edp_dclk_pol, dclk_inv); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_eDP1) { + VOP_CTRL_SET(vop2, edp1_en, 1); + VOP_CTRL_SET(vop2, edp1_mux, vp_data->id); + VOP_CTRL_SET(vop2, edp_pin_pol, val); + VOP_CTRL_SET(vop2, edp_dclk_pol, dclk_inv); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_DP0) { + VOP_CTRL_SET(vop2, dp0_en, 1); + VOP_CTRL_SET(vop2, dp0_mux, vp_data->id); + VOP_CTRL_SET(vop2, dp_dclk_pol, 0); + VOP_CTRL_SET(vop2, dp_pin_pol, val); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_DP1) { + VOP_CTRL_SET(vop2, dp1_en, 1); + VOP_CTRL_SET(vop2, dp1_mux, vp_data->id); + VOP_CTRL_SET(vop2, dp_dclk_pol, 0); + VOP_CTRL_SET(vop2, dp_pin_pol, val); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_HDMI0) { + VOP_CTRL_SET(vop2, hdmi0_en, 1); + VOP_CTRL_SET(vop2, hdmi0_mux, vp_data->id); + VOP_CTRL_SET(vop2, hdmi_pin_pol, val); + VOP_CTRL_SET(vop2, hdmi_dclk_pol, 1); + } + + if (vcstate->output_if & VOP_OUTPUT_IF_HDMI1) { + VOP_CTRL_SET(vop2, hdmi1_en, 1); + VOP_CTRL_SET(vop2, hdmi1_mux, vp_data->id); + VOP_CTRL_SET(vop2, hdmi_pin_pol, val); + VOP_CTRL_SET(vop2, hdmi_dclk_pol, 1); + } + + VOP_MODULE_SET(vop2, vp, out_mode, vcstate->output_mode); + VOP_MODULE_SET(vop2, vp, overlay_mode, is_yuv_output(vcstate->bus_format)); + + vop2_dither_setup(crtc); + + VOP_MODULE_SET(vop2, vp, htotal_pw, (htotal << 16) | hsync_len); + val = hact_st << 16; + val |= hact_end; + VOP_MODULE_SET(vop2, vp, hact_st_end, val); + VOP_MODULE_SET(vop2, vp, hpost_st_end, val); + + val = vact_st << 16; + val |= vact_end; + VOP_MODULE_SET(vop2, vp, vact_st_end, val); + VOP_MODULE_SET(vop2, vp, vpost_st_end, val); + + val = scl_cal_scale2(vdisplay, vdisplay) << 16; + val |= scl_cal_scale2(hdisplay, hdisplay); + VOP_MODULE_SET(vop2, vp, post_scl_factor, val); + VOP_MODULE_SET(vop2, vp, post_scl_ctrl, 0); + + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { + u16 vact_st_f1 = vtotal + vact_st + 1; + u16 vact_end_f1 = vact_st_f1 + vdisplay; + + val = vact_st_f1 << 16 | vact_end_f1; + VOP_MODULE_SET(vop2, vp, vact_st_end_f1, val); + VOP_MODULE_SET(vop2, vp, vpost_st_end_f1, val); + + val = vtotal << 16 | (vtotal + vsync_len); + VOP_MODULE_SET(vop2, vp, vs_st_end_f1, val); + VOP_MODULE_SET(vop2, vp, dsp_interlace, 1); + VOP_MODULE_SET(vop2, vp, p2i_en, 1); + vtotal += vtotal + 1; + act_end = vact_end_f1; + } else { + VOP_MODULE_SET(vop2, vp, dsp_interlace, 0); + VOP_MODULE_SET(vop2, vp, p2i_en, 0); + act_end = vact_end; + } + + VOP_INTR_SET(vop2, intr, line_flag_num[0], act_end); + VOP_INTR_SET(vop2, intr, line_flag_num[1], + act_end - us_to_vertical_line(adjusted_mode, for_ddr_freq)); + + VOP_MODULE_SET(vop2, vp, vtotal_pw, vtotal << 16 | vsync_len); + + VOP_MODULE_SET(vop2, vp, core_dclk_div, !!(adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)); + + clk_set_rate(vp->dclk, adjusted_mode->crtc_clock * 1000); + + vop2_cfg_done(crtc); + + drm_crtc_vblank_on(crtc); + + vop2_unlock(vop2); +} + +static int vop2_zpos_cmp(const void *a, const void *b) +{ + struct vop2_zpos *pa = (struct vop2_zpos *)a; + struct vop2_zpos *pb = (struct vop2_zpos *)b; + + return pa->zpos - pb->zpos; +} + +static int vop2_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + return 0; +} + +static void vop2_setup_hdr10(struct vop2_video_port *vp, uint8_t win_phys_id) +{ + struct vop2 *vop2 = vp->vop2; + struct vop2_win *win = vop2_find_win_by_phys_id(vop2, win_phys_id); + struct drm_plane *plane = &win->base; + struct drm_plane_state *pstate = plane->state; + struct vop2_plane_state *vpstate = to_vop2_plane_state(pstate); + struct drm_crtc_state *cstate = vp->crtc.state; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(cstate); + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_hdr_table *hdr_table = vp_data->hdr_table; + int hdr_en = 0; + int hdr2sdr_en = 0; + int sdr2hdr_en = 0; + int sdr2hdr_tf = 0; + int sdr2hdr_r2r_mode = 0; + + /* + * Check whether this video port support hdr or not + */ + if (!hdr_table) + return; + + /* + * HDR video plane input + */ + if (vpstate->eotf == SMPTE_ST2084) + hdr_en = 1; + + vp->hdr_en = hdr_en; + vp->hdr_in = hdr_en; + vp->hdr_out = (vcstate->eotf == SMPTE_ST2084) ? true : false; + + /* + * only laryer0 support hdr2sdr + * if we have more than one active win attached to the video port, + * the other attached win must for ui, and should do sdr2hdr. + * + */ + if (hdr_en) { + if (vcstate->eotf < SMPTE_ST2084) + hdr2sdr_en = 1; + if ((vcstate->eotf == SMPTE_ST2084) && (vp->nr_wins > 1)) { + sdr2hdr_en = 1; + sdr2hdr_r2r_mode = BT709_TO_BT2020; + sdr2hdr_tf = SDR2HDR_FOR_HDR; + } + } else { + if (vcstate->eotf == SMPTE_ST2084) { + sdr2hdr_en = 1; + sdr2hdr_tf = SDR2HDR_FOR_BT2020; + sdr2hdr_r2r_mode = BT709_TO_BT2020; + } + } + + if (hdr2sdr_en) { + vop2_load_hdr2sdr_table(vp); + VOP_MODULE_SET(vop2, vp, hdr2sdr_src_min, hdr_table->hdr2sdr_src_range_min); + VOP_MODULE_SET(vop2, vp, hdr2sdr_src_max, hdr_table->hdr2sdr_src_range_max); + VOP_MODULE_SET(vop2, vp, hdr2sdr_normfaceetf, hdr_table->hdr2sdr_normfaceetf); + VOP_MODULE_SET(vop2, vp, hdr2sdr_dst_min, hdr_table->hdr2sdr_dst_range_min); + VOP_MODULE_SET(vop2, vp, hdr2sdr_dst_max, hdr_table->hdr2sdr_dst_range_max); + VOP_MODULE_SET(vop2, vp, hdr2sdr_normfacgamma, hdr_table->hdr2sdr_normfacgamma); + } + + if (sdr2hdr_en) { + vop2_load_sdr2hdr_table(vp, sdr2hdr_tf); + VOP_MODULE_SET(vop2, vp, sdr2hdr_eotf_en, 1); + VOP_MODULE_SET(vop2, vp, sdr2hdr_r2r_en, 1); + VOP_MODULE_SET(vop2, vp, sdr2hdr_r2r_mode, sdr2hdr_r2r_mode); + VOP_MODULE_SET(vop2, vp, sdr2hdr_oetf_en, 1); + + } else { + VOP_MODULE_SET(vop2, vp, sdr2hdr_bypass_en, 1); + } + + VOP_MODULE_SET(vop2, vp, hdr2sdr_en, hdr2sdr_en); +} + +static void vop2_parse_alpha(struct vop2_alpha *alpha, int pixel_alpha_en, + int glb_alpha_value, int premulti_en) +{ + int glb_alpha_en = (glb_alpha_value == 0xff) ? 0 : 1; + int color_mode = premulti_en ? ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL; + + alpha->src_color_ctrl.val = 0; + alpha->dst_color_ctrl.val = 0; + alpha->src_alpha_ctrl.val = 0; + alpha->dst_alpha_ctrl.val = 0; + + if (!pixel_alpha_en) + alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; + else if (pixel_alpha_en && !glb_alpha_en) + alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX; + else + alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL; + + alpha->src_color_ctrl.bits.alpha_en = 1; + + if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) { + alpha->src_color_ctrl.bits.color_mode = color_mode; + alpha->src_color_ctrl.bits.factor_mode = ALPHA_ONE; + } else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) { + alpha->src_color_ctrl.bits.color_mode = color_mode; + alpha->src_color_ctrl.bits.factor_mode = ALPHA_ONE; + } else { + alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL; + alpha->src_color_ctrl.bits.factor_mode = ALPHA_DST_GLOBAL; + } + + alpha->src_color_ctrl.bits.glb_alpha = glb_alpha_value; + alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; + alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; + + alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; + alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; + + alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; + alpha->dst_color_ctrl.bits.glb_alpha = 0xff; + alpha->dst_color_ctrl.bits.color_mode = ALPHA_SRC_NO_PRE_MUL; + alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; + + alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; + alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode; + alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; + alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE; + + alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; + alpha->dst_alpha_ctrl.bits.blend_mode = alpha->dst_color_ctrl.bits.blend_mode; + alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; + alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; +} + +static int vop2_get_mixer_number(int nr_wins) +{ + if (nr_wins <= 1) + return nr_wins; + else + return nr_wins - 1; +} + +static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, uint8_t port_id) +{ + struct vop2_video_port *vp; + int mixer_id = 0; + int i; + + for (i = 0; i < port_id; i++) { + vp = &vop2->vps[i]; + mixer_id += vop2_get_mixer_number(vp->nr_wins); + } + + return mixer_id; +} + +static void vop2_setup_alpha(struct vop2_video_port *vp, const struct vop2_zpos *vop2_zpos) +{ + struct vop2 *vop2 = vp->vop2; + uint32_t src_color_ctrl_offset = vop2->data->ctrl->src_color_ctrl.offset; + uint32_t dst_color_ctrl_offset = vop2->data->ctrl->dst_color_ctrl.offset; + uint32_t src_alpha_ctrl_offset = vop2->data->ctrl->src_alpha_ctrl.offset; + uint32_t dst_alpha_ctrl_offset = vop2->data->ctrl->dst_alpha_ctrl.offset; + const struct vop2_zpos *zpos; + struct drm_framebuffer *fb; + struct vop2_alpha alpha; + struct vop2_win *win; + struct drm_plane *plane; + struct vop2_plane_state *vpstate; + int pixel_alpha_en; + int premulti_en; + int mixer_id; + uint32_t offset; + int i; + + mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id); + /* layer0 is the bottom layer, so always no alpha */ + for (i = 1; i < vp->nr_wins; i++) { + zpos = &vop2_zpos[i]; + win = vop2_find_win_by_phys_id(vop2, zpos->win_phys_id); + plane = &win->base; + vpstate = to_vop2_plane_state(plane->state); + fb = plane->state->fb; + if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) + premulti_en = 1; + else + premulti_en = 0; + pixel_alpha_en = is_alpha_support(fb->format->format); + vop2_parse_alpha(&alpha, pixel_alpha_en, vpstate->global_alpha, premulti_en); + offset = (mixer_id + i - 1) * 0x10; + vop2_writel(vop2, src_color_ctrl_offset + offset, alpha.src_color_ctrl.val); + vop2_writel(vop2, dst_color_ctrl_offset + offset, alpha.dst_color_ctrl.val); + vop2_writel(vop2, src_alpha_ctrl_offset + offset, alpha.src_alpha_ctrl.val); + vop2_writel(vop2, dst_alpha_ctrl_offset + offset, alpha.dst_alpha_ctrl.val); + if (vp->hdr_en && (i == 1)) { + VOP_MODULE_SET(vop2, vp, hdr_src_color_ctrl, alpha.src_color_ctrl.val); + VOP_MODULE_SET(vop2, vp, hdr_dst_color_ctrl, alpha.dst_color_ctrl.val); + VOP_MODULE_SET(vop2, vp, hdr_src_alpha_ctrl, alpha.src_color_ctrl.val); + VOP_MODULE_SET(vop2, vp, hdr_dst_alpha_ctrl, alpha.dst_color_ctrl.val); + } + }; +} + +static void vop2_setup_layer_mixer_for_vp(struct vop2_video_port *vp, + const struct vop2_zpos *vop2_zpos) +{ + struct vop2 *vop2 = vp->vop2; + u8 port_id = vp->id; + const struct vop2_data *vop2_data = vop2->data; + uint8_t nr_wins = vp->nr_wins; + const struct vop2_zpos *zpos; + struct vop2_win *win; + struct vop2_layer *layer; + u8 used_layers = 0; + u8 layer_id, win_phys_id; + int i; + + for (i = 0; i < port_id; i++) + used_layers += vop2->vps[i].nr_wins; + + /* + * Win and layer must map one by one, if a win is selected + * by two layers, unexpected error may happen. + * So when we attach a new win to a layer, we also move the + * old win of the layer to the layer where the new win comes from. + * + */ + for (i = 0; i < nr_wins; i++) { + layer = &vop2->layers[used_layers + i]; + zpos = &vop2_zpos[i]; + win = vop2_find_win_by_phys_id(vop2, zpos->win_phys_id); + layer_id = win->layer_id; + win_phys_id = layer->win_phys_id; + VOP_CTRL_SET(vop2, win_vp_id[win->phys_id], port_id); + VOP_MODULE_SET(vop2, layer, layer_sel, win->layer_sel_id); + win->layer_id = layer->id; + layer->win_phys_id = win->phys_id; + layer = &vop2->layers[layer_id]; + win = vop2_find_win_by_phys_id(vop2, win_phys_id); + VOP_MODULE_SET(vop2, layer, layer_sel, win->layer_sel_id); + win->layer_id = layer_id; + layer->win_phys_id = win_phys_id; + } + + used_layers += nr_wins; + + if (used_layers > vop2_data->nr_layers) { + DRM_DEV_ERROR(vop2->dev, "out of layers: %d\n", used_layers); + return; + } + + used_layers -= 1; + + if (port_id == (vop2_data->nr_vps - 1)) + used_layers = vop2_data->nr_mixers; + + vp->bg_ovl_dly = (vop2_data->nr_mixers - used_layers) << 1; + + VOP_MODULE_SET(vop2, vp, port_mux, used_layers); +} + +static void vop2_setup_dly_for_vp(struct vop2_video_port *vp) +{ + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + struct drm_crtc *crtc = &vp->crtc; + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + u16 hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + u16 hdisplay = adjusted_mode->crtc_hdisplay; + u32 bg_dly, pre_scan_dly; + + if (vp_data->hdr_table) { + if (vp->hdr_in && !vp->hdr_out) + bg_dly = vp_data->pre_scan_max_dly[1] - vp->bg_ovl_dly; + else if (vp->hdr_out) + bg_dly = vp_data->pre_scan_max_dly[2] - vp->bg_ovl_dly; + else + bg_dly = vp_data->pre_scan_max_dly[3] - vp->bg_ovl_dly; + + } else { + bg_dly = vp_data->pre_scan_max_dly[0] - vp->bg_ovl_dly; + } + + pre_scan_dly = bg_dly + (hdisplay >> 1) - 1; + pre_scan_dly = (pre_scan_dly << 16) | hsync_len; + VOP_MODULE_SET(vop2, vp, bg_dly, bg_dly); + VOP_MODULE_SET(vop2, vp, pre_scan_htiming, pre_scan_dly); +} + +static void vop2_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_plane *plane; + struct vop2_plane_state *vpstate; + struct vop2_zpos *vop2_zpos; + uint8_t nr_wins = 0; + + vop2_zpos = kmalloc_array(vop2->data->win_size, sizeof(*vop2_zpos), GFP_KERNEL); + if (!vop2_zpos) + return; + + drm_atomic_crtc_for_each_plane(plane, crtc) { + struct vop2_win *win = to_vop2_win(plane); + + /* + * Sub win of a cluster will be handled by pre overlay module automatically + * win in multi area share the same overlay zorder with it's parent. + */ + if ((win->feature & WIN_FEATURE_CLUSTER_SUB) || win->parent) + continue; + vp->win_mask |= 1 << win->phys_id; + vpstate = to_vop2_plane_state(plane->state); + vop2_zpos[nr_wins].win_phys_id = win->phys_id; + vop2_zpos[nr_wins].zpos = vpstate->zpos; + nr_wins++; + DRM_DEV_DEBUG(vop2->dev, "%s active zpos:%d\n", win->name, vpstate->zpos); + } + + if (nr_wins) { + vp->nr_wins = nr_wins; + + sort(vop2_zpos, nr_wins, sizeof(vop2_zpos[0]), vop2_zpos_cmp, NULL); + + vop2_setup_hdr10(vp, vop2_zpos[0].win_phys_id); + vop2_setup_layer_mixer_for_vp(vp, vop2_zpos); + vop2_setup_alpha(vp, vop2_zpos); + vop2_setup_dly_for_vp(vp); + } + + kfree(vop2_zpos); +} + +static void vop2_post_config(struct drm_crtc *crtc) +{ +} + +static void vop2_tv_config_update(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ +} + +static void vop2_cfg_update(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + spin_lock(&vop2->reg_lock); + + vop2_tv_config_update(crtc, old_crtc_state); + + vop2_post_config(crtc); + + spin_unlock(&vop2->reg_lock); +} + +static bool vop2_fs_irq_is_pending(struct vop2_video_port *vp) +{ + struct vop2 *vop2 = vp->vop2; + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + const struct vop_intr *intr = vp_data->intr; + + return VOP_INTR_GET_TYPE(vop2, intr, status, FS_FIELD_INTR); +} + +static void vop2_wait_for_irq_handler(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + bool pending; + int ret; + + /* + * Spin until frame start interrupt status bit goes low, which means + * that interrupt handler was invoked and cleared it. The timeout of + * 10 msecs is really too long, but it is just a safety measure if + * something goes really wrong. The wait will only happen in the very + * unlikely case of a vblank happening exactly at the same time and + * shouldn't exceed microseconds range. + */ + ret = readx_poll_timeout_atomic(vop2_fs_irq_is_pending, vp, pending, + !pending, 0, 10 * 1000); + if (ret) + DRM_DEV_ERROR(vop2->dev, "VOP vblank IRQ stuck for 10 ms\n"); + + synchronize_irq(vop2->irq); +} + +static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_cstate) +{ + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct drm_atomic_state *old_state = old_cstate->state; + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct drm_plane_state *old_pstate; + struct vop2 *vop2 = vp->vop2; + struct drm_plane *plane; + unsigned long flags; + int i, ret; + + vop2_cfg_update(crtc, old_cstate); + + if (!vop2->is_iommu_enabled && vop2->is_iommu_needed) { + if (vcstate->mode_update) + VOP_CTRL_SET(vop2, dma_stop, 1); + + ret = rockchip_drm_dma_attach_device(vop2->drm_dev, vop2->dev); + if (ret) { + vop2->is_iommu_enabled = false; + vop2_disable_all_planes_for_crtc(crtc); + DRM_DEV_ERROR(vop2->dev, "failed to attach dma mapping, %d\n", ret); + } else { + vop2->is_iommu_enabled = true; + VOP_CTRL_SET(vop2, dma_stop, 0); + } + } + + spin_lock_irqsave(&vop2->irq_lock, flags); + + vop2_cfg_done(crtc); + + spin_unlock_irqrestore(&vop2->irq_lock, flags); + + /* + * There is a (rather unlikely) possibility that a vblank interrupt + * fired before we set the cfg_done bit. To avoid spuriously + * signalling flip completion we need to wait for it to finish. + */ + vop2_wait_for_irq_handler(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + WARN_ON(vp->event); + + vp->event = crtc->state->event; + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); + + for_each_old_plane_in_state(old_state, plane, old_pstate, i) { + if (!old_pstate->fb) + continue; + + if (old_pstate->fb == plane->state->fb) + continue; + + drm_framebuffer_get(old_pstate->fb); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + drm_flip_work_queue(&vp->fb_unref_work, old_pstate->fb); + set_bit(VOP_PENDING_FB_UNREF, &vp->pending); + } +} + +static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { + .mode_fixup = vop2_crtc_mode_fixup, + .atomic_check = vop2_crtc_atomic_check, + .atomic_begin = vop2_crtc_atomic_begin, + .atomic_flush = vop2_crtc_atomic_flush, + .atomic_enable = vop2_crtc_atomic_enable, + .atomic_disable = vop2_crtc_atomic_disable, +}; + +static void vop2_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); +} + +static void vop2_crtc_reset(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + kfree(vcstate); + } + + vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL); + if (!vcstate) + return; + crtc->state = &vcstate->base; + crtc->state->crtc = crtc; + + vcstate->left_margin = 100; + vcstate->right_margin = 100; + vcstate->top_margin = 100; + vcstate->bottom_margin = 100; +} + +static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *vcstate, *old_vcstate; + + old_vcstate = to_rockchip_crtc_state(crtc->state); + vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL); + if (!vcstate) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base); + return &vcstate->base; +} + +static void vop2_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state); + + __drm_atomic_helper_crtc_destroy_state(&vcstate->base); + kfree(vcstate); +} + +#ifdef CONFIG_DRM_ANALOGIX_DP +static struct drm_connector *vop2_get_edp_connector(struct vop2 *vop2) +{ + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + + drm_connector_list_iter_begin(vop2->drm_dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + drm_connector_list_iter_end(&conn_iter); + return connector; + } + } + drm_connector_list_iter_end(&conn_iter); + + return NULL; +} + +static int vop2_crtc_set_crc_source(struct drm_crtc *crtc, + const char *source_name) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + struct drm_connector *connector; + int ret; + + connector = vop2_get_edp_connector(vop2); + if (!connector) + return -EINVAL; + + if (source_name && strcmp(source_name, "auto") == 0) + ret = analogix_dp_start_crc(connector); + else if (!source_name) + ret = analogix_dp_stop_crc(connector); + else + ret = -EINVAL; + + return ret; +} + +static int vop2_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, + size_t *values_cnt) +{ + if (source_name && strcmp(source_name, "auto") != 0) + return -EINVAL; + + *values_cnt = 3; + return 0; +} + +#else +static int vop2_crtc_set_crc_source(struct drm_crtc *crtc, + const char *source_name) +{ + return -ENODEV; +} + +static int +vop2_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, + size_t *values_cnt) +{ + return -ENODEV; +} +#endif + +static int vop2_crtc_atomic_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct drm_device *drm_dev = crtc->dev; + struct rockchip_drm_private *private = drm_dev->dev_private; + struct drm_mode_config *mode_config = &drm_dev->mode_config; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state); + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + + if (property == mode_config->tv_left_margin_property) { + *val = vcstate->left_margin; + return 0; + } + + if (property == mode_config->tv_right_margin_property) { + *val = vcstate->right_margin; + return 0; + } + + if (property == mode_config->tv_top_margin_property) { + *val = vcstate->top_margin; + return 0; + } + + if (property == mode_config->tv_bottom_margin_property) { + *val = vcstate->bottom_margin; + return 0; + } + + if (property == private->alpha_scale_prop) { + *val = (vop2->data->feature & VOP_FEATURE_ALPHA_SCALE) ? 1 : 0; + return 0; + } + + DRM_ERROR("failed to get vop2 crtc property\n"); + return -EINVAL; +} + +static int vop2_crtc_atomic_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *drm_dev = crtc->dev; + struct drm_mode_config *mode_config = &drm_dev->mode_config; + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state); + + if (property == mode_config->tv_left_margin_property) { + vcstate->left_margin = val; + return 0; + } + + if (property == mode_config->tv_right_margin_property) { + vcstate->right_margin = val; + return 0; + } + + if (property == mode_config->tv_top_margin_property) { + vcstate->top_margin = val; + return 0; + } + + if (property == mode_config->tv_bottom_margin_property) { + vcstate->bottom_margin = val; + return 0; + } + + DRM_ERROR("failed to set vop2 crtc property\n"); + return -EINVAL; +} + +static int vop2_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + struct vop2 *vop2 = vp->vop2; + int len, i; + + if (!vop2->lut) + return -EINVAL; + + len = min(size, vop2->lut_len); + for (i = 0; i < len; i++) + rockchip_vop2_crtc_fb_gamma_set(crtc, red[i], green[i], + blue[i], i); + vop2_crtc_load_lut(crtc); + + return 0; +} + +static const struct drm_crtc_funcs vop2_crtc_funcs = { + .gamma_set = vop2_crtc_gamma_set, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .destroy = vop2_crtc_destroy, + .reset = vop2_crtc_reset, + .atomic_get_property = vop2_crtc_atomic_get_property, + .atomic_set_property = vop2_crtc_atomic_set_property, + .atomic_duplicate_state = vop2_crtc_duplicate_state, + .atomic_destroy_state = vop2_crtc_destroy_state, + .enable_vblank = vop2_crtc_enable_vblank, + .disable_vblank = vop2_crtc_disable_vblank, + .set_crc_source = vop2_crtc_set_crc_source, + .verify_crc_source = vop2_crtc_verify_crc_source, +}; + +static void vop2_fb_unref_worker(struct drm_flip_work *work, void *val) +{ + struct vop2_video_port *vp = container_of(work, struct vop2_video_port, fb_unref_work); + struct drm_framebuffer *fb = val; + + drm_crtc_vblank_put(&vp->crtc); + drm_framebuffer_put(fb); +} + +static void vop2_handle_vblank(struct vop2 *vop2, struct drm_crtc *crtc) +{ + struct drm_device *drm = vop2->drm_dev; + struct vop2_video_port *vp = to_vop2_video_port(crtc); + unsigned long flags; + + spin_lock_irqsave(&drm->event_lock, flags); + if (vp->event) { + drm_crtc_send_vblank_event(crtc, vp->event); + drm_crtc_vblank_put(crtc); + vp->event = NULL; + } + spin_unlock_irqrestore(&drm->event_lock, flags); + + if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vp->pending)) + drm_flip_work_commit(&vp->fb_unref_work, system_unbound_wq); +} + +static u32 vop2_read_and_clear_active_vp_irqs(struct vop2 *vop2, int vp_id) +{ + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_video_port_data *vp_data; + const struct vop_intr *intr; + int val; + + vp_data = &vop2_data->vp[vp_id]; + intr = vp_data->intr; + val = VOP_INTR_GET_TYPE(vop2, intr, status, INTR_MASK); + if (val) + VOP_INTR_SET_TYPE(vop2, intr, clear, val, 1); + return val; +} + +static irqreturn_t vop2_isr(int irq, void *data) +{ + struct vop2 *vop2 = data; + struct drm_crtc *crtc; + struct vop2_video_port *vp; + const struct vop2_data *vop2_data = vop2->data; + size_t vp_max = min_t(size_t, vop2_data->nr_vps, ROCKCHIP_MAX_CRTC); + size_t axi_max = min_t(size_t, vop2_data->nr_axi_intr, VOP2_SYS_AXI_BUS_NUM); + uint32_t vp_irqs[ROCKCHIP_MAX_CRTC]; + uint32_t axi_irqs[VOP2_SYS_AXI_BUS_NUM]; + uint32_t active_irqs; + unsigned long flags; + int ret = IRQ_NONE; + int i; + +#define ERROR_HANDLER(x) \ + do { \ + if (active_irqs & x##_INTR) {\ + DRM_DEV_ERROR_RATELIMITED(vop2->dev, #x " irq err\n"); \ + active_irqs &= ~x##_INTR; \ + ret = IRQ_HANDLED; \ + } \ + } while (0) + + /* + * The irq is shared with the iommu. If the runtime-pm state of the + * vop2-device is disabled the irq has to be targeted at the iommu. + */ + if (!pm_runtime_get_if_in_use(vop2->dev)) + return IRQ_NONE; + + if (vop2_core_clks_enable(vop2)) { + DRM_DEV_ERROR(vop2->dev, "couldn't enable clocks\n"); + goto out; + } + + /* + * interrupt register has interrupt status, enable and clear bits, we + * must hold irq_lock to avoid a race with enable/disable_vblank(). + */ + spin_lock_irqsave(&vop2->irq_lock, flags); + for (i = 0; i < vp_max; i++) + vp_irqs[i] = vop2_read_and_clear_active_vp_irqs(vop2, i); + for (i = 0; i < axi_max; i++) + axi_irqs[i] = vop2_read_and_clear_axi_irqs(vop2, i); + spin_unlock_irqrestore(&vop2->irq_lock, flags); + + for (i = 0; i < vp_max; i++) { + vp = &vop2->vps[i]; + crtc = &vp->crtc; + active_irqs = vp_irqs[i]; + if (active_irqs & DSP_HOLD_VALID_INTR) { + complete(&vp->dsp_hold_completion); + active_irqs &= ~DSP_HOLD_VALID_INTR; + ret = IRQ_HANDLED; + } + + if (active_irqs & LINE_FLAG_INTR) { + complete(&vp->line_flag_completion); + active_irqs &= ~LINE_FLAG_INTR; + ret = IRQ_HANDLED; + } + + if (active_irqs & FS_FIELD_INTR) { + drm_crtc_handle_vblank(crtc); + vop2_handle_vblank(vop2, crtc); + active_irqs &= ~FS_FIELD_INTR; + ret = IRQ_HANDLED; + } + + ERROR_HANDLER(POST_BUF_EMPTY); + + /* Unhandled irqs are spurious. */ + if (active_irqs) + DRM_ERROR("Unknown video_port%d IRQs: %02x\n", i, active_irqs); + } + + for (i = 0; i < axi_max; i++) { + active_irqs = axi_irqs[i]; + + ERROR_HANDLER(BUS_ERROR); + + /* Unhandled irqs are spurious. */ + if (active_irqs) + DRM_ERROR("Unknown axi_bus%d IRQs: %02x\n", i, active_irqs); + } +out: + pm_runtime_put(vop2->dev); + return ret; +} + +static int vop2_plane_create_name_property(struct vop2 *vop2, struct vop2_win *win) +{ + struct drm_prop_enum_list *props = vop2->plane_name_list; + struct drm_property *prop; + + prop = drm_property_create_bitmask(vop2->drm_dev, + DRM_MODE_PROP_IMMUTABLE, "NAME", + props, vop2->registered_num_wins, + BIT(win->win_id)); + if (!prop) { + DRM_DEV_ERROR(vop2->dev, "create Name prop for %s failed\n", win->name); + return -ENOMEM; + } + win->name_prop = prop; + drm_object_attach_property(&win->base.base, win->name_prop, BIT(win->win_id)); + + return 0; +} + +static int vop2_plane_create_feature_property(struct vop2 *vop2, struct vop2_win *win) +{ + uint64_t feature = 0; + struct drm_property *prop; + + static const struct drm_prop_enum_list props[] = { + { ROCKCHIP_DRM_PLANE_FEATURE_SCALE, "scale" }, + { ROCKCHIP_DRM_PLANE_FEATURE_AFBDC, "afbdc" }, + }; + + if ((win->max_upscale_factor != 1) || (win->max_downscale_factor != 1)) + feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_SCALE); + if (win->feature & WIN_FEATURE_AFBDC) + feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_AFBDC); + + prop = drm_property_create_bitmask(vop2->drm_dev, + DRM_MODE_PROP_IMMUTABLE, "FEATURE", + props, ARRAY_SIZE(props), + feature); + if (!prop) { + DRM_DEV_ERROR(vop2->dev, "create feature prop for %s failed\n", win->name); + return -ENOMEM; + } + + win->feature_prop = prop; + + drm_object_attach_property(&win->base.base, win->feature_prop, feature); + + return 0; +} + +static int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, unsigned long possible_crtcs) +{ + struct rockchip_drm_private *private = vop2->drm_dev->dev_private; + unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE); + unsigned int max_width, max_height; + int ret; + + ret = drm_universal_plane_init(vop2->drm_dev, &win->base, possible_crtcs, &vop2_plane_funcs, + win->formats, win->nformats, NULL, win->type, win->name); + if (ret) { + DRM_DEV_ERROR(vop2->dev, "failed to initialize plane %d\n", ret); + return ret; + } + + drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs); + + drm_object_attach_property(&win->base.base, private->eotf_prop, 0); + drm_object_attach_property(&win->base.base, private->color_space_prop, 0); + drm_object_attach_property(&win->base.base, private->async_commit_prop, 0); + + if (win->parent) + drm_object_attach_property(&win->base.base, private->share_id_prop, + win->parent->base.base.id); + else + drm_object_attach_property(&win->base.base, private->share_id_prop, + win->base.base.id); + if (win->supported_rotations) + drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_0 | win->supported_rotations); + drm_plane_create_alpha_property(&win->base); + drm_plane_create_blend_mode_property(&win->base, blend_caps); + drm_plane_create_zpos_property(&win->base, win->win_id, 0, vop2->registered_num_wins - 1); + vop2_plane_create_name_property(vop2, win); + vop2_plane_create_feature_property(vop2, win); + max_width = vop2->data->max_input.width; + max_height = vop2->data->max_input.height; + win->input_width_prop = drm_property_create_range(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, + "INPUT_WIDTH", 0, max_width); + win->input_height_prop = drm_property_create_range(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, + "INPUT_HEIGHT", 0, max_height); + max_width = vop2->data->max_output.width; + max_height = vop2->data->max_output.height; + win->output_width_prop = drm_property_create_range(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, + "OUTPUT_WIDTH", 0, max_width); + win->output_height_prop = drm_property_create_range(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, + "OUTPUT_HEIGHT", 0, max_height); + win->scale_prop = drm_property_create_range(vop2->drm_dev, DRM_MODE_PROP_IMMUTABLE, + "SCALE_RATE", win->max_downscale_factor, + win->max_upscale_factor); + if (!win->input_width_prop || !win->input_height_prop || + !win->output_width_prop || !win->output_height_prop || + !win->scale_prop) { + DRM_ERROR("failed to create max_input/output property\n"); + return -EINVAL; + } + + drm_object_attach_property(&win->base.base, win->input_width_prop, 0); + drm_object_attach_property(&win->base.base, win->input_height_prop, 0); + drm_object_attach_property(&win->base.base, win->output_width_prop, 0); + drm_object_attach_property(&win->base.base, win->output_height_prop, 0); + drm_object_attach_property(&win->base.base, win->scale_prop, 0); + + return 0; +} + +static int vop2_create_crtc(struct vop2 *vop2) +{ + const struct vop2_data *vop2_data = vop2->data; + struct drm_device *drm_dev = vop2->drm_dev; + struct device *dev = vop2->dev; + struct drm_plane *plane; + struct drm_crtc *crtc; + struct device_node *port; + struct vop2_win *win = NULL; + struct vop2_video_port *vp; + const struct vop2_video_port_data *vp_data; + uint32_t possible_crtcs; + char dclk_name[9]; + int i = 0, j = 0; + int ret = 0; + + /* all planes can attach to any crtc */ + possible_crtcs = (1 << vop2_data->nr_vps) - 1; + + /* + * Create primary plane for eache crtc first, since we need + * to pass them to drm_crtc_init_with_planes, which sets the + * "possible_crtcs" to the newly initialized crtc. + */ + for (i = 0; i < vop2_data->nr_vps; i++) { + vp_data = &vop2_data->vp[i]; + vp = &vop2->vps[i]; + vp->vop2 = vop2; + vp->id = vp_data->id; + vp->regs = vp_data->regs; + + snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id); + vp->dclk = devm_clk_get(vop2->dev, dclk_name); + if (IS_ERR(vp->dclk)) { + DRM_DEV_ERROR(vop2->dev, "failed to get %s\n", dclk_name); + return PTR_ERR(vp->dclk); + } + + crtc = &vp->crtc; + + while (j < vop2->registered_num_wins) { + win = &vop2->win[j]; + j++; + + if (win->type == DRM_PLANE_TYPE_PRIMARY) + break; + win = NULL; + } + + if (!win) { + DRM_DEV_ERROR(vop2->dev, "No primary plane find for video_port%d\n", i); + break; + } + + if (vop2_plane_init(vop2, win, possible_crtcs)) { + DRM_DEV_ERROR(vop2->dev, "failed to init plane\n"); + break; + } + plane = &win->base; + ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, &vop2_crtc_funcs, + "video_port%d", vp->id); + if (ret) { + DRM_DEV_ERROR(vop2->dev, "crtc init for video_port%d failed\n", i); + return ret; + } + + drm_crtc_helper_add(crtc, &vop2_crtc_helper_funcs); + + port = of_graph_get_port_by_id(dev->of_node, i); + if (!port) { + DRM_DEV_ERROR(vop2->dev, "no port node found for video_port%d\n", i); + ret = -ENOENT; + } + + crtc->port = port; + + drm_flip_work_init(&vp->fb_unref_work, "fb_unref", vop2_fb_unref_worker); + + init_completion(&vp->dsp_hold_completion); + init_completion(&vp->line_flag_completion); + rockchip_register_crtc_funcs(crtc, &private_crtc_funcs); + drm_object_attach_property(&crtc->base, vop2->soc_id_prop, vp_data->soc_id); + drm_object_attach_property(&crtc->base, vop2->vp_id_prop, vp->id); + } + + /** + * change the unused primary window to overlay window + */ + for (; j < vop2->registered_num_wins; j++) { + win = &vop2->win[j]; + if (win->type == DRM_PLANE_TYPE_PRIMARY) + win->type = DRM_PLANE_TYPE_OVERLAY; + } + + /* + * create overlay planes of the leftover overlay win + * Create drm_planes for overlay windows with possible_crtcs restricted + */ + for (j = 0; j < vop2->registered_num_wins; j++) { + win = &vop2->win[j]; + + if (win->type != DRM_PLANE_TYPE_OVERLAY) + continue; + + ret = vop2_plane_init(vop2, win, possible_crtcs); + if (ret) { + DRM_DEV_ERROR(vop2->dev, "failed to init overlay\n"); + break; + } + } + + return ret; +} + +static void vop2_destroy_crtc(struct drm_crtc *crtc) +{ + struct vop2_video_port *vp = to_vop2_video_port(crtc); + + of_node_put(crtc->port); + + /* + * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane() + * references the CRTC. + */ + drm_crtc_cleanup(crtc); + drm_flip_work_cleanup(&vp->fb_unref_work); +} + +static int vop2_win_init(struct vop2 *vop2) +{ + const struct vop2_data *vop2_data = vop2->data; + const struct vop2_layer_data *layer_data; + struct drm_prop_enum_list *plane_name_list; + struct vop2_win *win; + struct vop2_layer *layer; + struct drm_property *prop; + char name[DRM_PROP_NAME_LEN]; + unsigned int num_wins = 0; + unsigned int i, j; + + for (i = 0; i < vop2_data->win_size; i++) { + const struct vop2_win_data *win_data = &vop2_data->win[i]; + + win = &vop2->win[num_wins]; + win->name = win_data->name; + win->regs = win_data->regs; + win->offset = win_data->base; + win->type = win_data->type; + win->formats = win_data->formats; + win->nformats = win_data->nformats; + win->supported_rotations = win_data->supported_rotations; + win->max_upscale_factor = win_data->max_upscale_factor; + win->max_downscale_factor = win_data->max_downscale_factor; + win->feature = win_data->feature; + win->phys_id = win_data->phys_id; + win->layer_sel_id = win_data->layer_sel_id; + win->win_id = i; + win->area_id = 0; + win->zpos = i; + win->vop2 = vop2; + + if (win->feature & WIN_FEATURE_CLUSTER_SUB) + win->parent = &vop2->win[num_wins - 1]; + + num_wins++; + + if (!vop2->support_multi_area) + continue; + + for (j = 0; j < win_data->area_size; j++) { + struct vop2_win *area = &vop2->win[num_wins]; + const struct vop2_win_regs *regs = win_data->area[j]; + + area->parent = win; + area->offset = win->offset; + area->regs = regs; + area->type = DRM_PLANE_TYPE_OVERLAY; + area->formats = win->formats; + area->nformats = win->nformats; + area->max_upscale_factor = win_data->max_upscale_factor; + area->max_downscale_factor = win_data->max_downscale_factor; + area->vop2 = vop2; + area->win_id = i; + area->phys_id = win->phys_id; + area->area_id = j + 1; + area->layer_sel_id = -1; + sprintf(name, "%s_win%d\n", win->name, area->area_id); + area->name = devm_kstrdup(vop2->dev, name, GFP_KERNEL); + num_wins++; + } + } + + vop2->registered_num_wins = num_wins; + + for (i = 0; i < vop2_data->nr_layers; i++) { + layer = &vop2->layers[i]; + layer_data = &vop2_data->layer[i]; + layer->id = layer_data->id; + layer->regs = layer_data->regs; + } + + plane_name_list = devm_kzalloc(vop2->dev, + vop2->registered_num_wins * sizeof(*plane_name_list), + GFP_KERNEL); + if (!plane_name_list) { + DRM_DEV_ERROR(vop2->dev, "failed to alloc memory for plane_name_list\n"); + return -ENOMEM; + } + + for (i = 0; i < vop2->registered_num_wins; i++) { + win = &vop2->win[i]; + plane_name_list[i].type = win->win_id; + plane_name_list[i].name = win->name; + } + + vop2->plane_name_list = plane_name_list; + + prop = drm_property_create_object(vop2->drm_dev, + DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, + "SOC_ID", DRM_MODE_OBJECT_CRTC); + vop2->soc_id_prop = prop; + + prop = drm_property_create_object(vop2->drm_dev, + DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE, + "PORT_ID", DRM_MODE_OBJECT_CRTC); + vop2->vp_id_prop = prop; + + if (!vop2->soc_id_prop || !vop2->vp_id_prop) { + DRM_DEV_ERROR(vop2->dev, "failed to create soc_id/vp_id property\n"); + return -ENOMEM; + } + + return 0; +} + +static int vop2_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct vop2_data *vop2_data; + struct drm_device *drm_dev = data; + struct vop2 *vop2; + struct resource *res; + size_t alloc_size; + int ret, i; + int num_wins = 0; + + vop2_data = of_device_get_match_data(dev); + if (!vop2_data) + return -ENODEV; + + for (i = 0; i < vop2_data->win_size; i++) { + const struct vop2_win_data *win_data = &vop2_data->win[i]; + + num_wins += win_data->area_size + 1; + } + + /* Allocate vop2 struct and its vop2_win array */ + alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * num_wins; + vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL); + if (!vop2) + return -ENOMEM; + + vop2->dev = dev; + vop2->data = vop2_data; + vop2->drm_dev = drm_dev; + vop2->version = vop2_data->version; + + dev_set_drvdata(dev, vop2); + + vop2->support_multi_area = of_property_read_bool(dev->of_node, "support-multi-area"); + + ret = vop2_win_init(vop2); + if (ret) + return ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!res) { + DRM_DEV_ERROR(vop2->dev, "failed to get vop2 register byname\n"); + return -EINVAL; + } + vop2->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(vop2->regs)) + return PTR_ERR(vop2->regs); + vop2->len = resource_size(res); + + vop2->regsbak = devm_kzalloc(dev, vop2->len, GFP_KERNEL); + if (!vop2->regsbak) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut"); + if (res) { + vop2->lut_len = resource_size(res) / sizeof(*vop2->lut); + if (vop2->lut_len != 256 && vop2->lut_len != 1024) { + DRM_DEV_ERROR(vop2->dev, "unsupported lut sizes %d\n", vop2->lut_len); + return -EINVAL; + } + + vop2->lut_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(vop2->lut_regs)) + return PTR_ERR(vop2->lut_regs); + } + + vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); + + vop2->hclk = devm_clk_get(vop2->dev, "hclk_vop"); + if (IS_ERR(vop2->hclk)) { + DRM_DEV_ERROR(vop2->dev, "failed to get hclk source\n"); + return PTR_ERR(vop2->hclk); + } + vop2->aclk = devm_clk_get(vop2->dev, "aclk_vop"); + if (IS_ERR(vop2->aclk)) { + DRM_DEV_ERROR(vop2->dev, "failed to get aclk source\n"); + return PTR_ERR(vop2->aclk); + } + + vop2->irq = platform_get_irq(pdev, 0); + if (vop2->irq < 0) { + DRM_DEV_ERROR(dev, "cannot find irq for vop2\n"); + return vop2->irq; + } + + spin_lock_init(&vop2->reg_lock); + spin_lock_init(&vop2->irq_lock); + mutex_init(&vop2->vop2_lock); + + ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2); + if (ret) + return ret; + + ret = vop2_create_crtc(vop2); + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static void vop2_unbind(struct device *dev, struct device *master, void *data) +{ + struct vop2 *vop2 = dev_get_drvdata(dev); + struct drm_device *drm_dev = vop2->drm_dev; + struct list_head *plane_list = &drm_dev->mode_config.plane_list; + struct list_head *crtc_list = &drm_dev->mode_config.crtc_list; + struct drm_crtc *crtc, *tmpc; + struct drm_plane *plane, *tmpp; + + pm_runtime_disable(dev); + + list_for_each_entry_safe(plane, tmpp, plane_list, head) + drm_plane_cleanup(plane); + + list_for_each_entry_safe(crtc, tmpc, crtc_list, head) + vop2_destroy_crtc(crtc); +} + +const struct component_ops vop2_component_ops = { + .bind = vop2_bind, + .unbind = vop2_unbind, +}; +EXPORT_SYMBOL_GPL(vop2_component_ops); diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c new file mode 100644 index 000000000000..0c82d08c3296 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -0,0 +1,950 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) Rockchip Electronics Co.Ltd + * Author: Andy Yan + */ + +#include +#include +#include + +#include "rockchip_drm_vop.h" +#include "rockchip_vop_reg.h" + +#define _VOP_REG(off, _mask, _shift, _write_mask) \ + { \ + .offset = off, \ + .mask = _mask, \ + .shift = _shift, \ + .write_mask = _write_mask, \ + } + +#define VOP_REG(off, _mask, _shift) \ + _VOP_REG(off, _mask, _shift, false) + +#define VOP_REG_MASK(off, _mask, s) \ + _VOP_REG(off, _mask, s, true) + +static const uint32_t formats_win_full_10bit[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_NV12, + DRM_FORMAT_NV16, + DRM_FORMAT_NV24, + DRM_FORMAT_NV12_10, + DRM_FORMAT_NV16_10, + DRM_FORMAT_NV24_10, +}; + +static const uint32_t formats_win_lite[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, +}; + +static const u32 sdr2hdr_bt1886eotf_yn_for_hlg_hdr[65] = { + 0, + 1, 7, 17, 35, + 60, 92, 134, 184, + 244, 315, 396, 487, + 591, 706, 833, 915, + 1129, 1392, 1717, 2118, + 2352, 2612, 2900, 3221, + 3577, 3972, 4411, 4899, + 5441, 6042, 6710, 7452, + 7853, 8276, 8721, 9191, + 9685, 10207, 10756, 11335, + 11945, 12588, 13266, 13980, + 14732, 15525, 16361, 17241, + 17699, 18169, 18652, 19147, + 19656, 20178, 20714, 21264, + 21829, 22408, 23004, 23615, + 24242, 24886, 25547, 26214, +}; + +static const u32 sdr2hdr_bt1886eotf_yn_for_bt2020[65] = { + 0, + 1820, 3640, 5498, 7674, + 10256, 13253, 16678, 20539, + 24847, 29609, 34833, 40527, + 46699, 53354, 60499, 68141, + 76285, 84937, 94103, 103787, + 108825, 113995, 119296, 124731, + 130299, 136001, 141837, 147808, + 153915, 160158, 166538, 173055, + 176365, 179709, 183089, 186502, + 189951, 193434, 196952, 200505, + 204093, 207715, 211373, 215066, + 218795, 222558, 226357, 230191, + 232121, 234060, 236008, 237965, + 239931, 241906, 243889, 245882, + 247883, 249894, 251913, 253941, + 255978, 258024, 260079, 262143, +}; + +static u32 sdr2hdr_bt1886eotf_yn_for_hdr[65] = { + /* dst_range 425int */ + 0, + 5, 21, 49, 91, + 150, 225, 320, 434, + 569, 726, 905, 1108, + 1336, 1588, 1866, 2171, + 2502, 2862, 3250, 3667, + 3887, 4114, 4349, 4591, + 4841, 5099, 5364, 5638, + 5920, 6209, 6507, 6812, + 6968, 7126, 7287, 7449, + 7613, 7779, 7948, 8118, + 8291, 8466, 8643, 8822, + 9003, 9187, 9372, 9560, + 9655, 9750, 9846, 9942, + 10039, 10136, 10234, 10333, + 10432, 10531, 10631, 10732, + 10833, 10935, 11038, 11141, +}; + +static const u32 sdr2hdr_st2084oetf_yn_for_hlg_hdr[65] = { + 0, + 668, 910, 1217, 1600, + 2068, 2384, 2627, 3282, + 3710, 4033, 4879, 5416, + 5815, 6135, 6401, 6631, + 6833, 7176, 7462, 7707, + 7921, 8113, 8285, 8442, + 8586, 8843, 9068, 9268, + 9447, 9760, 10027, 10259, + 10465, 10650, 10817, 10971, + 11243, 11480, 11689, 11877, + 12047, 12202, 12345, 12477, + 12601, 12716, 12926, 13115, + 13285, 13441, 13583, 13716, + 13839, 13953, 14163, 14350, + 14519, 14673, 14945, 15180, + 15570, 15887, 16153, 16383, +}; + +static const u32 sdr2hdr_st2084oetf_yn_for_bt2020[65] = { + 0, + 0, 0, 1, 2, + 4, 6, 9, 18, + 27, 36, 72, 108, + 144, 180, 216, 252, + 288, 360, 432, 504, + 576, 648, 720, 792, + 864, 1008, 1152, 1296, + 1444, 1706, 1945, 2166, + 2372, 2566, 2750, 2924, + 3251, 3553, 3834, 4099, + 4350, 4588, 4816, 5035, + 5245, 5447, 5832, 6194, + 6536, 6862, 7173, 7471, + 7758, 8035, 8560, 9055, + 9523, 9968, 10800, 11569, + 12963, 14210, 15347, 16383, +}; + +static u32 sdr2hdr_st2084oetf_yn_for_hdr[65] = { + 0, + 281, 418, 610, 871, + 1217, 1464, 1662, 2218, + 2599, 2896, 3699, 4228, + 4628, 4953, 5227, 5466, + 5676, 6038, 6341, 6602, + 6833, 7039, 7226, 7396, + 7554, 7835, 8082, 8302, + 8501, 8848, 9145, 9405, + 9635, 9842, 10031, 10204, + 10512, 10779, 11017, 11230, + 11423, 11599, 11762, 11913, + 12054, 12185, 12426, 12641, + 12835, 13013, 13177, 13328, + 13469, 13600, 13840, 14055, + 14248, 14425, 14737, 15006, + 15453, 15816, 16121, 16383, +}; + +static const u32 sdr2hdr_st2084oetf_dxn_pow2[64] = { + 0, 0, 1, 2, + 3, 3, 3, 5, + 5, 5, 7, 7, + 7, 7, 7, 7, + 7, 8, 8, 8, + 8, 8, 8, 8, + 8, 9, 9, 9, + 9, 10, 10, 10, + 10, 10, 10, 10, + 11, 11, 11, 11, + 11, 11, 11, 11, + 11, 11, 12, 12, + 12, 12, 12, 12, + 12, 12, 13, 13, + 13, 13, 14, 14, + 15, 15, 15, 15, +}; + +static const u32 sdr2hdr_st2084oetf_dxn[64] = { + 1, 1, 2, 4, + 8, 8, 8, 32, + 32, 32, 128, 128, + 128, 128, 128, 128, + 128, 256, 256, 256, + 256, 256, 256, 256, + 256, 512, 512, 512, + 512, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, + 2048, 2048, 2048, 2048, + 2048, 2048, 2048, 2048, + 2048, 2048, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 8192, 8192, + 8192, 8192, 16384, 16384, + 32768, 32768, 32768, 32768, +}; + +static const u32 sdr2hdr_st2084oetf_xn[63] = { + 1, 2, 4, 8, + 16, 24, 32, 64, + 96, 128, 256, 384, + 512, 640, 768, 896, + 1024, 1280, 1536, 1792, + 2048, 2304, 2560, 2816, + 3072, 3584, 4096, 4608, + 5120, 6144, 7168, 8192, + 9216, 10240, 11264, 12288, + 14336, 16384, 18432, 20480, + 22528, 24576, 26624, 28672, + 30720, 32768, 36864, 40960, + 45056, 49152, 53248, 57344, + 61440, 65536, 73728, 81920, + 90112, 98304, 114688, 131072, + 163840, 196608, 229376, +}; + +static u32 hdr2sdr_eetf_yn[33] = { + 1716, + 1880, 2067, 2277, 2508, + 2758, 3026, 3310, 3609, + 3921, 4246, 4581, 4925, + 5279, 5640, 6007, 6380, + 6758, 7140, 7526, 7914, + 8304, 8694, 9074, 9438, + 9779, 10093, 10373, 10615, + 10812, 10960, 11053, 11084, +}; + +static u32 hdr2sdr_bt1886oetf_yn[33] = { + 0, + 0, 0, 0, 0, + 0, 0, 0, 314, + 746, 1323, 2093, 2657, + 3120, 3519, 3874, 4196, + 4492, 5024, 5498, 5928, + 6323, 7034, 7666, 8239, + 8766, 9716, 10560, 11325, + 12029, 13296, 14422, 16383, +}; + +static const u32 hdr2sdr_sat_yn[9] = { + 0, + 1792, 3584, 3472, 2778, + 2083, 1389, 694, 0, +}; + +static const struct vop_hdr_table rk3568_vop_hdr_table = { + .hdr2sdr_eetf_yn = hdr2sdr_eetf_yn, + .hdr2sdr_bt1886oetf_yn = hdr2sdr_bt1886oetf_yn, + .hdr2sdr_sat_yn = hdr2sdr_sat_yn, + + .hdr2sdr_src_range_min = 494, + .hdr2sdr_src_range_max = 12642, + .hdr2sdr_normfaceetf = 1327, + .hdr2sdr_dst_range_min = 4, + .hdr2sdr_dst_range_max = 3276, + .hdr2sdr_normfacgamma = 5120, + + .sdr2hdr_bt1886eotf_yn_for_hlg_hdr = sdr2hdr_bt1886eotf_yn_for_hlg_hdr, + .sdr2hdr_bt1886eotf_yn_for_bt2020 = sdr2hdr_bt1886eotf_yn_for_bt2020, + .sdr2hdr_bt1886eotf_yn_for_hdr = sdr2hdr_bt1886eotf_yn_for_hdr, + .sdr2hdr_st2084oetf_yn_for_hlg_hdr = sdr2hdr_st2084oetf_yn_for_hlg_hdr, + .sdr2hdr_st2084oetf_yn_for_bt2020 = sdr2hdr_st2084oetf_yn_for_bt2020, + .sdr2hdr_st2084oetf_yn_for_hdr = sdr2hdr_st2084oetf_yn_for_hdr, + .sdr2hdr_st2084oetf_dxn_pow2 = sdr2hdr_st2084oetf_dxn_pow2, + .sdr2hdr_st2084oetf_dxn = sdr2hdr_st2084oetf_dxn, + .sdr2hdr_st2084oetf_xn = sdr2hdr_st2084oetf_xn, +}; + +static const int rk3568_vop_axi_intrs[] = { + 0, + BUS_ERROR_INTR, +}; + +static const struct vop_intr rk3568_vop_axi_intr[] = { + { + .intrs = rk3568_vop_axi_intrs, + .nintrs = ARRAY_SIZE(rk3568_vop_axi_intrs), + .status = VOP_REG(RK3568_SYS0_INT_STATUS, 0xfe, 0), + .enable = VOP_REG_MASK(RK3568_SYS0_INT_EN, 0xfe, 0), + .clear = VOP_REG_MASK(RK3568_SYS0_INT_CLR, 0xfe, 0), + }, + + { + .intrs = rk3568_vop_axi_intrs, + .nintrs = ARRAY_SIZE(rk3568_vop_axi_intrs), + .status = VOP_REG(RK3568_SYS1_INT_STATUS, 0xfe, 0), + .enable = VOP_REG_MASK(RK3568_SYS1_INT_EN, 0xfe, 0), + .clear = VOP_REG_MASK(RK3568_SYS1_INT_CLR, 0xfe, 0), + }, +}; + +static const int rk3568_vop_intrs[] = { + FS_INTR, + FS_NEW_INTR, + LINE_FLAG_INTR, + LINE_FLAG1_INTR, + POST_BUF_EMPTY_INTR, + FS_FIELD_INTR, + DSP_HOLD_VALID_INTR, +}; + +static const struct vop_intr rk3568_vp0_intr = { + .intrs = rk3568_vop_intrs, + .nintrs = ARRAY_SIZE(rk3568_vop_intrs), + .line_flag_num[0] = VOP_REG(RK3568_VP0_LINE_FLAG, 0x1fff, 0), + .line_flag_num[1] = VOP_REG(RK3568_VP0_LINE_FLAG, 0x1fff, 16), + .status = VOP_REG(RK3568_VP0_INT_STATUS, 0xffff, 0), + .enable = VOP_REG_MASK(RK3568_VP0_INT_EN, 0xffff, 0), + .clear = VOP_REG_MASK(RK3568_VP0_INT_CLR, 0xffff, 0), +}; + +static const struct vop_intr rk3568_vp1_intr = { + .intrs = rk3568_vop_intrs, + .nintrs = ARRAY_SIZE(rk3568_vop_intrs), + .line_flag_num[0] = VOP_REG(RK3568_VP1_LINE_FLAG, 0x1fff, 0), + .line_flag_num[1] = VOP_REG(RK3568_VP1_LINE_FLAG, 0x1fff, 16), + .status = VOP_REG(RK3568_VP1_INT_STATUS, 0xffff, 0), + .enable = VOP_REG_MASK(RK3568_VP1_INT_EN, 0xffff, 0), + .clear = VOP_REG_MASK(RK3568_VP1_INT_CLR, 0xffff, 0), +}; + +static const struct vop_intr rk3568_vp2_intr = { + .intrs = rk3568_vop_intrs, + .nintrs = ARRAY_SIZE(rk3568_vop_intrs), + .line_flag_num[0] = VOP_REG(RK3568_VP2_LINE_FLAG, 0x1fff, 0), + .line_flag_num[1] = VOP_REG(RK3568_VP2_LINE_FLAG, 0x1fff, 16), + .status = VOP_REG(RK3568_VP2_INT_STATUS, 0xffff, 0), + .enable = VOP_REG_MASK(RK3568_VP2_INT_EN, 0xffff, 0), + .clear = VOP_REG_MASK(RK3568_VP2_INT_CLR, 0xffff, 0), +}; + +static const struct vop2_video_port_regs rk3568_vop_vp0_regs = { + .cfg_done = VOP_REG(RK3568_REG_CFG_DONE, 0x1, 0), + .overlay_mode = VOP_REG(RK3568_OVL_CTRL, 0x1, 0), + .port_mux = VOP_REG(RK3568_OVL_PORT_SEL, 0xf, 0), + .out_mode = VOP_REG(RK3568_VP0_DSP_CTRL, 0xf, 0), + .standby = VOP_REG(RK3568_VP0_DSP_CTRL, 0x1, 31), + .core_dclk_div = VOP_REG(RK3568_VP0_DSP_CTRL, 0x1, 4), + .p2i_en = VOP_REG(RK3568_VP0_DSP_CTRL, 0x1, 5), + .dsp_interlace = VOP_REG(RK3568_VP0_DSP_CTRL, 0x1, 7), + .pre_scan_htiming = VOP_REG(RK3568_VP0_PRE_SCAN_HTIMING, 0x1fff1fff, 0), + .bg_dly = VOP_REG(RK3568_VP0_BG_MIX_CTRL, 0xff, 24), + .hpost_st_end = VOP_REG(RK3568_VP0_POST_DSP_HACT_INFO, 0x1fff1fff, 0), + .vpost_st_end = VOP_REG(RK3568_VP0_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .htotal_pw = VOP_REG(RK3568_VP0_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .post_scl_factor = VOP_REG(RK3568_VP0_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), + .post_scl_ctrl = VOP_REG(RK3568_VP0_POST_SCL_CTRL, 0x3, 0), + .hact_st_end = VOP_REG(RK3568_VP0_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3568_VP0_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3568_VP0_DSP_VACT_ST_END, 0x1fff1fff, 0), + .vact_st_end_f1 = VOP_REG(RK3568_VP0_DSP_VACT_ST_END_F1, 0x1fff1fff, 0), + .vs_st_end_f1 = VOP_REG(RK3568_VP0_DSP_VS_ST_END_F1, 0x1fff1fff, 0), + .vpost_st_end_f1 = VOP_REG(RK3568_VP0_POST_DSP_VACT_INFO_F1, 0x1fff1fff, 0), + .pre_dither_down_en = VOP_REG(RK3568_VP0_DSP_CTRL, 0x1, 16), + .dither_down_en = VOP_REG(RK3568_VP0_DSP_CTRL, 0x1, 17), + .dither_down_sel = VOP_REG(RK3568_VP0_DSP_CTRL, 0x3, 18), + .dither_down_mode = VOP_REG(RK3568_VP0_DSP_CTRL, 0x1, 20), + .mipi_dual_en = VOP_REG(RK3568_VP0_MIPI_CTRL, 0x1, 20), + .mipi_dual_channel_swap = VOP_REG(RK3568_VP0_MIPI_CTRL, 0x1, 21), + .hdr_lut_update_en = VOP_REG(RK3568_HDR_LUT_CTRL, 0x1, 0), + .hdr_lut_mode = VOP_REG(RK3568_HDR_LUT_CTRL, 0x1, 1), + .hdr_lut_mst = VOP_REG(RK3568_HDR_LUT_MST, 0xffffffff, 0), + .sdr2hdr_eotf_en = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 0), + .sdr2hdr_r2r_en = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 1), + .sdr2hdr_r2r_mode = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 2), + .sdr2hdr_oetf_en = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 3), + .sdr2hdr_bypass_en = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 8), + .sdr2hdr_gating_en = VOP_REG(RK3568_SDR2HDR_CTRL, 0x1, 9), + .hdr2sdr_en = VOP_REG(RK3568_HDR2SDR_CTRL, 0x1, 0), + .hdr2sdr_src_min = VOP_REG(RK3568_HDR2SDR_SRC_RANGE, 0x3fff, 0), + .hdr2sdr_src_max = VOP_REG(RK3568_HDR2SDR_SRC_RANGE, 0x3fff, 16), + .hdr2sdr_normfaceetf = VOP_REG(RK3568_HDR2SDR_NORMFACEETF, 0x7ff, 0), + .hdr2sdr_dst_min = VOP_REG(RK3568_HDR2SDR_DST_RANGE, 0xffff, 0), + .hdr2sdr_dst_max = VOP_REG(RK3568_HDR2SDR_DST_RANGE, 0xffff, 16), + .hdr2sdr_normfacgamma = VOP_REG(RK3568_HDR2SDR_NORMFACCGAMMA, 0xffff, 0), + .hdr2sdr_eetf_oetf_y0_offset = VOP_REG(RK3568_HDR_EETF_OETF_Y0, 0xffffffff, 0), + .hdr2sdr_sat_y0_offset = VOP_REG(RK3568_HDR_SAT_Y0, 0xffffffff, 0), + .sdr2hdr_eotf_oetf_y0_offset = VOP_REG(RK3568_HDR_EOTF_OETF_Y0, 0xffffffff, 0), + .sdr2hdr_oetf_dx_pow1_offset = VOP_REG(RK3568_HDR_OETF_DX_POW1, 0xffffffff, 0), + .sdr2hdr_oetf_xn1_offset = VOP_REG(RK3568_HDR_OETF_XN1, 0xffffffff, 0), + .hdr_src_color_ctrl = VOP_REG(RK3568_HDR0_SRC_COLOR_CTRL, 0xffffffff, 0), + .hdr_dst_color_ctrl = VOP_REG(RK3568_HDR0_DST_COLOR_CTRL, 0xffffffff, 0), + .hdr_src_alpha_ctrl = VOP_REG(RK3568_HDR0_SRC_ALPHA_CTRL, 0xffffffff, 0), + .hdr_dst_alpha_ctrl = VOP_REG(RK3568_HDR0_DST_ALPHA_CTRL, 0xffffffff, 0), +}; + +static const struct vop2_video_port_regs rk3568_vop_vp1_regs = { + .cfg_done = VOP_REG(RK3568_REG_CFG_DONE, 0x1, 1), + .overlay_mode = VOP_REG(RK3568_OVL_CTRL, 0x1, 0), + .port_mux = VOP_REG(RK3568_OVL_PORT_SEL, 0xf, 4), + .out_mode = VOP_REG(RK3568_VP1_DSP_CTRL, 0xf, 0), + .standby = VOP_REG(RK3568_VP1_DSP_CTRL, 0x1, 31), + .core_dclk_div = VOP_REG(RK3568_VP1_DSP_CTRL, 0x1, 4), + .p2i_en = VOP_REG(RK3568_VP1_DSP_CTRL, 0x1, 5), + .dsp_interlace = VOP_REG(RK3568_VP1_DSP_CTRL, 0x1, 7), + .pre_scan_htiming = VOP_REG(RK3568_VP1_PRE_SCAN_HTIMING, 0x1fff1fff, 0), + .bg_dly = VOP_REG(RK3568_VP1_BG_MIX_CTRL, 0xff, 24), + .hpost_st_end = VOP_REG(RK3568_VP1_POST_DSP_HACT_INFO, 0x1fff1fff, 0), + .vpost_st_end = VOP_REG(RK3568_VP1_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .htotal_pw = VOP_REG(RK3568_VP1_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .post_scl_factor = VOP_REG(RK3568_VP1_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), + .post_scl_ctrl = VOP_REG(RK3568_VP1_POST_SCL_CTRL, 0x3, 0), + .hact_st_end = VOP_REG(RK3568_VP1_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3568_VP1_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3568_VP1_DSP_VACT_ST_END, 0x1fff1fff, 0), + .vact_st_end_f1 = VOP_REG(RK3568_VP1_DSP_VACT_ST_END_F1, 0x1fff1fff, 0), + .vs_st_end_f1 = VOP_REG(RK3568_VP1_DSP_VS_ST_END_F1, 0x1fff1fff, 0), + .vpost_st_end_f1 = VOP_REG(RK3568_VP1_POST_DSP_VACT_INFO_F1, 0x1fff1fff, 0), + .pre_dither_down_en = VOP_REG(RK3568_VP1_DSP_CTRL, 0x1, 16), + .dither_down_en = VOP_REG(RK3568_VP1_DSP_CTRL, 0x1, 17), + .dither_down_sel = VOP_REG(RK3568_VP1_DSP_CTRL, 0x3, 18), + .dither_down_mode = VOP_REG(RK3568_VP1_DSP_CTRL, 0x1, 20), + .mipi_dual_en = VOP_REG(RK3568_VP1_MIPI_CTRL, 0x1, 20), + .mipi_dual_channel_swap = VOP_REG(RK3568_VP1_MIPI_CTRL, 0x1, 21), +}; + +static const struct vop2_video_port_regs rk3568_vop_vp2_regs = { + .cfg_done = VOP_REG(RK3568_REG_CFG_DONE, 0x1, 2), + .overlay_mode = VOP_REG(RK3568_OVL_CTRL, 0x1, 0), + .port_mux = VOP_REG(RK3568_OVL_PORT_SEL, 0xf, 8), + .out_mode = VOP_REG(RK3568_VP2_DSP_CTRL, 0xf, 0), + .standby = VOP_REG(RK3568_VP2_DSP_CTRL, 0x1, 31), + .core_dclk_div = VOP_REG(RK3568_VP2_DSP_CTRL, 0x1, 4), + .p2i_en = VOP_REG(RK3568_VP2_DSP_CTRL, 0x1, 5), + .dsp_interlace = VOP_REG(RK3568_VP2_DSP_CTRL, 0x1, 7), + .pre_scan_htiming = VOP_REG(RK3568_VP2_PRE_SCAN_HTIMING, 0x1fff1fff, 0), + .bg_dly = VOP_REG(RK3568_VP2_BG_MIX_CTRL, 0xff, 24), + .hpost_st_end = VOP_REG(RK3568_VP2_POST_DSP_HACT_INFO, 0x1fff1fff, 0), + .vpost_st_end = VOP_REG(RK3568_VP2_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .post_scl_factor = VOP_REG(RK3568_VP2_POST_SCL_FACTOR_YRGB, 0xffffffff, 0), + .post_scl_ctrl = VOP_REG(RK3568_VP2_POST_SCL_CTRL, 0x3, 0), + .htotal_pw = VOP_REG(RK3568_VP2_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .hact_st_end = VOP_REG(RK3568_VP2_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3568_VP2_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3568_VP2_DSP_VACT_ST_END, 0x1fff1fff, 0), + .vact_st_end_f1 = VOP_REG(RK3568_VP2_DSP_VACT_ST_END_F1, 0x1fff1fff, 0), + .vs_st_end_f1 = VOP_REG(RK3568_VP2_DSP_VS_ST_END_F1, 0x1fff1fff, 0), + .vpost_st_end_f1 = VOP_REG(RK3568_VP2_POST_DSP_VACT_INFO_F1, 0x1fff1fff, 0), + .pre_dither_down_en = VOP_REG(RK3568_VP2_DSP_CTRL, 0x1, 16), + .dither_down_en = VOP_REG(RK3568_VP2_DSP_CTRL, 0x1, 17), + .dither_down_sel = VOP_REG(RK3568_VP2_DSP_CTRL, 0x3, 18), + .dither_down_mode = VOP_REG(RK3568_VP2_DSP_CTRL, 0x1, 20), + .mipi_dual_en = VOP_REG(RK3568_VP2_MIPI_CTRL, 0x1, 20), + .mipi_dual_channel_swap = VOP_REG(RK3568_VP2_MIPI_CTRL, 0x1, 21), +}; + +static const struct vop2_video_port_data rk3568_vop_video_ports[] = { + { + .id = 0, + .soc_id = 0x3568, + .pre_scan_max_dly = { 40, 49, 33, 42 }, + .intr = &rk3568_vp0_intr, + .hdr_table = &rk3568_vop_hdr_table, + .regs = &rk3568_vop_vp0_regs, + }, + { + .id = 1, + .soc_id = 0x3568, + .pre_scan_max_dly = { 40, 40, 40, 40 }, + .intr = &rk3568_vp1_intr, + .regs = &rk3568_vop_vp1_regs, + }, + { + .id = 2, + .soc_id = 0x3568, + .pre_scan_max_dly = { 40, 40, 40, 40 }, + .intr = &rk3568_vp2_intr, + .regs = &rk3568_vop_vp2_regs, + }, +}; + +const struct vop2_layer_regs rk3568_vop_layer0_regs = { + .layer_sel = VOP_REG(RK3568_OVL_LAYER_SEL, 0x7, 0) +}; + +const struct vop2_layer_regs rk3568_vop_layer1_regs = { + .layer_sel = VOP_REG(RK3568_OVL_LAYER_SEL, 0x7, 4) +}; + +const struct vop2_layer_regs rk3568_vop_layer2_regs = { + .layer_sel = VOP_REG(RK3568_OVL_LAYER_SEL, 0x7, 8) +}; + +const struct vop2_layer_regs rk3568_vop_layer3_regs = { + .layer_sel = VOP_REG(RK3568_OVL_LAYER_SEL, 0x7, 12) +}; + +const struct vop2_layer_regs rk3568_vop_layer4_regs = { + .layer_sel = VOP_REG(RK3568_OVL_LAYER_SEL, 0x7, 16) +}; + +const struct vop2_layer_regs rk3568_vop_layer5_regs = { + .layer_sel = VOP_REG(RK3568_OVL_LAYER_SEL, 0x7, 20) +}; + +static const struct vop2_layer_data rk3568_vop_layers[] = { + { + .id = 0, + .regs = &rk3568_vop_layer0_regs, + }, + + { + .id = 1, + .regs = &rk3568_vop_layer1_regs, + }, + + { + .id = 2, + .regs = &rk3568_vop_layer2_regs, + }, + + { + .id = 3, + .regs = &rk3568_vop_layer3_regs, + }, + + { + .id = 4, + .regs = &rk3568_vop_layer4_regs, + }, + + { + .id = 5, + .regs = &rk3568_vop_layer5_regs, + }, +}; + +static const struct vop_afbc rk3568_vop_afbc = { + .enable = VOP_REG(RK3568_CLUSTER0_CTRL, 0x1, 1), + .format = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_CTRL, 0x1f, 2), + .half_block_en = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_CTRL, 0x1, 7), + .block_split_en = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_CTRL, 0x1, 8), + .hdr_ptr = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_HDR_PTR, 0xffffffff, 0), + .pic_size = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_PIC_SIZE, 0xffffffff, 0), + .pic_vir_width = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_VIR_WIDTH, 0xffff, 0), + .tile_num = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_VIR_WIDTH, 0xffff, 16), + .pic_offset = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_PIC_OFFSET, 0xffffffff, 0), + .dsp_offset = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_DSP_OFFSET, 0xffffffff, 0), + .rotate_90 = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_ROTATE_MODE, 0x1, 0), + .rotate_270 = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_ROTATE_MODE, 0x1, 1), + .xmirror = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_ROTATE_MODE, 0x1, 2), + .ymirror = VOP_REG(RK3568_CLUSTER0_WIN0_AFBCD_ROTATE_MODE, 0x1, 3), +}; + +static const struct vop2_scl_regs rk3568_cluster_win_scl = { + .scale_yrgb_x = VOP_REG(RK3568_CLUSTER0_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(RK3568_CLUSTER0_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), + .yrgb_ver_scl_mode = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL1, 0x3, 14), + .yrgb_hor_scl_mode = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL1, 0x3, 12), + .bic_coe_sel = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL1, 0x3, 2), +}; + +static const struct vop2_scl_regs rk3568_esmart_win_scl = { + .scale_yrgb_x = VOP_REG(RK3568_ESMART0_REGION0_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(RK3568_ESMART0_REGION0_SCL_FACTOR_YRGB, 0xffff, 16), + .scale_cbcr_x = VOP_REG(RK3568_ESMART0_REGION0_SCL_FACTOR_CBR, 0xffff, 0x0), + .scale_cbcr_y = VOP_REG(RK3568_ESMART0_REGION0_SCL_FACTOR_CBR, 0xffff, 16), + .yrgb_hor_scl_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 0), + .yrgb_hscl_filter_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 2), + .yrgb_ver_scl_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 4), + .yrgb_vscl_filter_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 6), + .cbcr_hor_scl_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 8), + .cbcr_hscl_filter_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 10), + .cbcr_ver_scl_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 12), + .cbcr_vscl_filter_mode = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 14), + .bic_coe_sel = VOP_REG(RK3568_ESMART0_REGION0_SCL_CTRL, 0x3, 16), + .vsd_yrgb_gt2 = VOP_REG(RK3568_ESMART0_REGION0_CTRL, 0x1, 8), + .vsd_yrgb_gt4 = VOP_REG(RK3568_ESMART0_REGION0_CTRL, 0x1, 9), + .vsd_cbcr_gt2 = VOP_REG(RK3568_ESMART0_REGION0_CTRL, 0x1, 10), + .vsd_cbcr_gt4 = VOP_REG(RK3568_ESMART0_REGION0_CTRL, 0x1, 11), +}; + +static const struct vop2_win_regs rk3568_area1_data = { + .enable = VOP_REG(RK3568_ESMART0_REGION1_CTRL, 0x1, 0), + .format = VOP_REG(RK3568_ESMART0_REGION1_CTRL, 0x1f, 1), + .rb_swap = VOP_REG(RK3568_ESMART0_REGION1_CTRL, 0x1, 14), + .act_info = VOP_REG(RK3568_ESMART0_REGION1_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3568_ESMART0_REGION1_DSP_INFO, 0x1fff1fff, 0), + .dsp_st = VOP_REG(RK3568_ESMART0_REGION1_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3568_ESMART0_REGION1_YRGB_MST, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3568_ESMART0_REGION1_CBR_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3568_ESMART0_REGION1_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3568_ESMART0_REGION1_VIR, 0xffff, 16), +}; + +static const struct vop2_win_regs rk3568_area2_data = { + .enable = VOP_REG(RK3568_ESMART0_REGION2_CTRL, 0x1, 0), + .format = VOP_REG(RK3568_ESMART0_REGION2_CTRL, 0x1f, 1), + .rb_swap = VOP_REG(RK3568_ESMART0_REGION2_CTRL, 0x1, 14), + .act_info = VOP_REG(RK3568_ESMART0_REGION2_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3568_ESMART0_REGION2_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3568_ESMART0_REGION2_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3568_ESMART0_REGION2_YRGB_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3568_ESMART0_REGION2_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3568_ESMART0_REGION2_VIR, 0xffff, 16), +}; + +static const struct vop2_win_regs rk3568_area3_data = { + .enable = VOP_REG(RK3568_ESMART0_REGION3_CTRL, 0x1, 0), + .format = VOP_REG(RK3568_ESMART0_REGION3_CTRL, 0x1f, 1), + .rb_swap = VOP_REG(RK3568_ESMART0_REGION3_CTRL, 0x1, 14), + .act_info = VOP_REG(RK3568_ESMART0_REGION3_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3568_ESMART0_REGION3_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3568_ESMART0_REGION3_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3568_ESMART0_REGION3_YRGB_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3568_ESMART0_REGION3_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3568_ESMART0_REGION3_VIR, 0xffff, 16), +}; + +static const struct vop2_win_regs *rk3568_area_data[] = { + &rk3568_area1_data, + &rk3568_area2_data, + &rk3568_area3_data +}; + +static const struct vop2_win_regs rk3568_cluster_win_data = { + .scl = &rk3568_cluster_win_scl, + .afbc = &rk3568_vop_afbc, + .gate = VOP_REG(RK3568_CLUSTER0_CTRL, 1, 0), + .lb_mode = VOP_REG(RK3568_CLUSTER0_CTRL, 0xf, 4), + .enable = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL0, 0x1, 0), + .format = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL0, 0x1f, 1), + .rb_swap = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL0, 0x1, 14), + .act_info = VOP_REG(RK3568_CLUSTER0_WIN0_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3568_CLUSTER0_WIN0_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3568_CLUSTER0_WIN0_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3568_CLUSTER0_WIN0_YRGB_MST, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3568_CLUSTER0_WIN0_CBR_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3568_CLUSTER0_WIN0_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3568_CLUSTER0_WIN0_VIR, 0xffff, 16), + .y2r_en = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL0, 0x1, 8), + .r2y_en = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL0, 0x1, 9), + .csc_mode = VOP_REG(RK3568_CLUSTER0_WIN0_CTRL0, 0x3, 10), +}; + +static const struct vop2_win_regs rk3568_esmart_win_data = { + .scl = &rk3568_esmart_win_scl, + .enable = VOP_REG(RK3568_ESMART0_REGION0_CTRL, 0x1, 0), + .format = VOP_REG(RK3568_ESMART0_REGION0_CTRL, 0x1f, 1), + .rb_swap = VOP_REG(RK3568_ESMART0_REGION0_CTRL, 0x1, 14), + .act_info = VOP_REG(RK3568_ESMART0_REGION0_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3568_ESMART0_REGION0_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3568_ESMART0_REGION0_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3568_ESMART0_REGION0_YRGB_MST, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3568_ESMART0_REGION0_CBR_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3568_ESMART0_REGION0_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3568_ESMART0_REGION0_VIR, 0xffff, 16), + .y2r_en = VOP_REG(RK3568_ESMART0_CTRL0, 0x1, 0), + .r2y_en = VOP_REG(RK3568_ESMART0_CTRL0, 0x1, 9), + .csc_mode = VOP_REG(RK3568_ESMART0_CTRL0, 0x3, 10), +}; + +/* + * rk3568 vop with 2 cluster, 2 esmart win, 2 smart win. + * Every cluster can work as 4K win or split into two win. + * All win in cluster support AFBCD. + * + * Every esmart win and smart win support 4 Multi-region. + * + * Scale filter mode: + * + * * Cluster: bicubic for horizontal scale up, others use bilinear + * * ESmart: + * * nearest-neighbor/bilinear/bicubic for scale up + * * nearest-neighbor/bilinear/average for scale down + * + * + * @TODO describe the wind like cpu-map dt nodes; + */ +static const struct vop2_win_data rk3568_vop_win_data[] = { + { + .name = "Cluster0-win0", + .phys_id = 0, + .base = 0x00, + .formats = formats_win_full_10bit, + .nformats = ARRAY_SIZE(formats_win_full_10bit), + .layer_sel_id = 0, + .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_cluster_win_data, + .max_upscale_factor = 4, + .max_downscale_factor = 4, + .type = DRM_PLANE_TYPE_OVERLAY, + .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_MAIN, + }, + + { + .name = "Cluster0-win1", + .phys_id = 0, + .base = 0x80, + .layer_sel_id = -1, + .formats = formats_win_full_10bit, + .nformats = ARRAY_SIZE(formats_win_full_10bit), + .supported_rotations = DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_cluster_win_data, + .max_upscale_factor = 4, + .max_downscale_factor = 4, + .type = DRM_PLANE_TYPE_OVERLAY, + .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_SUB, + }, + + { + .name = "Cluster1-win0", + .phys_id = 1, + .base = 0x200, + .formats = formats_win_full_10bit, + .nformats = ARRAY_SIZE(formats_win_full_10bit), + .layer_sel_id = 1, + .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_cluster_win_data, + .type = DRM_PLANE_TYPE_OVERLAY, + .max_upscale_factor = 4, + .max_downscale_factor = 4, + .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_MAIN, + }, + + { + .name = "Cluster1-win1", + .phys_id = 1, + .layer_sel_id = -1, + .formats = formats_win_full_10bit, + .nformats = ARRAY_SIZE(formats_win_full_10bit), + .base = 0x280, + .supported_rotations = DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_cluster_win_data, + .type = DRM_PLANE_TYPE_OVERLAY, + .max_upscale_factor = 4, + .max_downscale_factor = 4, + .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER_SUB, + }, + + { + .name = "Esmart0-win0", + .phys_id = 2, + .formats = formats_win_full_10bit, + .nformats = ARRAY_SIZE(formats_win_full_10bit), + .base = 0x0, + .layer_sel_id = 2, + .supported_rotations = DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_esmart_win_data, + .area = rk3568_area_data, + .area_size = ARRAY_SIZE(rk3568_area_data), + .type = DRM_PLANE_TYPE_PRIMARY, + .max_upscale_factor = 8, + .max_downscale_factor = 8, + }, + + { + .name = "Esmart1-win0", + .phys_id = 3, + .formats = formats_win_full_10bit, + .nformats = ARRAY_SIZE(formats_win_full_10bit), + .base = 0x200, + .layer_sel_id = 6, + .supported_rotations = DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_esmart_win_data, + .area = rk3568_area_data, + .area_size = ARRAY_SIZE(rk3568_area_data), + .type = DRM_PLANE_TYPE_PRIMARY, + .max_upscale_factor = 8, + .max_downscale_factor = 8, + }, + + { + .name = "Smart0-win0", + .phys_id = 4, + .base = 0x400, + .formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + .layer_sel_id = 3, + .supported_rotations = DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_esmart_win_data, + .area = rk3568_area_data, + .area_size = ARRAY_SIZE(rk3568_area_data), + .type = DRM_PLANE_TYPE_PRIMARY, + .max_upscale_factor = 8, + .max_downscale_factor = 8, + }, + + { + .name = "Smart1-win0", + .phys_id = 5, + .formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + .base = 0x600, + .layer_sel_id = 7, + .supported_rotations = DRM_MODE_REFLECT_Y, + .hsu_filter_mode = VOP2_SCALE_UP_BIC, + .hsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .vsu_filter_mode = VOP2_SCALE_UP_BIL, + .vsd_filter_mode = VOP2_SCALE_DOWN_BIL, + .regs = &rk3568_esmart_win_data, + .area = rk3568_area_data, + .area_size = ARRAY_SIZE(rk3568_area_data), + .type = DRM_PLANE_TYPE_OVERLAY, + .max_upscale_factor = 8, + .max_downscale_factor = 8, + }, +}; + +static const struct vop_grf_ctrl rk3568_grf_ctrl = { + .grf_dclk_inv = VOP_REG(RK3568_GRF_VO_CON1, 0x1, 3), +}; + +static const struct vop2_ctrl rk3568_vop_ctrl = { + .cfg_done_en = VOP_REG(RK3568_REG_CFG_DONE, 0x1, 15), + .ovl_cfg_done_port = VOP_REG(RK3568_OVL_LAYER_SEL, 0x3, 30), + .ovl_port_mux_cfg_done_imd = VOP_REG(RK3568_OVL_CTRL, 0x1, 28), + .if_ctrl_cfg_done_imd = VOP_REG(RK3568_DSP_IF_POL, 0x1, 28), + .version = VOP_REG(RK3568_VERSION_INFO, 0xffff, 16), + .cluster0_src_color_ctrl = VOP_REG(RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL, 0xffffffff, 0), + .cluster0_dst_color_ctrl = VOP_REG(RK3568_CLUSTER0_MIX_DST_COLOR_CTRL, 0xffffffff, 0), + .cluster0_src_alpha_ctrl = VOP_REG(RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL, 0xffffffff, 0), + .cluster0_dst_alpha_ctrl = VOP_REG(RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL, 0xffffffff, 0), + .src_color_ctrl = VOP_REG(RK3568_MIX0_SRC_COLOR_CTRL, 0xffffffff, 0), + .dst_color_ctrl = VOP_REG(RK3568_MIX0_DST_COLOR_CTRL, 0xffffffff, 0), + .src_alpha_ctrl = VOP_REG(RK3568_MIX0_SRC_ALPHA_CTRL, 0xffffffff, 0), + .dst_alpha_ctrl = VOP_REG(RK3568_MIX0_DST_ALPHA_CTRL, 0xffffffff, 0), + .rgb_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 0), + .hdmi0_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 1), + .edp0_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 3), + .mipi0_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 4), + .mipi1_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 20), + .lvds0_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 5), + .lvds1_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 24), + .bt1120_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 6), + .bt656_en = VOP_REG(RK3568_DSP_IF_EN, 0x1, 7), + .rgb_mux = VOP_REG(RK3568_DSP_IF_EN, 0x3, 8), + .hdmi0_mux = VOP_REG(RK3568_DSP_IF_EN, 0x3, 10), + .edp0_mux = VOP_REG(RK3568_DSP_IF_EN, 0x3, 14), + .mipi0_mux = VOP_REG(RK3568_DSP_IF_EN, 0x3, 16), + .mipi1_mux = VOP_REG(RK3568_DSP_IF_EN, 0x3, 21), + .lvds0_mux = VOP_REG(RK3568_DSP_IF_EN, 0x3, 18), + .lvds1_mux = VOP_REG(RK3568_DSP_IF_EN, 0x3, 25), + .lvds_dual_en = VOP_REG(RK3568_DSP_IF_CTRL, 0x1, 0), + .lvds_dual_mode = VOP_REG(RK3568_DSP_IF_CTRL, 0x1, 1), + .lvds_dual_channel_swap = VOP_REG(RK3568_DSP_IF_CTRL, 0x1, 2), + .lvds_pin_pol = VOP_REG(RK3568_DSP_IF_POL, 0x7, 0), + .lvds_dclk_pol = VOP_REG(RK3568_DSP_IF_POL, 0x1, 3), + .hdmi_pin_pol = VOP_REG(RK3568_DSP_IF_POL, 0x7, 4), + .hdmi_dclk_pol = VOP_REG(RK3568_DSP_IF_POL, 0x1, 7), + .edp_pin_pol = VOP_REG(RK3568_DSP_IF_POL, 0x3, 12), + .edp_dclk_pol = VOP_REG(RK3568_DSP_IF_POL, 0x1, 15), + .mipi_dclk_pol = VOP_REG(RK3568_DSP_IF_POL, 0x7, 16), + .mipi_pin_pol = VOP_REG(RK3568_DSP_IF_POL, 0x1, 19), + .win_vp_id[0] = VOP_REG(RK3568_OVL_PORT_SEL, 0x3, 16), + .win_vp_id[1] = VOP_REG(RK3568_OVL_PORT_SEL, 0x3, 18), + .win_vp_id[2] = VOP_REG(RK3568_OVL_PORT_SEL, 0x3, 24), + .win_vp_id[3] = VOP_REG(RK3568_OVL_PORT_SEL, 0x3, 26), + .win_vp_id[4] = VOP_REG(RK3568_OVL_PORT_SEL, 0x3, 28), + .win_vp_id[5] = VOP_REG(RK3568_OVL_PORT_SEL, 0x3, 30), +}; + +static const struct vop2_data rk3568_vop = { + .version = VOP_VERSION(0x40, 0x15), + .nr_vps = 3, + .nr_mixers = 5, + .max_input = { 4096, 2304 }, + .max_output = { 4096, 2304 }, + .ctrl = &rk3568_vop_ctrl, + .grf_ctrl = &rk3568_grf_ctrl, + .axi_intr = rk3568_vop_axi_intr, + .nr_axi_intr = ARRAY_SIZE(rk3568_vop_axi_intr), + .vp = rk3568_vop_video_ports, + .layer = rk3568_vop_layers, + .nr_layers = ARRAY_SIZE(rk3568_vop_layers), + .win = rk3568_vop_win_data, + .win_size = ARRAY_SIZE(rk3568_vop_win_data), +}; + +static const struct of_device_id vop2_dt_match[] = { + { .compatible = "rockchip,rk3568-vop", + .data = &rk3568_vop }, + {}, +}; +MODULE_DEVICE_TABLE(of, vop2_dt_match); + +static int vop2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + if (!dev->of_node) { + DRM_DEV_ERROR(dev, "can't find vop2 devices\n"); + return -ENODEV; + } + return component_add(dev, &vop2_component_ops); +} + +static int vop2_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vop2_component_ops); + + return 0; +} + +struct platform_driver vop2_platform_driver = { + .probe = vop2_probe, + .remove = vop2_remove, + .driver = { + .name = "rockchip-vop2", + .of_match_table = of_match_ptr(vop2_dt_match), + }, +}; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index 9ea2af6b8c1e..cb7c3dc5e179 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h @@ -1045,4 +1045,383 @@ #define RV1126_GRF_IOFUNC_CON3 0x1026c +/* rk3568 vop registers definition */ + +#define RK3568_GRF_VO_CON1 0x0364 +/* System registers definition */ +#define RK3568_REG_CFG_DONE 0x000 +#define RK3568_VERSION_INFO 0x004 +#define RK3568_DSP_IF_EN 0x028 +#define RK3568_DSP_IF_CTRL 0x02c +#define RK3568_DSP_IF_POL 0x030 +#define RK3568_VP0_LINE_FLAG 0x70 +#define RK3568_VP1_LINE_FLAG 0x74 +#define RK3568_VP2_LINE_FLAG 0x78 +#define RK3568_SYS0_INT_EN 0x80 +#define RK3568_SYS0_INT_CLR 0x84 +#define RK3568_SYS0_INT_STATUS 0x88 +#define RK3568_SYS1_INT_EN 0x90 +#define RK3568_SYS1_INT_CLR 0x94 +#define RK3568_SYS1_INT_STATUS 0x98 +#define RK3568_VP0_INT_EN 0xA0 +#define RK3568_VP0_INT_CLR 0xA4 +#define RK3568_VP0_INT_STATUS 0xA8 +#define RK3568_VP1_INT_EN 0xB0 +#define RK3568_VP1_INT_CLR 0xB4 +#define RK3568_VP1_INT_STATUS 0xB8 +#define RK3568_VP2_INT_EN 0xC0 +#define RK3568_VP2_INT_CLR 0xC4 +#define RK3568_VP2_INT_STATUS 0xC8 + +/* Video Port registers definition */ +#define RK3568_VP0_DSP_CTRL 0xC00 +#define RK3568_VP0_MIPI_CTRL 0xC04 +#define RK3568_VP0_COLOR_BAR_CTRL 0xC08 +#define RK3568_VP0_PRE_SCAN_HTIMING 0xC30 +#define RK3568_VP0_POST_DSP_HACT_INFO 0xC34 +#define RK3568_VP0_POST_DSP_VACT_INFO 0xC38 +#define RK3568_VP0_POST_SCL_FACTOR_YRGB 0xC3C +#define RK3568_VP0_POST_SCL_CTRL 0xC40 +#define RK3568_VP0_POST_DSP_VACT_INFO_F1 0xC44 +#define RK3568_VP0_DSP_HTOTAL_HS_END 0xC48 +#define RK3568_VP0_DSP_HACT_ST_END 0xC4C +#define RK3568_VP0_DSP_VTOTAL_VS_END 0xC50 +#define RK3568_VP0_DSP_VACT_ST_END 0xC54 +#define RK3568_VP0_DSP_VS_ST_END_F1 0xC58 +#define RK3568_VP0_DSP_VACT_ST_END_F1 0xC5C + +#define RK3568_VP1_DSP_CTRL 0xD00 +#define RK3568_VP1_MIPI_CTRL 0xD04 +#define RK3568_VP1_COLOR_BAR_CTRL 0xD08 +#define RK3568_VP1_PRE_SCAN_HTIMING 0xD30 +#define RK3568_VP1_POST_DSP_HACT_INFO 0xD34 +#define RK3568_VP1_POST_DSP_VACT_INFO 0xD38 +#define RK3568_VP1_POST_SCL_FACTOR_YRGB 0xD3C +#define RK3568_VP1_POST_SCL_CTRL 0xD40 +#define RK3568_VP1_DSP_HACT_INFO 0xD34 +#define RK3568_VP1_DSP_VACT_INFO 0xD38 +#define RK3568_VP1_POST_DSP_VACT_INFO_F1 0xD44 +#define RK3568_VP1_DSP_HTOTAL_HS_END 0xD48 +#define RK3568_VP1_DSP_HACT_ST_END 0xD4C +#define RK3568_VP1_DSP_VTOTAL_VS_END 0xD50 +#define RK3568_VP1_DSP_VACT_ST_END 0xD54 +#define RK3568_VP1_DSP_VS_ST_END_F1 0xD58 +#define RK3568_VP1_DSP_VACT_ST_END_F1 0xD5C + +#define RK3568_VP2_DSP_CTRL 0xE00 +#define RK3568_VP2_MIPI_CTRL 0xE04 +#define RK3568_VP2_COLOR_BAR_CTRL 0xE08 +#define RK3568_VP2_PRE_SCAN_HTIMING 0xE30 +#define RK3568_VP2_POST_DSP_HACT_INFO 0xE34 +#define RK3568_VP2_POST_DSP_VACT_INFO 0xE38 +#define RK3568_VP2_POST_SCL_FACTOR_YRGB 0xE3C +#define RK3568_VP2_POST_SCL_CTRL 0xE40 +#define RK3568_VP2_DSP_HACT_INFO 0xE34 +#define RK3568_VP2_DSP_VACT_INFO 0xE38 +#define RK3568_VP2_POST_DSP_VACT_INFO_F1 0xE44 +#define RK3568_VP2_DSP_HTOTAL_HS_END 0xE48 +#define RK3568_VP2_DSP_HACT_ST_END 0xE4C +#define RK3568_VP2_DSP_VTOTAL_VS_END 0xE50 +#define RK3568_VP2_DSP_VACT_ST_END 0xE54 +#define RK3568_VP2_DSP_VS_ST_END_F1 0xE58 +#define RK3568_VP2_DSP_VACT_ST_END_F1 0xE5C + +/* Overlay registers definition */ +#define RK3568_OVL_CTRL 0x600 +#define RK3568_OVL_LAYER_SEL 0x604 +#define RK3568_OVL_PORT_SEL 0x608 +#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL 0x610 +#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL 0x614 +#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL 0x618 +#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL 0x61C +#define RK3568_MIX0_SRC_COLOR_CTRL 0x650 +#define RK3568_MIX0_DST_COLOR_CTRL 0x654 +#define RK3568_MIX0_SRC_ALPHA_CTRL 0x658 +#define RK3568_MIX0_DST_ALPHA_CTRL 0x65C +#define RK3568_HDR0_SRC_COLOR_CTRL 0x6C0 +#define RK3568_HDR0_DST_COLOR_CTRL 0x6C4 +#define RK3568_HDR0_SRC_ALPHA_CTRL 0x6C8 +#define RK3568_HDR0_DST_ALPHA_CTRL 0x6CC +#define RK3568_VP0_BG_MIX_CTRL 0x6E0 +#define RK3568_VP1_BG_MIX_CTRL 0x6E4 +#define RK3568_VP2_BG_MIX_CTRL 0x6E8 +#define RK3568_CLUSTER_DLY_NUM 0x6F0 +#define RK3568_SMART_DLY_NUM 0x6F8 + +/* Cluster0 register definition */ +#define RK3568_CLUSTER0_WIN0_CTRL0 0x1000 +#define RK3568_CLUSTER0_WIN0_CTRL1 0x1004 +#define RK3568_CLUSTER0_WIN0_YRGB_MST 0x1010 +#define RK3568_CLUSTER0_WIN0_CBR_MST 0x1014 +#define RK3568_CLUSTER0_WIN0_VIR 0x1018 +#define RK3568_CLUSTER0_WIN0_ACT_INFO 0x1020 +#define RK3568_CLUSTER0_WIN0_DSP_INFO 0x1024 +#define RK3568_CLUSTER0_WIN0_DSP_ST 0x1028 +#define RK3568_CLUSTER0_WIN0_SCL_FACTOR_YRGB 0x1030 +#define RK3568_CLUSTER0_WIN0_AFBCD_ROTATE_MODE 0x1054 +#define RK3568_CLUSTER0_WIN0_AFBCD_HDR_PTR 0x1058 +#define RK3568_CLUSTER0_WIN0_AFBCD_VIR_WIDTH 0x105C +#define RK3568_CLUSTER0_WIN0_AFBCD_PIC_SIZE 0x1060 +#define RK3568_CLUSTER0_WIN0_AFBCD_PIC_OFFSET 0x1064 +#define RK3568_CLUSTER0_WIN0_AFBCD_DSP_OFFSET 0x1068 +#define RK3568_CLUSTER0_WIN0_AFBCD_CTRL 0x106C + +#define RK3568_CLUSTER0_WIN1_CTRL0 0x1080 +#define RK3568_CLUSTER0_WIN1_CTRL1 0x1084 +#define RK3568_CLUSTER0_WIN1_YRGB_MST 0x1090 +#define RK3568_CLUSTER0_WIN1_CBR_MST 0x1094 +#define RK3568_CLUSTER0_WIN1_VIR 0x1098 +#define RK3568_CLUSTER0_WIN1_ACT_INFO 0x10A0 +#define RK3568_CLUSTER0_WIN1_DSP_INFO 0x10A4 +#define RK3568_CLUSTER0_WIN1_DSP_ST 0x10A8 +#define RK3568_CLUSTER0_WIN1_SCL_FACTOR_YRGB 0x10B0 +#define RK3568_CLUSTER0_WIN1_AFBCD_ROTATE_MODE 0x10D4 +#define RK3568_CLUSTER0_WIN1_AFBCD_HDR_PTR 0x10D8 +#define RK3568_CLUSTER0_WIN1_AFBCD_VIR_WIDTH 0x10DC +#define RK3568_CLUSTER0_WIN1_AFBCD_PIC_SIZE 0x10E0 +#define RK3568_CLUSTER0_WIN1_AFBCD_PIC_OFFSET 0x10E4 +#define RK3568_CLUSTER0_WIN1_AFBCD_DSP_OFFSET 0x10E8 +#define RK3568_CLUSTER0_WIN1_AFBCD_CTRL 0x10EC + +#define RK3568_CLUSTER0_CTRL 0x1100 + +#define RK3568_CLUSTER1_WIN0_CTRL0 0x1200 +#define RK3568_CLUSTER1_WIN0_CTRL1 0x1204 +#define RK3568_CLUSTER1_WIN0_YRGB_MST 0x1210 +#define RK3568_CLUSTER1_WIN0_CBR_MST 0x1214 +#define RK3568_CLUSTER1_WIN0_VIR 0x1218 +#define RK3568_CLUSTER1_WIN0_ACT_INFO 0x1220 +#define RK3568_CLUSTER1_WIN0_DSP_INFO 0x1224 +#define RK3568_CLUSTER1_WIN0_DSP_ST 0x1228 +#define RK3568_CLUSTER1_WIN0_SCL_FACTOR_YRGB 0x1230 +#define RK3568_CLUSTER1_WIN0_AFBCD_ROTATE_MODE 0x1254 +#define RK3568_CLUSTER1_WIN0_AFBCD_HDR_PTR 0x1258 +#define RK3568_CLUSTER1_WIN0_AFBCD_VIR_WIDTH 0x125C +#define RK3568_CLUSTER1_WIN0_AFBCD_PIC_SIZE 0x1260 +#define RK3568_CLUSTER1_WIN0_AFBCD_PIC_OFFSET 0x1264 +#define RK3568_CLUSTER1_WIN0_AFBCD_DSP_OFFSET 0x1268 +#define RK3568_CLUSTER1_WIN0_AFBCD_CTRL 0x126C + +#define RK3568_CLUSTER1_WIN1_CTRL0 0x1280 +#define RK3568_CLUSTER1_WIN1_CTRL1 0x1284 +#define RK3568_CLUSTER1_WIN1_YRGB_MST 0x1290 +#define RK3568_CLUSTER1_WIN1_CBR_MST 0x1294 +#define RK3568_CLUSTER1_WIN1_VIR 0x1298 +#define RK3568_CLUSTER1_WIN1_ACT_INFO 0x12A0 +#define RK3568_CLUSTER1_WIN1_DSP_INFO 0x12A4 +#define RK3568_CLUSTER1_WIN1_DSP_ST 0x12A8 +#define RK3568_CLUSTER1_WIN1_SCL_FACTOR_YRGB 0x12B0 +#define RK3568_CLUSTER1_WIN1_AFBCD_ROTATE_MODE 0x12D4 +#define RK3568_CLUSTER1_WIN1_AFBCD_HDR_PTR 0x12D8 +#define RK3568_CLUSTER1_WIN1_AFBCD_VIR_WIDTH 0x12DC +#define RK3568_CLUSTER1_WIN1_AFBCD_PIC_SIZE 0x12E0 +#define RK3568_CLUSTER1_WIN1_AFBCD_PIC_OFFSET 0x12E4 +#define RK3568_CLUSTER1_WIN1_AFBCD_DSP_OFFSET 0x12E8 +#define RK3568_CLUSTER1_WIN1_AFBCD_CTRL 0x12EC + +#define RK3568_CLUSTER1_CTRL 0x1300 + +/* Esmart register definition */ +#define RK3568_ESMART0_CTRL0 0x1800 +#define RK3568_ESMART0_CTRL1 0x1804 +#define RK3568_ESMART0_REGION0_CTRL 0x1810 +#define RK3568_ESMART0_REGION0_YRGB_MST 0x1814 +#define RK3568_ESMART0_REGION0_CBR_MST 0x1818 +#define RK3568_ESMART0_REGION0_VIR 0x181C +#define RK3568_ESMART0_REGION0_ACT_INFO 0x1820 +#define RK3568_ESMART0_REGION0_DSP_INFO 0x1824 +#define RK3568_ESMART0_REGION0_DSP_ST 0x1828 +#define RK3568_ESMART0_REGION0_SCL_CTRL 0x1830 +#define RK3568_ESMART0_REGION0_SCL_FACTOR_YRGB 0x1834 +#define RK3568_ESMART0_REGION0_SCL_FACTOR_CBR 0x1838 +#define RK3568_ESMART0_REGION0_SCL_OFFSET 0x183C +#define RK3568_ESMART0_REGION1_CTRL 0x1840 +#define RK3568_ESMART0_REGION1_YRGB_MST 0x1844 +#define RK3568_ESMART0_REGION1_CBR_MST 0x1848 +#define RK3568_ESMART0_REGION1_VIR 0x184C +#define RK3568_ESMART0_REGION1_ACT_INFO 0x1850 +#define RK3568_ESMART0_REGION1_DSP_INFO 0x1854 +#define RK3568_ESMART0_REGION1_DSP_ST 0x1858 +#define RK3568_ESMART0_REGION1_SCL_CTRL 0x1860 +#define RK3568_ESMART0_REGION1_SCL_FACTOR_YRGB 0x1864 +#define RK3568_ESMART0_REGION1_SCL_FACTOR_CBR 0x1868 +#define RK3568_ESMART0_REGION1_SCL_OFFSET 0x186C +#define RK3568_ESMART0_REGION2_CTRL 0x1870 +#define RK3568_ESMART0_REGION2_YRGB_MST 0x1874 +#define RK3568_ESMART0_REGION2_CBR_MST 0x1878 +#define RK3568_ESMART0_REGION2_VIR 0x187C +#define RK3568_ESMART0_REGION2_ACT_INFO 0x1880 +#define RK3568_ESMART0_REGION2_DSP_INFO 0x1884 +#define RK3568_ESMART0_REGION2_DSP_ST 0x1888 +#define RK3568_ESMART0_REGION2_SCL_CTRL 0x1890 +#define RK3568_ESMART0_REGION2_SCL_FACTOR_YRGB 0x1894 +#define RK3568_ESMART0_REGION2_SCL_FACTOR_CBR 0x1898 +#define RK3568_ESMART0_REGION2_SCL_OFFSET 0x189C +#define RK3568_ESMART0_REGION3_CTRL 0x18A0 +#define RK3568_ESMART0_REGION3_YRGB_MST 0x18A4 +#define RK3568_ESMART0_REGION3_CBR_MST 0x18A8 +#define RK3568_ESMART0_REGION3_VIR 0x18AC +#define RK3568_ESMART0_REGION3_ACT_INFO 0x18B0 +#define RK3568_ESMART0_REGION3_DSP_INFO 0x18B4 +#define RK3568_ESMART0_REGION3_DSP_ST 0x18B8 +#define RK3568_ESMART0_REGION3_SCL_CTRL 0x18C0 +#define RK3568_ESMART0_REGION3_SCL_FACTOR_YRGB 0x18C4 +#define RK3568_ESMART0_REGION3_SCL_FACTOR_CBR 0x18C8 +#define RK3568_ESMART0_REGION3_SCL_OFFSET 0x18CC + +#define RK3568_ESMART1_CTRL0 0x1A00 +#define RK3568_ESMART1_CTRL1 0x1A04 +#define RK3568_ESMART1_REGION0_CTRL 0x1A10 +#define RK3568_ESMART1_REGION0_YRGB_MST 0x1A14 +#define RK3568_ESMART1_REGION0_CBR_MST 0x1A18 +#define RK3568_ESMART1_REGION0_VIR 0x1A1C +#define RK3568_ESMART1_REGION0_ACT_INFO 0x1A20 +#define RK3568_ESMART1_REGION0_DSP_INFO 0x1A24 +#define RK3568_ESMART1_REGION0_DSP_ST 0x1A28 +#define RK3568_ESMART1_REGION0_SCL_CTRL 0x1A30 +#define RK3568_ESMART1_REGION0_SCL_FACTOR_YRGB 0x1A34 +#define RK3568_ESMART1_REGION0_SCL_FACTOR_CBR 0x1A38 +#define RK3568_ESMART1_REGION0_SCL_OFFSET 0x1A3C +#define RK3568_ESMART1_REGION1_CTRL 0x1A40 +#define RK3568_ESMART1_REGION1_YRGB_MST 0x1A44 +#define RK3568_ESMART1_REGION1_CBR_MST 0x1A48 +#define RK3568_ESMART1_REGION1_VIR 0x1A4C +#define RK3568_ESMART1_REGION1_ACT_INFO 0x1A50 +#define RK3568_ESMART1_REGION1_DSP_INFO 0x1A54 +#define RK3568_ESMART1_REGION1_DSP_ST 0x1A58 +#define RK3568_ESMART1_REGION1_SCL_CTRL 0x1A60 +#define RK3568_ESMART1_REGION1_SCL_FACTOR_YRGB 0x1A64 +#define RK3568_ESMART1_REGION1_SCL_FACTOR_CBR 0x1A68 +#define RK3568_ESMART1_REGION1_SCL_OFFSET 0x1A6C +#define RK3568_ESMART1_REGION2_CTRL 0x1A70 +#define RK3568_ESMART1_REGION2_YRGB_MST 0x1A74 +#define RK3568_ESMART1_REGION2_CBR_MST 0x1A78 +#define RK3568_ESMART1_REGION2_VIR 0x1A7C +#define RK3568_ESMART1_REGION2_ACT_INFO 0x1A80 +#define RK3568_ESMART1_REGION2_DSP_INFO 0x1A84 +#define RK3568_ESMART1_REGION2_DSP_ST 0x1A88 +#define RK3568_ESMART1_REGION2_SCL_CTRL 0x1A90 +#define RK3568_ESMART1_REGION2_SCL_FACTOR_YRGB 0x1A94 +#define RK3568_ESMART1_REGION2_SCL_FACTOR_CBR 0x1A98 +#define RK3568_ESMART1_REGION2_SCL_OFFSET 0x1A9C +#define RK3568_ESMART1_REGION3_CTRL 0x1AA0 +#define RK3568_ESMART1_REGION3_YRGB_MST 0x1AA4 +#define RK3568_ESMART1_REGION3_CBR_MST 0x1AA8 +#define RK3568_ESMART1_REGION3_VIR 0x1AAC +#define RK3568_ESMART1_REGION3_ACT_INFO 0x1AB0 +#define RK3568_ESMART1_REGION3_DSP_INFO 0x1AB4 +#define RK3568_ESMART1_REGION3_DSP_ST 0x1AB8 +#define RK3568_ESMART1_REGION3_SCL_CTRL 0x1AC0 +#define RK3568_ESMART1_REGION3_SCL_FACTOR_YRGB 0x1AC4 +#define RK3568_ESMART1_REGION3_SCL_FACTOR_CBR 0x1AC8 +#define RK3568_ESMART1_REGION3_SCL_OFFSET 0x1ACC + +#define RK3568_SMART0_CTRL0 0x1C00 +#define RK3568_SMART0_CTRL1 0x1C04 +#define RK3568_SMART0_REGION0_CTRL 0x1C10 +#define RK3568_SMART0_REGION0_YRGB_MST 0x1C14 +#define RK3568_SMART0_REGION0_CBR_MST 0x1C18 +#define RK3568_SMART0_REGION0_VIR 0x1C1C +#define RK3568_SMART0_REGION0_ACT_INFO 0x1C20 +#define RK3568_SMART0_REGION0_DSP_INFO 0x1C24 +#define RK3568_SMART0_REGION0_DSP_ST 0x1C28 +#define RK3568_SMART0_REGION0_SCL_CTRL 0x1C30 +#define RK3568_SMART0_REGION0_SCL_FACTOR_YRGB 0x1C34 +#define RK3568_SMART0_REGION0_SCL_FACTOR_CBR 0x1C38 +#define RK3568_SMART0_REGION0_SCL_OFFSET 0x1C3C +#define RK3568_SMART0_REGION1_CTRL 0x1C40 +#define RK3568_SMART0_REGION1_YRGB_MST 0x1C44 +#define RK3568_SMART0_REGION1_CBR_MST 0x1C48 +#define RK3568_SMART0_REGION1_VIR 0x1C4C +#define RK3568_SMART0_REGION1_ACT_INFO 0x1C50 +#define RK3568_SMART0_REGION1_DSP_INFO 0x1C54 +#define RK3568_SMART0_REGION1_DSP_ST 0x1C58 +#define RK3568_SMART0_REGION1_SCL_CTRL 0x1C60 +#define RK3568_SMART0_REGION1_SCL_FACTOR_YRGB 0x1C64 +#define RK3568_SMART0_REGION1_SCL_FACTOR_CBR 0x1C68 +#define RK3568_SMART0_REGION1_SCL_OFFSET 0x1C6C +#define RK3568_SMART0_REGION2_CTRL 0x1C70 +#define RK3568_SMART0_REGION2_YRGB_MST 0x1C74 +#define RK3568_SMART0_REGION2_CBR_MST 0x1C78 +#define RK3568_SMART0_REGION2_VIR 0x1C7C +#define RK3568_SMART0_REGION2_ACT_INFO 0x1C80 +#define RK3568_SMART0_REGION2_DSP_INFO 0x1C84 +#define RK3568_SMART0_REGION2_DSP_ST 0x1C88 +#define RK3568_SMART0_REGION2_SCL_CTRL 0x1C90 +#define RK3568_SMART0_REGION2_SCL_FACTOR_YRGB 0x1C94 +#define RK3568_SMART0_REGION2_SCL_FACTOR_CBR 0x1C98 +#define RK3568_SMART0_REGION2_SCL_OFFSET 0x1C9C +#define RK3568_SMART0_REGION3_CTRL 0x1CA0 +#define RK3568_SMART0_REGION3_YRGB_MST 0x1CA4 +#define RK3568_SMART0_REGION3_CBR_MST 0x1CA8 +#define RK3568_SMART0_REGION3_VIR 0x1CAC +#define RK3568_SMART0_REGION3_ACT_INFO 0x1CB0 +#define RK3568_SMART0_REGION3_DSP_INFO 0x1CB4 +#define RK3568_SMART0_REGION3_DSP_ST 0x1CB8 +#define RK3568_SMART0_REGION3_SCL_CTRL 0x1CC0 +#define RK3568_SMART0_REGION3_SCL_FACTOR_YRGB 0x1CC4 +#define RK3568_SMART0_REGION3_SCL_FACTOR_CBR 0x1CC8 +#define RK3568_SMART0_REGION3_SCL_OFFSET 0x1CCC + +#define RK3568_SMART1_CTRL0 0x1E00 +#define RK3568_SMART1_CTRL1 0x1E04 +#define RK3568_SMART1_REGION0_CTRL 0x1E10 +#define RK3568_SMART1_REGION0_YRGB_MST 0x1E14 +#define RK3568_SMART1_REGION0_CBR_MST 0x1E18 +#define RK3568_SMART1_REGION0_VIR 0x1E1C +#define RK3568_SMART1_REGION0_ACT_INFO 0x1E20 +#define RK3568_SMART1_REGION0_DSP_INFO 0x1E24 +#define RK3568_SMART1_REGION0_DSP_ST 0x1E28 +#define RK3568_SMART1_REGION0_SCL_CTRL 0x1E30 +#define RK3568_SMART1_REGION0_SCL_FACTOR_YRGB 0x1E34 +#define RK3568_SMART1_REGION0_SCL_FACTOR_CBR 0x1E38 +#define RK3568_SMART1_REGION0_SCL_OFFSET 0x1E3C +#define RK3568_SMART1_REGION1_CTRL 0x1E40 +#define RK3568_SMART1_REGION1_YRGB_MST 0x1E44 +#define RK3568_SMART1_REGION1_CBR_MST 0x1E48 +#define RK3568_SMART1_REGION1_VIR 0x1E4C +#define RK3568_SMART1_REGION1_ACT_INFO 0x1E50 +#define RK3568_SMART1_REGION1_DSP_INFO 0x1E54 +#define RK3568_SMART1_REGION1_DSP_ST 0x1E58 +#define RK3568_SMART1_REGION1_SCL_CTRL 0x1E60 +#define RK3568_SMART1_REGION1_SCL_FACTOR_YRGB 0x1E64 +#define RK3568_SMART1_REGION1_SCL_FACTOR_CBR 0x1E68 +#define RK3568_SMART1_REGION1_SCL_OFFSET 0x1E6C +#define RK3568_SMART1_REGION2_CTRL 0x1E70 +#define RK3568_SMART1_REGION2_YRGB_MST 0x1E74 +#define RK3568_SMART1_REGION2_CBR_MST 0x1E78 +#define RK3568_SMART1_REGION2_VIR 0x1E7C +#define RK3568_SMART1_REGION2_ACT_INFO 0x1E80 +#define RK3568_SMART1_REGION2_DSP_INFO 0x1E84 +#define RK3568_SMART1_REGION2_DSP_ST 0x1E88 +#define RK3568_SMART1_REGION2_SCL_CTRL 0x1E90 +#define RK3568_SMART1_REGION2_SCL_FACTOR_YRGB 0x1E94 +#define RK3568_SMART1_REGION2_SCL_FACTOR_CBR 0x1E98 +#define RK3568_SMART1_REGION2_SCL_OFFSET 0x1E9C +#define RK3568_SMART1_REGION3_CTRL 0x1EA0 +#define RK3568_SMART1_REGION3_YRGB_MST 0x1EA4 +#define RK3568_SMART1_REGION3_CBR_MST 0x1EA8 +#define RK3568_SMART1_REGION3_VIR 0x1EAC +#define RK3568_SMART1_REGION3_ACT_INFO 0x1EB0 +#define RK3568_SMART1_REGION3_DSP_INFO 0x1EB4 +#define RK3568_SMART1_REGION3_DSP_ST 0x1EB8 +#define RK3568_SMART1_REGION3_SCL_CTRL 0x1EC0 +#define RK3568_SMART1_REGION3_SCL_FACTOR_YRGB 0x1EC4 +#define RK3568_SMART1_REGION3_SCL_FACTOR_CBR 0x1EC8 +#define RK3568_SMART1_REGION3_SCL_OFFSET 0x1ECC + +/* HDR register definition */ +#define RK3568_HDR_LUT_CTRL 0x2000 +#define RK3568_HDR_LUT_MST 0x2004 +#define RK3568_SDR2HDR_CTRL 0x2010 +#define RK3568_HDR2SDR_CTRL 0x2020 +#define RK3568_HDR2SDR_SRC_RANGE 0x2024 +#define RK3568_HDR2SDR_NORMFACEETF 0x2028 +#define RK3568_HDR2SDR_DST_RANGE 0x202C +#define RK3568_HDR2SDR_NORMFACCGAMMA 0x2030 +#define RK3568_HDR_EETF_OETF_Y0 0x203C +#define RK3568_HDR_SAT_Y0 0x20C0 +#define RK3568_HDR_EOTF_OETF_Y0 0x20F0 +#define RK3568_HDR_OETF_DX_POW1 0x2200 +#define RK3568_HDR_OETF_XN1 0x2300 #endif /* _ROCKCHIP_VOP_REG_H */