162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020 Rockchip Electronics Co., Ltd. 462306a36Sopenharmony_ci * Author: Andy Yan <andy.yan@rock-chips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/component.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/iopoll.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/media-bus-format.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_graph.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/regmap.h> 2062306a36Sopenharmony_ci#include <linux/swab.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <drm/drm.h> 2362306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2462306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h> 2562306a36Sopenharmony_ci#include <drm/drm_blend.h> 2662306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2762306a36Sopenharmony_ci#include <drm/drm_debugfs.h> 2862306a36Sopenharmony_ci#include <drm/drm_flip_work.h> 2962306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 3062306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3162306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <uapi/linux/videodev2.h> 3462306a36Sopenharmony_ci#include <dt-bindings/soc/rockchip,vop2.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "rockchip_drm_drv.h" 3762306a36Sopenharmony_ci#include "rockchip_drm_gem.h" 3862306a36Sopenharmony_ci#include "rockchip_drm_fb.h" 3962306a36Sopenharmony_ci#include "rockchip_drm_vop2.h" 4062306a36Sopenharmony_ci#include "rockchip_rgb.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * VOP2 architecture 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci +----------+ +-------------+ +-----------+ 4662306a36Sopenharmony_ci | Cluster | | Sel 1 from 6| | 1 from 3 | 4762306a36Sopenharmony_ci | window0 | | Layer0 | | RGB | 4862306a36Sopenharmony_ci +----------+ +-------------+ +---------------+ +-------------+ +-----------+ 4962306a36Sopenharmony_ci +----------+ +-------------+ |N from 6 layers| | | 5062306a36Sopenharmony_ci | Cluster | | Sel 1 from 6| | Overlay0 +--->| Video Port0 | +-----------+ 5162306a36Sopenharmony_ci | window1 | | Layer1 | | | | | | 1 from 3 | 5262306a36Sopenharmony_ci +----------+ +-------------+ +---------------+ +-------------+ | LVDS | 5362306a36Sopenharmony_ci +----------+ +-------------+ +-----------+ 5462306a36Sopenharmony_ci | Esmart | | Sel 1 from 6| 5562306a36Sopenharmony_ci | window0 | | Layer2 | +---------------+ +-------------+ +-----------+ 5662306a36Sopenharmony_ci +----------+ +-------------+ |N from 6 Layers| | | +--> | 1 from 3 | 5762306a36Sopenharmony_ci +----------+ +-------------+ --------> | Overlay1 +--->| Video Port1 | | MIPI | 5862306a36Sopenharmony_ci | Esmart | | Sel 1 from 6| --------> | | | | +-----------+ 5962306a36Sopenharmony_ci | Window1 | | Layer3 | +---------------+ +-------------+ 6062306a36Sopenharmony_ci +----------+ +-------------+ +-----------+ 6162306a36Sopenharmony_ci +----------+ +-------------+ | 1 from 3 | 6262306a36Sopenharmony_ci | Smart | | Sel 1 from 6| +---------------+ +-------------+ | HDMI | 6362306a36Sopenharmony_ci | Window0 | | Layer4 | |N from 6 Layers| | | +-----------+ 6462306a36Sopenharmony_ci +----------+ +-------------+ | Overlay2 +--->| Video Port2 | 6562306a36Sopenharmony_ci +----------+ +-------------+ | | | | +-----------+ 6662306a36Sopenharmony_ci | Smart | | Sel 1 from 6| +---------------+ +-------------+ | 1 from 3 | 6762306a36Sopenharmony_ci | Window1 | | Layer5 | | eDP | 6862306a36Sopenharmony_ci +----------+ +-------------+ +-----------+ 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cienum vop2_data_format { 7362306a36Sopenharmony_ci VOP2_FMT_ARGB8888 = 0, 7462306a36Sopenharmony_ci VOP2_FMT_RGB888, 7562306a36Sopenharmony_ci VOP2_FMT_RGB565, 7662306a36Sopenharmony_ci VOP2_FMT_XRGB101010, 7762306a36Sopenharmony_ci VOP2_FMT_YUV420SP, 7862306a36Sopenharmony_ci VOP2_FMT_YUV422SP, 7962306a36Sopenharmony_ci VOP2_FMT_YUV444SP, 8062306a36Sopenharmony_ci VOP2_FMT_YUYV422 = 8, 8162306a36Sopenharmony_ci VOP2_FMT_YUYV420, 8262306a36Sopenharmony_ci VOP2_FMT_VYUY422, 8362306a36Sopenharmony_ci VOP2_FMT_VYUY420, 8462306a36Sopenharmony_ci VOP2_FMT_YUV420SP_TILE_8x4 = 0x10, 8562306a36Sopenharmony_ci VOP2_FMT_YUV420SP_TILE_16x2, 8662306a36Sopenharmony_ci VOP2_FMT_YUV422SP_TILE_8x4, 8762306a36Sopenharmony_ci VOP2_FMT_YUV422SP_TILE_16x2, 8862306a36Sopenharmony_ci VOP2_FMT_YUV420SP_10, 8962306a36Sopenharmony_ci VOP2_FMT_YUV422SP_10, 9062306a36Sopenharmony_ci VOP2_FMT_YUV444SP_10, 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cienum vop2_afbc_format { 9462306a36Sopenharmony_ci VOP2_AFBC_FMT_RGB565, 9562306a36Sopenharmony_ci VOP2_AFBC_FMT_ARGB2101010 = 2, 9662306a36Sopenharmony_ci VOP2_AFBC_FMT_YUV420_10BIT, 9762306a36Sopenharmony_ci VOP2_AFBC_FMT_RGB888, 9862306a36Sopenharmony_ci VOP2_AFBC_FMT_ARGB8888, 9962306a36Sopenharmony_ci VOP2_AFBC_FMT_YUV420 = 9, 10062306a36Sopenharmony_ci VOP2_AFBC_FMT_YUV422 = 0xb, 10162306a36Sopenharmony_ci VOP2_AFBC_FMT_YUV422_10BIT = 0xe, 10262306a36Sopenharmony_ci VOP2_AFBC_FMT_INVALID = -1, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciunion vop2_alpha_ctrl { 10662306a36Sopenharmony_ci u32 val; 10762306a36Sopenharmony_ci struct { 10862306a36Sopenharmony_ci /* [0:1] */ 10962306a36Sopenharmony_ci u32 color_mode:1; 11062306a36Sopenharmony_ci u32 alpha_mode:1; 11162306a36Sopenharmony_ci /* [2:3] */ 11262306a36Sopenharmony_ci u32 blend_mode:2; 11362306a36Sopenharmony_ci u32 alpha_cal_mode:1; 11462306a36Sopenharmony_ci /* [5:7] */ 11562306a36Sopenharmony_ci u32 factor_mode:3; 11662306a36Sopenharmony_ci /* [8:9] */ 11762306a36Sopenharmony_ci u32 alpha_en:1; 11862306a36Sopenharmony_ci u32 src_dst_swap:1; 11962306a36Sopenharmony_ci u32 reserved:6; 12062306a36Sopenharmony_ci /* [16:23] */ 12162306a36Sopenharmony_ci u32 glb_alpha:8; 12262306a36Sopenharmony_ci } bits; 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct vop2_alpha { 12662306a36Sopenharmony_ci union vop2_alpha_ctrl src_color_ctrl; 12762306a36Sopenharmony_ci union vop2_alpha_ctrl dst_color_ctrl; 12862306a36Sopenharmony_ci union vop2_alpha_ctrl src_alpha_ctrl; 12962306a36Sopenharmony_ci union vop2_alpha_ctrl dst_alpha_ctrl; 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct vop2_alpha_config { 13362306a36Sopenharmony_ci bool src_premulti_en; 13462306a36Sopenharmony_ci bool dst_premulti_en; 13562306a36Sopenharmony_ci bool src_pixel_alpha_en; 13662306a36Sopenharmony_ci bool dst_pixel_alpha_en; 13762306a36Sopenharmony_ci u16 src_glb_alpha_value; 13862306a36Sopenharmony_ci u16 dst_glb_alpha_value; 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistruct vop2_win { 14262306a36Sopenharmony_ci struct vop2 *vop2; 14362306a36Sopenharmony_ci struct drm_plane base; 14462306a36Sopenharmony_ci const struct vop2_win_data *data; 14562306a36Sopenharmony_ci struct regmap_field *reg[VOP2_WIN_MAX_REG]; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /** 14862306a36Sopenharmony_ci * @win_id: graphic window id, a cluster may be split into two 14962306a36Sopenharmony_ci * graphics windows. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci u8 win_id; 15262306a36Sopenharmony_ci u8 delay; 15362306a36Sopenharmony_ci u32 offset; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci enum drm_plane_type type; 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistruct vop2_video_port { 15962306a36Sopenharmony_ci struct drm_crtc crtc; 16062306a36Sopenharmony_ci struct vop2 *vop2; 16162306a36Sopenharmony_ci struct clk *dclk; 16262306a36Sopenharmony_ci unsigned int id; 16362306a36Sopenharmony_ci const struct vop2_video_port_regs *regs; 16462306a36Sopenharmony_ci const struct vop2_video_port_data *data; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci struct completion dsp_hold_completion; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /** 16962306a36Sopenharmony_ci * @win_mask: Bitmask of windows attached to the video port; 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci u32 win_mask; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci struct vop2_win *primary_plane; 17462306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci unsigned int nlayers; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistruct vop2 { 18062306a36Sopenharmony_ci struct device *dev; 18162306a36Sopenharmony_ci struct drm_device *drm; 18262306a36Sopenharmony_ci struct vop2_video_port vps[ROCKCHIP_MAX_CRTC]; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci const struct vop2_data *data; 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * Number of windows that are registered as plane, may be less than the 18762306a36Sopenharmony_ci * total number of hardware windows. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci u32 registered_num_wins; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci void __iomem *regs; 19262306a36Sopenharmony_ci struct regmap *map; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci struct regmap *grf; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* physical map length of vop2 register */ 19762306a36Sopenharmony_ci u32 len; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci void __iomem *lut_regs; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* protects crtc enable/disable */ 20262306a36Sopenharmony_ci struct mutex vop2_lock; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci int irq; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Some global resources are shared between all video ports(crtcs), so 20862306a36Sopenharmony_ci * we need a ref counter here. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci unsigned int enable_count; 21162306a36Sopenharmony_ci struct clk *hclk; 21262306a36Sopenharmony_ci struct clk *aclk; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* optional internal rgb encoder */ 21562306a36Sopenharmony_ci struct rockchip_rgb *rgb; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* must be put at the end of the struct */ 21862306a36Sopenharmony_ci struct vop2_win win[]; 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci return container_of(crtc, struct vop2_video_port, crtc); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic struct vop2_win *to_vop2_win(struct drm_plane *p) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci return container_of(p, struct vop2_win, base); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void vop2_lock(struct vop2 *vop2) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci mutex_lock(&vop2->vop2_lock); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void vop2_unlock(struct vop2 *vop2) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci mutex_unlock(&vop2->vop2_lock); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void vop2_writel(struct vop2 *vop2, u32 offset, u32 v) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci regmap_write(vop2->map, offset, v); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci regmap_write(vp->vop2->map, vp->data->offset + offset, v); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic u32 vop2_readl(struct vop2 *vop2, u32 offset) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci u32 val; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci regmap_read(vop2->map, offset, &val); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return val; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci regmap_field_write(win->reg[reg], v); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic bool vop2_cluster_window(const struct vop2_win *win) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci return win->data->feature & WIN_FEATURE_CLUSTER; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void vop2_cfg_done(struct vop2_video_port *vp) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, 27562306a36Sopenharmony_ci BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void vop2_win_disable(struct vop2_win *win) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_ENABLE, 0); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (vop2_cluster_window(win)) 28362306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 0); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic enum vop2_data_format vop2_convert_format(u32 format) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci switch (format) { 28962306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 29062306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 29162306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 29262306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 29362306a36Sopenharmony_ci return VOP2_FMT_ARGB8888; 29462306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 29562306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 29662306a36Sopenharmony_ci return VOP2_FMT_RGB888; 29762306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 29862306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 29962306a36Sopenharmony_ci return VOP2_FMT_RGB565; 30062306a36Sopenharmony_ci case DRM_FORMAT_NV12: 30162306a36Sopenharmony_ci return VOP2_FMT_YUV420SP; 30262306a36Sopenharmony_ci case DRM_FORMAT_NV16: 30362306a36Sopenharmony_ci return VOP2_FMT_YUV422SP; 30462306a36Sopenharmony_ci case DRM_FORMAT_NV24: 30562306a36Sopenharmony_ci return VOP2_FMT_YUV444SP; 30662306a36Sopenharmony_ci case DRM_FORMAT_YUYV: 30762306a36Sopenharmony_ci case DRM_FORMAT_YVYU: 30862306a36Sopenharmony_ci return VOP2_FMT_VYUY422; 30962306a36Sopenharmony_ci case DRM_FORMAT_VYUY: 31062306a36Sopenharmony_ci case DRM_FORMAT_UYVY: 31162306a36Sopenharmony_ci return VOP2_FMT_YUYV422; 31262306a36Sopenharmony_ci default: 31362306a36Sopenharmony_ci DRM_ERROR("unsupported format[%08x]\n", format); 31462306a36Sopenharmony_ci return -EINVAL; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic enum vop2_afbc_format vop2_convert_afbc_format(u32 format) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci switch (format) { 32162306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 32262306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 32362306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 32462306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 32562306a36Sopenharmony_ci return VOP2_AFBC_FMT_ARGB8888; 32662306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 32762306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 32862306a36Sopenharmony_ci return VOP2_AFBC_FMT_RGB888; 32962306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 33062306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 33162306a36Sopenharmony_ci return VOP2_AFBC_FMT_RGB565; 33262306a36Sopenharmony_ci case DRM_FORMAT_NV12: 33362306a36Sopenharmony_ci return VOP2_AFBC_FMT_YUV420; 33462306a36Sopenharmony_ci case DRM_FORMAT_NV16: 33562306a36Sopenharmony_ci return VOP2_AFBC_FMT_YUV422; 33662306a36Sopenharmony_ci default: 33762306a36Sopenharmony_ci return VOP2_AFBC_FMT_INVALID; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return VOP2_AFBC_FMT_INVALID; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic bool vop2_win_rb_swap(u32 format) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci switch (format) { 34662306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 34762306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 34862306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 34962306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 35062306a36Sopenharmony_ci return true; 35162306a36Sopenharmony_ci default: 35262306a36Sopenharmony_ci return false; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic bool vop2_afbc_rb_swap(u32 format) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci switch (format) { 35962306a36Sopenharmony_ci case DRM_FORMAT_NV24: 36062306a36Sopenharmony_ci return true; 36162306a36Sopenharmony_ci default: 36262306a36Sopenharmony_ci return false; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic bool vop2_afbc_uv_swap(u32 format) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci switch (format) { 36962306a36Sopenharmony_ci case DRM_FORMAT_NV12: 37062306a36Sopenharmony_ci case DRM_FORMAT_NV16: 37162306a36Sopenharmony_ci return true; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci return false; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic bool vop2_win_uv_swap(u32 format) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci switch (format) { 38062306a36Sopenharmony_ci case DRM_FORMAT_NV12: 38162306a36Sopenharmony_ci case DRM_FORMAT_NV16: 38262306a36Sopenharmony_ci case DRM_FORMAT_NV24: 38362306a36Sopenharmony_ci return true; 38462306a36Sopenharmony_ci default: 38562306a36Sopenharmony_ci return false; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic bool vop2_win_dither_up(u32 format) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci switch (format) { 39262306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 39362306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 39462306a36Sopenharmony_ci return true; 39562306a36Sopenharmony_ci default: 39662306a36Sopenharmony_ci return false; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic bool vop2_output_uv_swap(u32 bus_format, u32 output_mode) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * FIXME: 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * There is no media type for YUV444 output, 40662306a36Sopenharmony_ci * so when out_mode is AAAA or P888, assume output is YUV444 on 40762306a36Sopenharmony_ci * yuv format. 40862306a36Sopenharmony_ci * 40962306a36Sopenharmony_ci * From H/W testing, YUV444 mode need a rb swap. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 || 41262306a36Sopenharmony_ci bus_format == MEDIA_BUS_FMT_VYUY8_1X16 || 41362306a36Sopenharmony_ci bus_format == MEDIA_BUS_FMT_YVYU8_2X8 || 41462306a36Sopenharmony_ci bus_format == MEDIA_BUS_FMT_VYUY8_2X8 || 41562306a36Sopenharmony_ci ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 || 41662306a36Sopenharmony_ci bus_format == MEDIA_BUS_FMT_YUV10_1X30) && 41762306a36Sopenharmony_ci (output_mode == ROCKCHIP_OUT_MODE_AAAA || 41862306a36Sopenharmony_ci output_mode == ROCKCHIP_OUT_MODE_P888))) 41962306a36Sopenharmony_ci return true; 42062306a36Sopenharmony_ci else 42162306a36Sopenharmony_ci return false; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic bool is_yuv_output(u32 bus_format) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci switch (bus_format) { 42762306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUV8_1X24: 42862306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUV10_1X30: 42962306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 43062306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 43162306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 43262306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 43362306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 43462306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 43562306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_1X16: 43662306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_1X16: 43762306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 43862306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_1X16: 43962306a36Sopenharmony_ci return true; 44062306a36Sopenharmony_ci default: 44162306a36Sopenharmony_ci return false; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic bool rockchip_afbc(struct drm_plane *plane, u64 modifier) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci int i; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_LINEAR) 45062306a36Sopenharmony_ci return false; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci for (i = 0 ; i < plane->modifier_count; i++) 45362306a36Sopenharmony_ci if (plane->modifiers[i] == modifier) 45462306a36Sopenharmony_ci return true; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return false; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format, 46062306a36Sopenharmony_ci u64 modifier) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 46362306a36Sopenharmony_ci struct vop2 *vop2 = win->vop2; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_INVALID) 46662306a36Sopenharmony_ci return false; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_LINEAR) 46962306a36Sopenharmony_ci return true; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (!rockchip_afbc(plane, modifier)) { 47262306a36Sopenharmony_ci drm_err(vop2->drm, "Unsupported format modifier 0x%llx\n", 47362306a36Sopenharmony_ci modifier); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return false; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return vop2_convert_afbc_format(format) >= 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic u32 vop2_afbc_transform_offset(struct drm_plane_state *pstate, 48262306a36Sopenharmony_ci bool afbc_half_block_en) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct drm_rect *src = &pstate->src; 48562306a36Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 48662306a36Sopenharmony_ci u32 bpp = fb->format->cpp[0] * 8; 48762306a36Sopenharmony_ci u32 vir_width = (fb->pitches[0] << 3) / bpp; 48862306a36Sopenharmony_ci u32 width = drm_rect_width(src) >> 16; 48962306a36Sopenharmony_ci u32 height = drm_rect_height(src) >> 16; 49062306a36Sopenharmony_ci u32 act_xoffset = src->x1 >> 16; 49162306a36Sopenharmony_ci u32 act_yoffset = src->y1 >> 16; 49262306a36Sopenharmony_ci u32 align16_crop = 0; 49362306a36Sopenharmony_ci u32 align64_crop = 0; 49462306a36Sopenharmony_ci u32 height_tmp; 49562306a36Sopenharmony_ci u8 tx, ty; 49662306a36Sopenharmony_ci u8 bottom_crop_line_num = 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* 16 pixel align */ 49962306a36Sopenharmony_ci if (height & 0xf) 50062306a36Sopenharmony_ci align16_crop = 16 - (height & 0xf); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci height_tmp = height + align16_crop; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* 64 pixel align */ 50562306a36Sopenharmony_ci if (height_tmp & 0x3f) 50662306a36Sopenharmony_ci align64_crop = 64 - (height_tmp & 0x3f); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci bottom_crop_line_num = align16_crop + align64_crop; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci switch (pstate->rotation & 51162306a36Sopenharmony_ci (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y | 51262306a36Sopenharmony_ci DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) { 51362306a36Sopenharmony_ci case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y: 51462306a36Sopenharmony_ci tx = 16 - ((act_xoffset + width) & 0xf); 51562306a36Sopenharmony_ci ty = bottom_crop_line_num - act_yoffset; 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90: 51862306a36Sopenharmony_ci tx = bottom_crop_line_num - act_yoffset; 51962306a36Sopenharmony_ci ty = vir_width - width - act_xoffset; 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_270: 52262306a36Sopenharmony_ci tx = act_yoffset; 52362306a36Sopenharmony_ci ty = act_xoffset; 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci case DRM_MODE_REFLECT_X: 52662306a36Sopenharmony_ci tx = 16 - ((act_xoffset + width) & 0xf); 52762306a36Sopenharmony_ci ty = act_yoffset; 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci case DRM_MODE_REFLECT_Y: 53062306a36Sopenharmony_ci tx = act_xoffset; 53162306a36Sopenharmony_ci ty = bottom_crop_line_num - act_yoffset; 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci case DRM_MODE_ROTATE_90: 53462306a36Sopenharmony_ci tx = bottom_crop_line_num - act_yoffset; 53562306a36Sopenharmony_ci ty = act_xoffset; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci case DRM_MODE_ROTATE_270: 53862306a36Sopenharmony_ci tx = act_yoffset; 53962306a36Sopenharmony_ci ty = vir_width - width - act_xoffset; 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci case 0: 54262306a36Sopenharmony_ci tx = act_xoffset; 54362306a36Sopenharmony_ci ty = act_yoffset; 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (afbc_half_block_en) 54862306a36Sopenharmony_ci ty &= 0x7f; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci#define TRANSFORM_XOFFSET GENMASK(7, 0) 55162306a36Sopenharmony_ci#define TRANSFORM_YOFFSET GENMASK(23, 16) 55262306a36Sopenharmony_ci return FIELD_PREP(TRANSFORM_XOFFSET, tx) | 55362306a36Sopenharmony_ci FIELD_PREP(TRANSFORM_YOFFSET, ty); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* 55762306a36Sopenharmony_ci * A Cluster window has 2048 x 16 line buffer, which can 55862306a36Sopenharmony_ci * works at 2048 x 16(Full) or 4096 x 8 (Half) mode. 55962306a36Sopenharmony_ci * for Cluster_lb_mode register: 56062306a36Sopenharmony_ci * 0: half mode, for plane input width range 2048 ~ 4096 56162306a36Sopenharmony_ci * 1: half mode, for cluster work at 2 * 2048 plane mode 56262306a36Sopenharmony_ci * 2: half mode, for rotate_90/270 mode 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic int vop2_get_cluster_lb_mode(struct vop2_win *win, 56662306a36Sopenharmony_ci struct drm_plane_state *pstate) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci if ((pstate->rotation & DRM_MODE_ROTATE_270) || 56962306a36Sopenharmony_ci (pstate->rotation & DRM_MODE_ROTATE_90)) 57062306a36Sopenharmony_ci return 2; 57162306a36Sopenharmony_ci else 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic u16 vop2_scale_factor(u32 src, u32 dst) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci u32 fac; 57862306a36Sopenharmony_ci int shift; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (src == dst) 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (dst < 2) 58462306a36Sopenharmony_ci return U16_MAX; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (src < 2) 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (src > dst) 59062306a36Sopenharmony_ci shift = 12; 59162306a36Sopenharmony_ci else 59262306a36Sopenharmony_ci shift = 16; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci src--; 59562306a36Sopenharmony_ci dst--; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci fac = DIV_ROUND_UP(src << shift, dst) - 1; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (fac > U16_MAX) 60062306a36Sopenharmony_ci return U16_MAX; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return fac; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic void vop2_setup_scale(struct vop2 *vop2, const struct vop2_win *win, 60662306a36Sopenharmony_ci u32 src_w, u32 src_h, u32 dst_w, 60762306a36Sopenharmony_ci u32 dst_h, u32 pixel_format) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci const struct drm_format_info *info; 61062306a36Sopenharmony_ci u16 hor_scl_mode, ver_scl_mode; 61162306a36Sopenharmony_ci u16 hscl_filter_mode, vscl_filter_mode; 61262306a36Sopenharmony_ci u8 gt2 = 0; 61362306a36Sopenharmony_ci u8 gt4 = 0; 61462306a36Sopenharmony_ci u32 val; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci info = drm_format_info(pixel_format); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (src_h >= (4 * dst_h)) { 61962306a36Sopenharmony_ci gt4 = 1; 62062306a36Sopenharmony_ci src_h >>= 2; 62162306a36Sopenharmony_ci } else if (src_h >= (2 * dst_h)) { 62262306a36Sopenharmony_ci gt2 = 1; 62362306a36Sopenharmony_ci src_h >>= 1; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci hor_scl_mode = scl_get_scl_mode(src_w, dst_w); 62762306a36Sopenharmony_ci ver_scl_mode = scl_get_scl_mode(src_h, dst_h); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (hor_scl_mode == SCALE_UP) 63062306a36Sopenharmony_ci hscl_filter_mode = VOP2_SCALE_UP_BIC; 63162306a36Sopenharmony_ci else 63262306a36Sopenharmony_ci hscl_filter_mode = VOP2_SCALE_DOWN_BIL; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (ver_scl_mode == SCALE_UP) 63562306a36Sopenharmony_ci vscl_filter_mode = VOP2_SCALE_UP_BIL; 63662306a36Sopenharmony_ci else 63762306a36Sopenharmony_ci vscl_filter_mode = VOP2_SCALE_DOWN_BIL; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* 64062306a36Sopenharmony_ci * RK3568 VOP Esmart/Smart dsp_w should be even pixel 64162306a36Sopenharmony_ci * at scale down mode 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci if (!(win->data->feature & WIN_FEATURE_AFBDC)) { 64462306a36Sopenharmony_ci if ((hor_scl_mode == SCALE_DOWN) && (dst_w & 0x1)) { 64562306a36Sopenharmony_ci drm_dbg(vop2->drm, "%s dst_w[%d] should align as 2 pixel\n", 64662306a36Sopenharmony_ci win->data->name, dst_w); 64762306a36Sopenharmony_ci dst_w++; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci val = vop2_scale_factor(src_w, dst_w); 65262306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_SCALE_YRGB_X, val); 65362306a36Sopenharmony_ci val = vop2_scale_factor(src_h, dst_h); 65462306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_SCALE_YRGB_Y, val); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT4, gt4); 65762306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_VSD_YRGB_GT2, gt2); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YRGB_HOR_SCL_MODE, hor_scl_mode); 66062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YRGB_VER_SCL_MODE, ver_scl_mode); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (vop2_cluster_window(win)) 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YRGB_HSCL_FILTER_MODE, hscl_filter_mode); 66662306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YRGB_VSCL_FILTER_MODE, vscl_filter_mode); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (info->is_yuv) { 66962306a36Sopenharmony_ci src_w /= info->hsub; 67062306a36Sopenharmony_ci src_h /= info->vsub; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci gt4 = 0; 67362306a36Sopenharmony_ci gt2 = 0; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (src_h >= (4 * dst_h)) { 67662306a36Sopenharmony_ci gt4 = 1; 67762306a36Sopenharmony_ci src_h >>= 2; 67862306a36Sopenharmony_ci } else if (src_h >= (2 * dst_h)) { 67962306a36Sopenharmony_ci gt2 = 1; 68062306a36Sopenharmony_ci src_h >>= 1; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci hor_scl_mode = scl_get_scl_mode(src_w, dst_w); 68462306a36Sopenharmony_ci ver_scl_mode = scl_get_scl_mode(src_h, dst_h); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci val = vop2_scale_factor(src_w, dst_w); 68762306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_SCALE_CBCR_X, val); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci val = vop2_scale_factor(src_h, dst_h); 69062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_SCALE_CBCR_Y, val); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT4, gt4); 69362306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_VSD_CBCR_GT2, gt2); 69462306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CBCR_HOR_SCL_MODE, hor_scl_mode); 69562306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CBCR_VER_SCL_MODE, ver_scl_mode); 69662306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CBCR_HSCL_FILTER_MODE, hscl_filter_mode); 69762306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CBCR_VSCL_FILTER_MODE, vscl_filter_mode); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int vop2_convert_csc_mode(int csc_mode) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci switch (csc_mode) { 70462306a36Sopenharmony_ci case V4L2_COLORSPACE_SMPTE170M: 70562306a36Sopenharmony_ci case V4L2_COLORSPACE_470_SYSTEM_M: 70662306a36Sopenharmony_ci case V4L2_COLORSPACE_470_SYSTEM_BG: 70762306a36Sopenharmony_ci return CSC_BT601L; 70862306a36Sopenharmony_ci case V4L2_COLORSPACE_REC709: 70962306a36Sopenharmony_ci case V4L2_COLORSPACE_SMPTE240M: 71062306a36Sopenharmony_ci case V4L2_COLORSPACE_DEFAULT: 71162306a36Sopenharmony_ci return CSC_BT709L; 71262306a36Sopenharmony_ci case V4L2_COLORSPACE_JPEG: 71362306a36Sopenharmony_ci return CSC_BT601F; 71462306a36Sopenharmony_ci case V4L2_COLORSPACE_BT2020: 71562306a36Sopenharmony_ci return CSC_BT2020; 71662306a36Sopenharmony_ci default: 71762306a36Sopenharmony_ci return CSC_BT709L; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/* 72262306a36Sopenharmony_ci * colorspace path: 72362306a36Sopenharmony_ci * Input Win csc Output 72462306a36Sopenharmony_ci * 1. YUV(2020) --> Y2R->2020To709->R2Y --> YUV_OUTPUT(601/709) 72562306a36Sopenharmony_ci * RGB --> R2Y __/ 72662306a36Sopenharmony_ci * 72762306a36Sopenharmony_ci * 2. YUV(2020) --> bypasss --> YUV_OUTPUT(2020) 72862306a36Sopenharmony_ci * RGB --> 709To2020->R2Y __/ 72962306a36Sopenharmony_ci * 73062306a36Sopenharmony_ci * 3. YUV(2020) --> Y2R->2020To709 --> RGB_OUTPUT(709) 73162306a36Sopenharmony_ci * RGB --> R2Y __/ 73262306a36Sopenharmony_ci * 73362306a36Sopenharmony_ci * 4. YUV(601/709)-> Y2R->709To2020->R2Y --> YUV_OUTPUT(2020) 73462306a36Sopenharmony_ci * RGB --> 709To2020->R2Y __/ 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * 5. YUV(601/709)-> bypass --> YUV_OUTPUT(709) 73762306a36Sopenharmony_ci * RGB --> R2Y __/ 73862306a36Sopenharmony_ci * 73962306a36Sopenharmony_ci * 6. YUV(601/709)-> bypass --> YUV_OUTPUT(601) 74062306a36Sopenharmony_ci * RGB --> R2Y(601) __/ 74162306a36Sopenharmony_ci * 74262306a36Sopenharmony_ci * 7. YUV --> Y2R(709) --> RGB_OUTPUT(709) 74362306a36Sopenharmony_ci * RGB --> bypass __/ 74462306a36Sopenharmony_ci * 74562306a36Sopenharmony_ci * 8. RGB --> 709To2020->R2Y --> YUV_OUTPUT(2020) 74662306a36Sopenharmony_ci * 74762306a36Sopenharmony_ci * 9. RGB --> R2Y(709) --> YUV_OUTPUT(709) 74862306a36Sopenharmony_ci * 74962306a36Sopenharmony_ci * 10. RGB --> R2Y(601) --> YUV_OUTPUT(601) 75062306a36Sopenharmony_ci * 75162306a36Sopenharmony_ci * 11. RGB --> bypass --> RGB_OUTPUT(709) 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void vop2_setup_csc_mode(struct vop2_video_port *vp, 75562306a36Sopenharmony_ci struct vop2_win *win, 75662306a36Sopenharmony_ci struct drm_plane_state *pstate) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(vp->crtc.state); 75962306a36Sopenharmony_ci int is_input_yuv = pstate->fb->format->is_yuv; 76062306a36Sopenharmony_ci int is_output_yuv = is_yuv_output(vcstate->bus_format); 76162306a36Sopenharmony_ci int input_csc = V4L2_COLORSPACE_DEFAULT; 76262306a36Sopenharmony_ci int output_csc = vcstate->color_space; 76362306a36Sopenharmony_ci bool r2y_en, y2r_en; 76462306a36Sopenharmony_ci int csc_mode; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (is_input_yuv && !is_output_yuv) { 76762306a36Sopenharmony_ci y2r_en = true; 76862306a36Sopenharmony_ci r2y_en = false; 76962306a36Sopenharmony_ci csc_mode = vop2_convert_csc_mode(input_csc); 77062306a36Sopenharmony_ci } else if (!is_input_yuv && is_output_yuv) { 77162306a36Sopenharmony_ci y2r_en = false; 77262306a36Sopenharmony_ci r2y_en = true; 77362306a36Sopenharmony_ci csc_mode = vop2_convert_csc_mode(output_csc); 77462306a36Sopenharmony_ci } else { 77562306a36Sopenharmony_ci y2r_en = false; 77662306a36Sopenharmony_ci r2y_en = false; 77762306a36Sopenharmony_ci csc_mode = false; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_Y2R_EN, y2r_en); 78162306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_R2Y_EN, r2y_en); 78262306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irq << 16 | irq); 79062306a36Sopenharmony_ci vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16 | irq); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void vop2_crtc_disable_irq(struct vop2_video_port *vp, u32 irq) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci vop2_writel(vop2, RK3568_VP_INT_EN(vp->id), irq << 16); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int vop2_core_clks_prepare_enable(struct vop2 *vop2) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci int ret; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci ret = clk_prepare_enable(vop2->hclk); 80562306a36Sopenharmony_ci if (ret < 0) { 80662306a36Sopenharmony_ci drm_err(vop2->drm, "failed to enable hclk - %d\n", ret); 80762306a36Sopenharmony_ci return ret; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci ret = clk_prepare_enable(vop2->aclk); 81162306a36Sopenharmony_ci if (ret < 0) { 81262306a36Sopenharmony_ci drm_err(vop2->drm, "failed to enable aclk - %d\n", ret); 81362306a36Sopenharmony_ci goto err; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return 0; 81762306a36Sopenharmony_cierr: 81862306a36Sopenharmony_ci clk_disable_unprepare(vop2->hclk); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return ret; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void vop2_enable(struct vop2 *vop2) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci int ret; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(vop2->dev); 82862306a36Sopenharmony_ci if (ret < 0) { 82962306a36Sopenharmony_ci drm_err(vop2->drm, "failed to get pm runtime: %d\n", ret); 83062306a36Sopenharmony_ci return; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ret = vop2_core_clks_prepare_enable(vop2); 83462306a36Sopenharmony_ci if (ret) { 83562306a36Sopenharmony_ci pm_runtime_put_sync(vop2->dev); 83662306a36Sopenharmony_ci return; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci ret = rockchip_drm_dma_attach_device(vop2->drm, vop2->dev); 84062306a36Sopenharmony_ci if (ret) { 84162306a36Sopenharmony_ci drm_err(vop2->drm, "failed to attach dma mapping, %d\n", ret); 84262306a36Sopenharmony_ci return; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci regcache_sync(vop2->map); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (vop2->data->soc_id == 3566) 84862306a36Sopenharmony_ci vop2_writel(vop2, RK3568_OTP_WIN_EN, 1); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* 85362306a36Sopenharmony_ci * Disable auto gating, this is a workaround to 85462306a36Sopenharmony_ci * avoid display image shift when a window enabled. 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci regmap_clear_bits(vop2->map, RK3568_SYS_AUTO_GATING_CTRL, 85762306a36Sopenharmony_ci RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci vop2_writel(vop2, RK3568_SYS0_INT_CLR, 86062306a36Sopenharmony_ci VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); 86162306a36Sopenharmony_ci vop2_writel(vop2, RK3568_SYS0_INT_EN, 86262306a36Sopenharmony_ci VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); 86362306a36Sopenharmony_ci vop2_writel(vop2, RK3568_SYS1_INT_CLR, 86462306a36Sopenharmony_ci VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); 86562306a36Sopenharmony_ci vop2_writel(vop2, RK3568_SYS1_INT_EN, 86662306a36Sopenharmony_ci VOP2_INT_BUS_ERRPR << 16 | VOP2_INT_BUS_ERRPR); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic void vop2_disable(struct vop2 *vop2) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci rockchip_drm_dma_detach_device(vop2->drm, vop2->dev); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci pm_runtime_put_sync(vop2->dev); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci regcache_mark_dirty(vop2->map); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci clk_disable_unprepare(vop2->aclk); 87862306a36Sopenharmony_ci clk_disable_unprepare(vop2->hclk); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic void vop2_crtc_atomic_disable(struct drm_crtc *crtc, 88262306a36Sopenharmony_ci struct drm_atomic_state *state) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 88562306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 88662306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state; 88762306a36Sopenharmony_ci int ret; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci vop2_lock(vop2); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); 89262306a36Sopenharmony_ci drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* 89762306a36Sopenharmony_ci * Vop standby will take effect at end of current frame, 89862306a36Sopenharmony_ci * if dsp hold valid irq happen, it means standby complete. 89962306a36Sopenharmony_ci * 90062306a36Sopenharmony_ci * we must wait standby complete when we want to disable aclk, 90162306a36Sopenharmony_ci * if not, memory bus maybe dead. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_ci reinit_completion(&vp->dsp_hold_completion); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci ret = wait_for_completion_timeout(&vp->dsp_hold_completion, 91062306a36Sopenharmony_ci msecs_to_jiffies(50)); 91162306a36Sopenharmony_ci if (!ret) 91262306a36Sopenharmony_ci drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci clk_disable_unprepare(vp->dclk); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci vop2->enable_count--; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (!vop2->enable_count) 92162306a36Sopenharmony_ci vop2_disable(vop2); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci vop2_unlock(vop2); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (crtc->state->event && !crtc->state->active) { 92662306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 92762306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 92862306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci crtc->state->event = NULL; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int vop2_plane_atomic_check(struct drm_plane *plane, 93562306a36Sopenharmony_ci struct drm_atomic_state *astate) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(astate, plane); 93862306a36Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 93962306a36Sopenharmony_ci struct drm_crtc *crtc = pstate->crtc; 94062306a36Sopenharmony_ci struct drm_crtc_state *cstate; 94162306a36Sopenharmony_ci struct vop2_video_port *vp; 94262306a36Sopenharmony_ci struct vop2 *vop2; 94362306a36Sopenharmony_ci const struct vop2_data *vop2_data; 94462306a36Sopenharmony_ci struct drm_rect *dest = &pstate->dst; 94562306a36Sopenharmony_ci struct drm_rect *src = &pstate->src; 94662306a36Sopenharmony_ci int min_scale = FRAC_16_16(1, 8); 94762306a36Sopenharmony_ci int max_scale = FRAC_16_16(8, 1); 94862306a36Sopenharmony_ci int format; 94962306a36Sopenharmony_ci int ret; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (!crtc) 95262306a36Sopenharmony_ci return 0; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci vp = to_vop2_video_port(crtc); 95562306a36Sopenharmony_ci vop2 = vp->vop2; 95662306a36Sopenharmony_ci vop2_data = vop2->data; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc); 95962306a36Sopenharmony_ci if (WARN_ON(!cstate)) 96062306a36Sopenharmony_ci return -EINVAL; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(pstate, cstate, 96362306a36Sopenharmony_ci min_scale, max_scale, 96462306a36Sopenharmony_ci true, true); 96562306a36Sopenharmony_ci if (ret) 96662306a36Sopenharmony_ci return ret; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (!pstate->visible) 96962306a36Sopenharmony_ci return 0; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci format = vop2_convert_format(fb->format->format); 97262306a36Sopenharmony_ci if (format < 0) 97362306a36Sopenharmony_ci return format; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 || 97662306a36Sopenharmony_ci drm_rect_width(dest) < 4 || drm_rect_width(dest) < 4) { 97762306a36Sopenharmony_ci drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n", 97862306a36Sopenharmony_ci drm_rect_width(src) >> 16, drm_rect_height(src) >> 16, 97962306a36Sopenharmony_ci drm_rect_width(dest), drm_rect_height(dest)); 98062306a36Sopenharmony_ci pstate->visible = false; 98162306a36Sopenharmony_ci return 0; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (drm_rect_width(src) >> 16 > vop2_data->max_input.width || 98562306a36Sopenharmony_ci drm_rect_height(src) >> 16 > vop2_data->max_input.height) { 98662306a36Sopenharmony_ci drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n", 98762306a36Sopenharmony_ci drm_rect_width(src) >> 16, 98862306a36Sopenharmony_ci drm_rect_height(src) >> 16, 98962306a36Sopenharmony_ci vop2_data->max_input.width, 99062306a36Sopenharmony_ci vop2_data->max_input.height); 99162306a36Sopenharmony_ci return -EINVAL; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* 99562306a36Sopenharmony_ci * Src.x1 can be odd when do clip, but yuv plane start point 99662306a36Sopenharmony_ci * need align with 2 pixel. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ci if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) { 99962306a36Sopenharmony_ci drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n"); 100062306a36Sopenharmony_ci return -EINVAL; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void vop2_plane_atomic_disable(struct drm_plane *plane, 100762306a36Sopenharmony_ci struct drm_atomic_state *state) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct drm_plane_state *old_pstate = NULL; 101062306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 101162306a36Sopenharmony_ci struct vop2 *vop2 = win->vop2; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci drm_dbg(vop2->drm, "%s disable\n", win->data->name); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (state) 101662306a36Sopenharmony_ci old_pstate = drm_atomic_get_old_plane_state(state, plane); 101762306a36Sopenharmony_ci if (old_pstate && !old_pstate->crtc) 101862306a36Sopenharmony_ci return; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci vop2_win_disable(win); 102162306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YUV_CLIP, 0); 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci/* 102562306a36Sopenharmony_ci * The color key is 10 bit, so all format should 102662306a36Sopenharmony_ci * convert to 10 bit here. 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_cistatic void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct drm_plane_state *pstate = plane->state; 103162306a36Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 103262306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 103362306a36Sopenharmony_ci u32 color_key_en = 0; 103462306a36Sopenharmony_ci u32 r = 0; 103562306a36Sopenharmony_ci u32 g = 0; 103662306a36Sopenharmony_ci u32 b = 0; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (!(color_key & VOP2_COLOR_KEY_MASK) || fb->format->is_yuv) { 103962306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, 0); 104062306a36Sopenharmony_ci return; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci switch (fb->format->format) { 104462306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 104562306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 104662306a36Sopenharmony_ci r = (color_key & 0xf800) >> 11; 104762306a36Sopenharmony_ci g = (color_key & 0x7e0) >> 5; 104862306a36Sopenharmony_ci b = (color_key & 0x1f); 104962306a36Sopenharmony_ci r <<= 5; 105062306a36Sopenharmony_ci g <<= 4; 105162306a36Sopenharmony_ci b <<= 5; 105262306a36Sopenharmony_ci color_key_en = 1; 105362306a36Sopenharmony_ci break; 105462306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 105562306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 105662306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 105762306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 105862306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 105962306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 106062306a36Sopenharmony_ci r = (color_key & 0xff0000) >> 16; 106162306a36Sopenharmony_ci g = (color_key & 0xff00) >> 8; 106262306a36Sopenharmony_ci b = (color_key & 0xff); 106362306a36Sopenharmony_ci r <<= 2; 106462306a36Sopenharmony_ci g <<= 2; 106562306a36Sopenharmony_ci b <<= 2; 106662306a36Sopenharmony_ci color_key_en = 1; 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_COLOR_KEY_EN, color_key_en); 107162306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b); 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic void vop2_plane_atomic_update(struct drm_plane *plane, 107562306a36Sopenharmony_ci struct drm_atomic_state *state) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct drm_plane_state *pstate = plane->state; 107862306a36Sopenharmony_ci struct drm_crtc *crtc = pstate->crtc; 107962306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 108062306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 108162306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; 108262306a36Sopenharmony_ci struct vop2 *vop2 = win->vop2; 108362306a36Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 108462306a36Sopenharmony_ci u32 bpp = fb->format->cpp[0] * 8; 108562306a36Sopenharmony_ci u32 actual_w, actual_h, dsp_w, dsp_h; 108662306a36Sopenharmony_ci u32 act_info, dsp_info; 108762306a36Sopenharmony_ci u32 format; 108862306a36Sopenharmony_ci u32 afbc_format; 108962306a36Sopenharmony_ci u32 rb_swap; 109062306a36Sopenharmony_ci u32 uv_swap; 109162306a36Sopenharmony_ci struct drm_rect *src = &pstate->src; 109262306a36Sopenharmony_ci struct drm_rect *dest = &pstate->dst; 109362306a36Sopenharmony_ci u32 afbc_tile_num; 109462306a36Sopenharmony_ci u32 transform_offset; 109562306a36Sopenharmony_ci bool dither_up; 109662306a36Sopenharmony_ci bool xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false; 109762306a36Sopenharmony_ci bool ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false; 109862306a36Sopenharmony_ci bool rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270; 109962306a36Sopenharmony_ci bool rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90; 110062306a36Sopenharmony_ci struct rockchip_gem_object *rk_obj; 110162306a36Sopenharmony_ci unsigned long offset; 110262306a36Sopenharmony_ci bool afbc_en; 110362306a36Sopenharmony_ci dma_addr_t yrgb_mst; 110462306a36Sopenharmony_ci dma_addr_t uv_mst; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* 110762306a36Sopenharmony_ci * can't update plane when vop2 is disabled. 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ci if (WARN_ON(!crtc)) 111062306a36Sopenharmony_ci return; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (!pstate->visible) { 111362306a36Sopenharmony_ci vop2_plane_atomic_disable(plane, state); 111462306a36Sopenharmony_ci return; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci afbc_en = rockchip_afbc(plane, fb->modifier); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci offset = (src->x1 >> 16) * fb->format->cpp[0]; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* 112262306a36Sopenharmony_ci * AFBC HDR_PTR must set to the zero offset of the framebuffer. 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_ci if (afbc_en) 112562306a36Sopenharmony_ci offset = 0; 112662306a36Sopenharmony_ci else if (pstate->rotation & DRM_MODE_REFLECT_Y) 112762306a36Sopenharmony_ci offset += ((src->y2 >> 16) - 1) * fb->pitches[0]; 112862306a36Sopenharmony_ci else 112962306a36Sopenharmony_ci offset += (src->y1 >> 16) * fb->pitches[0]; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci rk_obj = to_rockchip_obj(fb->obj[0]); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0]; 113462306a36Sopenharmony_ci if (fb->format->is_yuv) { 113562306a36Sopenharmony_ci int hsub = fb->format->hsub; 113662306a36Sopenharmony_ci int vsub = fb->format->vsub; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub; 113962306a36Sopenharmony_ci offset += (src->y1 >> 16) * fb->pitches[1] / vsub; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if ((pstate->rotation & DRM_MODE_REFLECT_Y) && !afbc_en) 114262306a36Sopenharmony_ci offset += fb->pitches[1] * ((pstate->src_h >> 16) - 2) / vsub; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci rk_obj = to_rockchip_obj(fb->obj[0]); 114562306a36Sopenharmony_ci uv_mst = rk_obj->dma_addr + offset + fb->offsets[1]; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci actual_w = drm_rect_width(src) >> 16; 114962306a36Sopenharmony_ci actual_h = drm_rect_height(src) >> 16; 115062306a36Sopenharmony_ci dsp_w = drm_rect_width(dest); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (dest->x1 + dsp_w > adjusted_mode->hdisplay) { 115362306a36Sopenharmony_ci drm_err(vop2->drm, "vp%d %s dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n", 115462306a36Sopenharmony_ci vp->id, win->data->name, dest->x1, dsp_w, adjusted_mode->hdisplay); 115562306a36Sopenharmony_ci dsp_w = adjusted_mode->hdisplay - dest->x1; 115662306a36Sopenharmony_ci if (dsp_w < 4) 115762306a36Sopenharmony_ci dsp_w = 4; 115862306a36Sopenharmony_ci actual_w = dsp_w * actual_w / drm_rect_width(dest); 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci dsp_h = drm_rect_height(dest); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (dest->y1 + dsp_h > adjusted_mode->vdisplay) { 116462306a36Sopenharmony_ci drm_err(vop2->drm, "vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n", 116562306a36Sopenharmony_ci vp->id, win->data->name, dest->y1, dsp_h, adjusted_mode->vdisplay); 116662306a36Sopenharmony_ci dsp_h = adjusted_mode->vdisplay - dest->y1; 116762306a36Sopenharmony_ci if (dsp_h < 4) 116862306a36Sopenharmony_ci dsp_h = 4; 116962306a36Sopenharmony_ci actual_h = dsp_h * actual_h / drm_rect_height(dest); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* 117362306a36Sopenharmony_ci * This is workaround solution for IC design: 117462306a36Sopenharmony_ci * esmart can't support scale down when actual_w % 16 == 1. 117562306a36Sopenharmony_ci */ 117662306a36Sopenharmony_ci if (!(win->data->feature & WIN_FEATURE_AFBDC)) { 117762306a36Sopenharmony_ci if (actual_w > dsp_w && (actual_w & 0xf) == 1) { 117862306a36Sopenharmony_ci drm_err(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n", 117962306a36Sopenharmony_ci vp->id, win->data->name, actual_w); 118062306a36Sopenharmony_ci actual_w -= 1; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (afbc_en && actual_w % 4) { 118562306a36Sopenharmony_ci drm_err(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n", 118662306a36Sopenharmony_ci vp->id, win->data->name, actual_w); 118762306a36Sopenharmony_ci actual_w = ALIGN_DOWN(actual_w, 4); 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); 119162306a36Sopenharmony_ci dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci format = vop2_convert_format(fb->format->format); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n", 119662306a36Sopenharmony_ci vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h, 119762306a36Sopenharmony_ci dest->x1, dest->y1, 119862306a36Sopenharmony_ci &fb->format->format, 119962306a36Sopenharmony_ci afbc_en ? "AFBC" : "", &yrgb_mst); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (afbc_en) { 120262306a36Sopenharmony_ci u32 stride; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci /* the afbc superblock is 16 x 16 */ 120562306a36Sopenharmony_ci afbc_format = vop2_convert_afbc_format(fb->format->format); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* Enable color transform for YTR */ 120862306a36Sopenharmony_ci if (fb->modifier & AFBC_FORMAT_MOD_YTR) 120962306a36Sopenharmony_ci afbc_format |= (1 << 4); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci afbc_tile_num = ALIGN(actual_w, 16) >> 4; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* 121462306a36Sopenharmony_ci * AFBC pic_vir_width is count by pixel, this is different 121562306a36Sopenharmony_ci * with WIN_VIR_STRIDE. 121662306a36Sopenharmony_ci */ 121762306a36Sopenharmony_ci stride = (fb->pitches[0] << 3) / bpp; 121862306a36Sopenharmony_ci if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270)) 121962306a36Sopenharmony_ci drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n", 122062306a36Sopenharmony_ci vp->id, win->data->name, stride); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci rb_swap = vop2_afbc_rb_swap(fb->format->format); 122362306a36Sopenharmony_ci uv_swap = vop2_afbc_uv_swap(fb->format->format); 122462306a36Sopenharmony_ci /* 122562306a36Sopenharmony_ci * This is a workaround for crazy IC design, Cluster 122662306a36Sopenharmony_ci * and Esmart/Smart use different format configuration map: 122762306a36Sopenharmony_ci * YUV420_10BIT: 0x10 for Cluster, 0x14 for Esmart/Smart. 122862306a36Sopenharmony_ci * 122962306a36Sopenharmony_ci * This is one thing we can make the convert simple: 123062306a36Sopenharmony_ci * AFBCD decode all the YUV data to YUV444. So we just 123162306a36Sopenharmony_ci * set all the yuv 10 bit to YUV444_10. 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_ci if (fb->format->is_yuv && bpp == 10) 123462306a36Sopenharmony_ci format = VOP2_CLUSTER_YUV444_10; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci if (vop2_cluster_window(win)) 123762306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1); 123862306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format); 123962306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_RB_SWAP, rb_swap); 124062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap); 124162306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0); 124262306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0); 124362306a36Sopenharmony_ci if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) { 124462306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0); 124562306a36Sopenharmony_ci transform_offset = vop2_afbc_transform_offset(pstate, false); 124662306a36Sopenharmony_ci } else { 124762306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 1); 124862306a36Sopenharmony_ci transform_offset = vop2_afbc_transform_offset(pstate, true); 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_HDR_PTR, yrgb_mst); 125162306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_PIC_SIZE, act_info); 125262306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_TRANSFORM_OFFSET, transform_offset); 125362306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_PIC_OFFSET, ((src->x1 >> 16) | src->y1)); 125462306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_DSP_OFFSET, (dest->x1 | (dest->y1 << 16))); 125562306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_PIC_VIR_WIDTH, stride); 125662306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_TILE_NUM, afbc_tile_num); 125762306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_XMIRROR, xmirror); 125862306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_270, rotate_270); 125962306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_AFBC_ROTATE_90, rotate_90); 126062306a36Sopenharmony_ci } else { 126162306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YRGB_VIR, DIV_ROUND_UP(fb->pitches[0], 4)); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YMIRROR, ymirror); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (rotate_90 || rotate_270) { 126762306a36Sopenharmony_ci act_info = swahw32(act_info); 126862306a36Sopenharmony_ci actual_w = drm_rect_height(src) >> 16; 126962306a36Sopenharmony_ci actual_h = drm_rect_width(src) >> 16; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_FORMAT, format); 127362306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_YRGB_MST, yrgb_mst); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci rb_swap = vop2_win_rb_swap(fb->format->format); 127662306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_RB_SWAP, rb_swap); 127762306a36Sopenharmony_ci if (!vop2_cluster_window(win)) { 127862306a36Sopenharmony_ci uv_swap = vop2_win_uv_swap(fb->format->format); 127962306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_UV_SWAP, uv_swap); 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (fb->format->is_yuv) { 128362306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_UV_VIR, DIV_ROUND_UP(fb->pitches[1], 4)); 128462306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst); 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format); 128862306a36Sopenharmony_ci if (!vop2_cluster_window(win)) 128962306a36Sopenharmony_ci vop2_plane_setup_color_key(plane, 0); 129062306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info); 129162306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_DSP_INFO, dsp_info); 129262306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_DSP_ST, dest->y1 << 16 | (dest->x1 & 0xffff)); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci vop2_setup_csc_mode(vp, win, pstate); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci dither_up = vop2_win_dither_up(fb->format->format); 129762306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_DITHER_UP, dither_up); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_ENABLE, 1); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (vop2_cluster_window(win)) { 130262306a36Sopenharmony_ci int lb_mode = vop2_get_cluster_lb_mode(win, pstate); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CLUSTER_LB_MODE, lb_mode); 130562306a36Sopenharmony_ci vop2_win_write(win, VOP2_WIN_CLUSTER_ENABLE, 1); 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs vop2_plane_helper_funcs = { 131062306a36Sopenharmony_ci .atomic_check = vop2_plane_atomic_check, 131162306a36Sopenharmony_ci .atomic_update = vop2_plane_atomic_update, 131262306a36Sopenharmony_ci .atomic_disable = vop2_plane_atomic_disable, 131362306a36Sopenharmony_ci}; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic const struct drm_plane_funcs vop2_plane_funcs = { 131662306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 131762306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 131862306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 131962306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 132062306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 132162306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 132262306a36Sopenharmony_ci .format_mod_supported = rockchip_vop2_mod_supported, 132362306a36Sopenharmony_ci}; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic int vop2_crtc_enable_vblank(struct drm_crtc *crtc) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci vop2_crtc_enable_irq(vp, VP_INT_FS_FIELD); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci return 0; 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic void vop2_crtc_disable_vblank(struct drm_crtc *crtc) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci vop2_crtc_disable_irq(vp, VP_INT_FS_FIELD); 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic bool vop2_crtc_mode_fixup(struct drm_crtc *crtc, 134262306a36Sopenharmony_ci const struct drm_display_mode *mode, 134362306a36Sopenharmony_ci struct drm_display_mode *adj_mode) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V | 134662306a36Sopenharmony_ci CRTC_STEREO_DOUBLE); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci return true; 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci switch (vcstate->bus_format) { 135662306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_1X16: 135762306a36Sopenharmony_ci *dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN; 135862306a36Sopenharmony_ci break; 135962306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X18: 136062306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: 136162306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: 136262306a36Sopenharmony_ci *dsp_ctrl |= RK3568_VP_DSP_CTRL__DITHER_DOWN_EN; 136362306a36Sopenharmony_ci *dsp_ctrl |= RGB888_TO_RGB666; 136462306a36Sopenharmony_ci break; 136562306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUV8_1X24: 136662306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 136762306a36Sopenharmony_ci *dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN; 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci default: 137062306a36Sopenharmony_ci break; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (vcstate->output_mode != ROCKCHIP_OUT_MODE_AAAA) 137462306a36Sopenharmony_ci *dsp_ctrl |= RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci *dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL, 137762306a36Sopenharmony_ci DITHER_DOWN_ALLEGRO); 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic void vop2_post_config(struct drm_crtc *crtc) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 138362306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 138462306a36Sopenharmony_ci u16 vtotal = mode->crtc_vtotal; 138562306a36Sopenharmony_ci u16 hdisplay = mode->crtc_hdisplay; 138662306a36Sopenharmony_ci u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; 138762306a36Sopenharmony_ci u16 vdisplay = mode->crtc_vdisplay; 138862306a36Sopenharmony_ci u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start; 138962306a36Sopenharmony_ci u32 left_margin = 100, right_margin = 100; 139062306a36Sopenharmony_ci u32 top_margin = 100, bottom_margin = 100; 139162306a36Sopenharmony_ci u16 hsize = hdisplay * (left_margin + right_margin) / 200; 139262306a36Sopenharmony_ci u16 vsize = vdisplay * (top_margin + bottom_margin) / 200; 139362306a36Sopenharmony_ci u16 hact_end, vact_end; 139462306a36Sopenharmony_ci u32 val; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci vsize = rounddown(vsize, 2); 139762306a36Sopenharmony_ci hsize = rounddown(hsize, 2); 139862306a36Sopenharmony_ci hact_st += hdisplay * (100 - left_margin) / 200; 139962306a36Sopenharmony_ci hact_end = hact_st + hsize; 140062306a36Sopenharmony_ci val = hact_st << 16; 140162306a36Sopenharmony_ci val |= hact_end; 140262306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_POST_DSP_HACT_INFO, val); 140362306a36Sopenharmony_ci vact_st += vdisplay * (100 - top_margin) / 200; 140462306a36Sopenharmony_ci vact_end = vact_st + vsize; 140562306a36Sopenharmony_ci val = vact_st << 16; 140662306a36Sopenharmony_ci val |= vact_end; 140762306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO, val); 140862306a36Sopenharmony_ci val = scl_cal_scale2(vdisplay, vsize) << 16; 140962306a36Sopenharmony_ci val |= scl_cal_scale2(hdisplay, hsize); 141062306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_POST_SCL_FACTOR_YRGB, val); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci val = 0; 141362306a36Sopenharmony_ci if (hdisplay != hsize) 141462306a36Sopenharmony_ci val |= RK3568_VP_POST_SCL_CTRL__HSCALEDOWN; 141562306a36Sopenharmony_ci if (vdisplay != vsize) 141662306a36Sopenharmony_ci val |= RK3568_VP_POST_SCL_CTRL__VSCALEDOWN; 141762306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_POST_SCL_CTRL, val); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 142062306a36Sopenharmony_ci u16 vact_st_f1 = vtotal + vact_st + 1; 142162306a36Sopenharmony_ci u16 vact_end_f1 = vact_st_f1 + vsize; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci val = vact_st_f1 << 16 | vact_end_f1; 142462306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val); 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_BG, 0); 142862306a36Sopenharmony_ci} 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, 143162306a36Sopenharmony_ci u32 polflags) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 143462306a36Sopenharmony_ci u32 die, dip; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci die = vop2_readl(vop2, RK3568_DSP_IF_EN); 143762306a36Sopenharmony_ci dip = vop2_readl(vop2, RK3568_DSP_IF_POL); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci switch (id) { 144062306a36Sopenharmony_ci case ROCKCHIP_VOP2_EP_RGB0: 144162306a36Sopenharmony_ci die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX; 144262306a36Sopenharmony_ci die |= RK3568_SYS_DSP_INFACE_EN_RGB | 144362306a36Sopenharmony_ci FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id); 144462306a36Sopenharmony_ci dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; 144562306a36Sopenharmony_ci dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); 144662306a36Sopenharmony_ci if (polflags & POLFLAG_DCLK_INV) 144762306a36Sopenharmony_ci regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3)); 144862306a36Sopenharmony_ci else 144962306a36Sopenharmony_ci regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16)); 145062306a36Sopenharmony_ci break; 145162306a36Sopenharmony_ci case ROCKCHIP_VOP2_EP_HDMI0: 145262306a36Sopenharmony_ci die &= ~RK3568_SYS_DSP_INFACE_EN_HDMI_MUX; 145362306a36Sopenharmony_ci die |= RK3568_SYS_DSP_INFACE_EN_HDMI | 145462306a36Sopenharmony_ci FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id); 145562306a36Sopenharmony_ci dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL; 145662306a36Sopenharmony_ci dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags); 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci case ROCKCHIP_VOP2_EP_EDP0: 145962306a36Sopenharmony_ci die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX; 146062306a36Sopenharmony_ci die |= RK3568_SYS_DSP_INFACE_EN_EDP | 146162306a36Sopenharmony_ci FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_EDP_MUX, vp->id); 146262306a36Sopenharmony_ci dip &= ~RK3568_DSP_IF_POL__EDP_PIN_POL; 146362306a36Sopenharmony_ci dip |= FIELD_PREP(RK3568_DSP_IF_POL__EDP_PIN_POL, polflags); 146462306a36Sopenharmony_ci break; 146562306a36Sopenharmony_ci case ROCKCHIP_VOP2_EP_MIPI0: 146662306a36Sopenharmony_ci die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX; 146762306a36Sopenharmony_ci die |= RK3568_SYS_DSP_INFACE_EN_MIPI0 | 146862306a36Sopenharmony_ci FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX, vp->id); 146962306a36Sopenharmony_ci dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL; 147062306a36Sopenharmony_ci dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags); 147162306a36Sopenharmony_ci break; 147262306a36Sopenharmony_ci case ROCKCHIP_VOP2_EP_MIPI1: 147362306a36Sopenharmony_ci die &= ~RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX; 147462306a36Sopenharmony_ci die |= RK3568_SYS_DSP_INFACE_EN_MIPI1 | 147562306a36Sopenharmony_ci FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id); 147662306a36Sopenharmony_ci dip &= ~RK3568_DSP_IF_POL__MIPI_PIN_POL; 147762306a36Sopenharmony_ci dip |= FIELD_PREP(RK3568_DSP_IF_POL__MIPI_PIN_POL, polflags); 147862306a36Sopenharmony_ci break; 147962306a36Sopenharmony_ci case ROCKCHIP_VOP2_EP_LVDS0: 148062306a36Sopenharmony_ci die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX; 148162306a36Sopenharmony_ci die |= RK3568_SYS_DSP_INFACE_EN_LVDS0 | 148262306a36Sopenharmony_ci FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX, vp->id); 148362306a36Sopenharmony_ci dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; 148462306a36Sopenharmony_ci dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci case ROCKCHIP_VOP2_EP_LVDS1: 148762306a36Sopenharmony_ci die &= ~RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX; 148862306a36Sopenharmony_ci die |= RK3568_SYS_DSP_INFACE_EN_LVDS1 | 148962306a36Sopenharmony_ci FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX, vp->id); 149062306a36Sopenharmony_ci dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; 149162306a36Sopenharmony_ci dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); 149262306a36Sopenharmony_ci break; 149362306a36Sopenharmony_ci default: 149462306a36Sopenharmony_ci drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id); 149562306a36Sopenharmony_ci return; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci vop2_writel(vop2, RK3568_DSP_IF_EN, die); 150162306a36Sopenharmony_ci vop2_writel(vop2, RK3568_DSP_IF_POL, dip); 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_cistatic int us_to_vertical_line(struct drm_display_mode *mode, int us) 150562306a36Sopenharmony_ci{ 150662306a36Sopenharmony_ci return us * mode->clock / mode->htotal / 1000; 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cistatic void vop2_crtc_atomic_enable(struct drm_crtc *crtc, 151062306a36Sopenharmony_ci struct drm_atomic_state *state) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 151362306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 151462306a36Sopenharmony_ci const struct vop2_data *vop2_data = vop2->data; 151562306a36Sopenharmony_ci const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; 151662306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 151762306a36Sopenharmony_ci struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); 151862306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 151962306a36Sopenharmony_ci unsigned long clock = mode->crtc_clock * 1000; 152062306a36Sopenharmony_ci u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; 152162306a36Sopenharmony_ci u16 hdisplay = mode->crtc_hdisplay; 152262306a36Sopenharmony_ci u16 htotal = mode->crtc_htotal; 152362306a36Sopenharmony_ci u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; 152462306a36Sopenharmony_ci u16 hact_end = hact_st + hdisplay; 152562306a36Sopenharmony_ci u16 vdisplay = mode->crtc_vdisplay; 152662306a36Sopenharmony_ci u16 vtotal = mode->crtc_vtotal; 152762306a36Sopenharmony_ci u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; 152862306a36Sopenharmony_ci u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start; 152962306a36Sopenharmony_ci u16 vact_end = vact_st + vdisplay; 153062306a36Sopenharmony_ci u8 out_mode; 153162306a36Sopenharmony_ci u32 dsp_ctrl = 0; 153262306a36Sopenharmony_ci int act_end; 153362306a36Sopenharmony_ci u32 val, polflags; 153462306a36Sopenharmony_ci int ret; 153562306a36Sopenharmony_ci struct drm_encoder *encoder; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n", 153862306a36Sopenharmony_ci hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p", 153962306a36Sopenharmony_ci drm_mode_vrefresh(mode), vcstate->output_type, vp->id); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci vop2_lock(vop2); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci ret = clk_prepare_enable(vp->dclk); 154462306a36Sopenharmony_ci if (ret < 0) { 154562306a36Sopenharmony_ci drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n", 154662306a36Sopenharmony_ci vp->id, ret); 154762306a36Sopenharmony_ci vop2_unlock(vop2); 154862306a36Sopenharmony_ci return; 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci if (!vop2->enable_count) 155262306a36Sopenharmony_ci vop2_enable(vop2); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci vop2->enable_count++; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci vop2_crtc_enable_irq(vp, VP_INT_POST_BUF_EMPTY); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci polflags = 0; 155962306a36Sopenharmony_ci if (vcstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) 156062306a36Sopenharmony_ci polflags |= POLFLAG_DCLK_INV; 156162306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PHSYNC) 156262306a36Sopenharmony_ci polflags |= BIT(HSYNC_POSITIVE); 156362306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PVSYNC) 156462306a36Sopenharmony_ci polflags |= BIT(VSYNC_POSITIVE); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) { 156762306a36Sopenharmony_ci struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags); 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA && 157362306a36Sopenharmony_ci !(vp_data->feature & VOP_FEATURE_OUTPUT_10BIT)) 157462306a36Sopenharmony_ci out_mode = ROCKCHIP_OUT_MODE_P888; 157562306a36Sopenharmony_ci else 157662306a36Sopenharmony_ci out_mode = vcstate->output_mode; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci dsp_ctrl |= FIELD_PREP(RK3568_VP_DSP_CTRL__OUT_MODE, out_mode); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci if (vop2_output_uv_swap(vcstate->bus_format, vcstate->output_mode)) 158162306a36Sopenharmony_ci dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_RB_SWAP; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci if (is_yuv_output(vcstate->bus_format)) 158462306a36Sopenharmony_ci dsp_ctrl |= RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci vop2_dither_setup(crtc, &dsp_ctrl); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_HTOTAL_HS_END, (htotal << 16) | hsync_len); 158962306a36Sopenharmony_ci val = hact_st << 16; 159062306a36Sopenharmony_ci val |= hact_end; 159162306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_HACT_ST_END, val); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci val = vact_st << 16; 159462306a36Sopenharmony_ci val |= vact_end; 159562306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END, val); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 159862306a36Sopenharmony_ci u16 vact_st_f1 = vtotal + vact_st + 1; 159962306a36Sopenharmony_ci u16 vact_end_f1 = vact_st_f1 + vdisplay; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci val = vact_st_f1 << 16 | vact_end_f1; 160262306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_VACT_ST_END_F1, val); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci val = vtotal << 16 | (vtotal + vsync_len); 160562306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_VS_ST_END_F1, val); 160662306a36Sopenharmony_ci dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_INTERLACE; 160762306a36Sopenharmony_ci dsp_ctrl |= RK3568_VP_DSP_CTRL__DSP_FILED_POL; 160862306a36Sopenharmony_ci dsp_ctrl |= RK3568_VP_DSP_CTRL__P2I_EN; 160962306a36Sopenharmony_ci vtotal += vtotal + 1; 161062306a36Sopenharmony_ci act_end = vact_end_f1; 161162306a36Sopenharmony_ci } else { 161262306a36Sopenharmony_ci act_end = vact_end; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci vop2_writel(vop2, RK3568_VP_LINE_FLAG(vp->id), 161662306a36Sopenharmony_ci (act_end - us_to_vertical_line(mode, 0)) << 16 | act_end); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLCLK) { 162162306a36Sopenharmony_ci dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV; 162262306a36Sopenharmony_ci clock *= 2; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci clk_set_rate(vp->dclk, clock); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci vop2_post_config(crtc); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci vop2_cfg_done(vp); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci vop2_unlock(vop2); 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic int vop2_crtc_atomic_check(struct drm_crtc *crtc, 164162306a36Sopenharmony_ci struct drm_atomic_state *state) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 164462306a36Sopenharmony_ci struct drm_plane *plane; 164562306a36Sopenharmony_ci int nplanes = 0; 164662306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane(plane, crtc_state) 164962306a36Sopenharmony_ci nplanes++; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (nplanes > vp->nlayers) 165262306a36Sopenharmony_ci return -EINVAL; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci return 0; 165562306a36Sopenharmony_ci} 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic bool is_opaque(u16 alpha) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci return (alpha >> 8) == 0xff; 166062306a36Sopenharmony_ci} 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_cistatic void vop2_parse_alpha(struct vop2_alpha_config *alpha_config, 166362306a36Sopenharmony_ci struct vop2_alpha *alpha) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci int src_glb_alpha_en = is_opaque(alpha_config->src_glb_alpha_value) ? 0 : 1; 166662306a36Sopenharmony_ci int dst_glb_alpha_en = is_opaque(alpha_config->dst_glb_alpha_value) ? 0 : 1; 166762306a36Sopenharmony_ci int src_color_mode = alpha_config->src_premulti_en ? 166862306a36Sopenharmony_ci ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL; 166962306a36Sopenharmony_ci int dst_color_mode = alpha_config->dst_premulti_en ? 167062306a36Sopenharmony_ci ALPHA_SRC_PRE_MUL : ALPHA_SRC_NO_PRE_MUL; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci alpha->src_color_ctrl.val = 0; 167362306a36Sopenharmony_ci alpha->dst_color_ctrl.val = 0; 167462306a36Sopenharmony_ci alpha->src_alpha_ctrl.val = 0; 167562306a36Sopenharmony_ci alpha->dst_alpha_ctrl.val = 0; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (!alpha_config->src_pixel_alpha_en) 167862306a36Sopenharmony_ci alpha->src_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; 167962306a36Sopenharmony_ci else if (alpha_config->src_pixel_alpha_en && !src_glb_alpha_en) 168062306a36Sopenharmony_ci alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX; 168162306a36Sopenharmony_ci else 168262306a36Sopenharmony_ci alpha->src_color_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci alpha->src_color_ctrl.bits.alpha_en = 1; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_GLOBAL) { 168762306a36Sopenharmony_ci alpha->src_color_ctrl.bits.color_mode = src_color_mode; 168862306a36Sopenharmony_ci alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL; 168962306a36Sopenharmony_ci } else if (alpha->src_color_ctrl.bits.blend_mode == ALPHA_PER_PIX) { 169062306a36Sopenharmony_ci alpha->src_color_ctrl.bits.color_mode = src_color_mode; 169162306a36Sopenharmony_ci alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_ONE; 169262306a36Sopenharmony_ci } else { 169362306a36Sopenharmony_ci alpha->src_color_ctrl.bits.color_mode = ALPHA_SRC_PRE_MUL; 169462306a36Sopenharmony_ci alpha->src_color_ctrl.bits.factor_mode = SRC_FAC_ALPHA_SRC_GLOBAL; 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci alpha->src_color_ctrl.bits.glb_alpha = alpha_config->src_glb_alpha_value >> 8; 169762306a36Sopenharmony_ci alpha->src_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; 169862306a36Sopenharmony_ci alpha->src_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci alpha->dst_color_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; 170162306a36Sopenharmony_ci alpha->dst_color_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; 170262306a36Sopenharmony_ci alpha->dst_color_ctrl.bits.blend_mode = ALPHA_GLOBAL; 170362306a36Sopenharmony_ci alpha->dst_color_ctrl.bits.glb_alpha = alpha_config->dst_glb_alpha_value >> 8; 170462306a36Sopenharmony_ci alpha->dst_color_ctrl.bits.color_mode = dst_color_mode; 170562306a36Sopenharmony_ci alpha->dst_color_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci alpha->src_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; 170862306a36Sopenharmony_ci alpha->src_alpha_ctrl.bits.blend_mode = alpha->src_color_ctrl.bits.blend_mode; 170962306a36Sopenharmony_ci alpha->src_alpha_ctrl.bits.alpha_cal_mode = ALPHA_SATURATION; 171062306a36Sopenharmony_ci alpha->src_alpha_ctrl.bits.factor_mode = ALPHA_ONE; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci alpha->dst_alpha_ctrl.bits.alpha_mode = ALPHA_STRAIGHT; 171362306a36Sopenharmony_ci if (alpha_config->dst_pixel_alpha_en && !dst_glb_alpha_en) 171462306a36Sopenharmony_ci alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX; 171562306a36Sopenharmony_ci else 171662306a36Sopenharmony_ci alpha->dst_alpha_ctrl.bits.blend_mode = ALPHA_PER_PIX_GLOBAL; 171762306a36Sopenharmony_ci alpha->dst_alpha_ctrl.bits.alpha_cal_mode = ALPHA_NO_SATURATION; 171862306a36Sopenharmony_ci alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE; 171962306a36Sopenharmony_ci} 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_cistatic int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci struct vop2_video_port *vp; 172462306a36Sopenharmony_ci int used_layer = 0; 172562306a36Sopenharmony_ci int i; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci for (i = 0; i < port_id; i++) { 172862306a36Sopenharmony_ci vp = &vop2->vps[i]; 172962306a36Sopenharmony_ci used_layer += hweight32(vp->win_mask); 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci return used_layer; 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci u32 offset = (main_win->data->phys_id * 0x10); 173862306a36Sopenharmony_ci struct vop2_alpha_config alpha_config; 173962306a36Sopenharmony_ci struct vop2_alpha alpha; 174062306a36Sopenharmony_ci struct drm_plane_state *bottom_win_pstate; 174162306a36Sopenharmony_ci bool src_pixel_alpha_en = false; 174262306a36Sopenharmony_ci u16 src_glb_alpha_val, dst_glb_alpha_val; 174362306a36Sopenharmony_ci bool premulti_en = false; 174462306a36Sopenharmony_ci bool swap = false; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci /* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */ 174762306a36Sopenharmony_ci bottom_win_pstate = main_win->base.state; 174862306a36Sopenharmony_ci src_glb_alpha_val = 0; 174962306a36Sopenharmony_ci dst_glb_alpha_val = main_win->base.state->alpha; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci if (!bottom_win_pstate->fb) 175262306a36Sopenharmony_ci return; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci alpha_config.src_premulti_en = premulti_en; 175562306a36Sopenharmony_ci alpha_config.dst_premulti_en = false; 175662306a36Sopenharmony_ci alpha_config.src_pixel_alpha_en = src_pixel_alpha_en; 175762306a36Sopenharmony_ci alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */ 175862306a36Sopenharmony_ci alpha_config.src_glb_alpha_value = src_glb_alpha_val; 175962306a36Sopenharmony_ci alpha_config.dst_glb_alpha_value = dst_glb_alpha_val; 176062306a36Sopenharmony_ci vop2_parse_alpha(&alpha_config, &alpha); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci alpha.src_color_ctrl.bits.src_dst_swap = swap; 176362306a36Sopenharmony_ci vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset, 176462306a36Sopenharmony_ci alpha.src_color_ctrl.val); 176562306a36Sopenharmony_ci vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset, 176662306a36Sopenharmony_ci alpha.dst_color_ctrl.val); 176762306a36Sopenharmony_ci vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL + offset, 176862306a36Sopenharmony_ci alpha.src_alpha_ctrl.val); 176962306a36Sopenharmony_ci vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL + offset, 177062306a36Sopenharmony_ci alpha.dst_alpha_ctrl.val); 177162306a36Sopenharmony_ci} 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_cistatic void vop2_setup_alpha(struct vop2_video_port *vp) 177462306a36Sopenharmony_ci{ 177562306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 177662306a36Sopenharmony_ci struct drm_framebuffer *fb; 177762306a36Sopenharmony_ci struct vop2_alpha_config alpha_config; 177862306a36Sopenharmony_ci struct vop2_alpha alpha; 177962306a36Sopenharmony_ci struct drm_plane *plane; 178062306a36Sopenharmony_ci int pixel_alpha_en; 178162306a36Sopenharmony_ci int premulti_en, gpremulti_en = 0; 178262306a36Sopenharmony_ci int mixer_id; 178362306a36Sopenharmony_ci u32 offset; 178462306a36Sopenharmony_ci bool bottom_layer_alpha_en = false; 178562306a36Sopenharmony_ci u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id); 178862306a36Sopenharmony_ci alpha_config.dst_pixel_alpha_en = true; /* alpha value need transfer to next mix */ 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { 179162306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci if (plane->state->normalized_zpos == 0 && 179462306a36Sopenharmony_ci !is_opaque(plane->state->alpha) && 179562306a36Sopenharmony_ci !vop2_cluster_window(win)) { 179662306a36Sopenharmony_ci /* 179762306a36Sopenharmony_ci * If bottom layer have global alpha effect [except cluster layer, 179862306a36Sopenharmony_ci * because cluster have deal with bottom layer global alpha value 179962306a36Sopenharmony_ci * at cluster mix], bottom layer mix need deal with global alpha. 180062306a36Sopenharmony_ci */ 180162306a36Sopenharmony_ci bottom_layer_alpha_en = true; 180262306a36Sopenharmony_ci dst_global_alpha = plane->state->alpha; 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { 180762306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 180862306a36Sopenharmony_ci int zpos = plane->state->normalized_zpos; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) 181162306a36Sopenharmony_ci premulti_en = 1; 181262306a36Sopenharmony_ci else 181362306a36Sopenharmony_ci premulti_en = 0; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci plane = &win->base; 181662306a36Sopenharmony_ci fb = plane->state->fb; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci pixel_alpha_en = fb->format->has_alpha; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci alpha_config.src_premulti_en = premulti_en; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci if (bottom_layer_alpha_en && zpos == 1) { 182362306a36Sopenharmony_ci gpremulti_en = premulti_en; 182462306a36Sopenharmony_ci /* Cd = Cs + (1 - As) * Cd * Agd */ 182562306a36Sopenharmony_ci alpha_config.dst_premulti_en = false; 182662306a36Sopenharmony_ci alpha_config.src_pixel_alpha_en = pixel_alpha_en; 182762306a36Sopenharmony_ci alpha_config.src_glb_alpha_value = plane->state->alpha; 182862306a36Sopenharmony_ci alpha_config.dst_glb_alpha_value = dst_global_alpha; 182962306a36Sopenharmony_ci } else if (vop2_cluster_window(win)) { 183062306a36Sopenharmony_ci /* Mix output data only have pixel alpha */ 183162306a36Sopenharmony_ci alpha_config.dst_premulti_en = true; 183262306a36Sopenharmony_ci alpha_config.src_pixel_alpha_en = true; 183362306a36Sopenharmony_ci alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; 183462306a36Sopenharmony_ci alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; 183562306a36Sopenharmony_ci } else { 183662306a36Sopenharmony_ci /* Cd = Cs + (1 - As) * Cd */ 183762306a36Sopenharmony_ci alpha_config.dst_premulti_en = true; 183862306a36Sopenharmony_ci alpha_config.src_pixel_alpha_en = pixel_alpha_en; 183962306a36Sopenharmony_ci alpha_config.src_glb_alpha_value = plane->state->alpha; 184062306a36Sopenharmony_ci alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; 184162306a36Sopenharmony_ci } 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci vop2_parse_alpha(&alpha_config, &alpha); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci offset = (mixer_id + zpos - 1) * 0x10; 184662306a36Sopenharmony_ci vop2_writel(vop2, RK3568_MIX0_SRC_COLOR_CTRL + offset, 184762306a36Sopenharmony_ci alpha.src_color_ctrl.val); 184862306a36Sopenharmony_ci vop2_writel(vop2, RK3568_MIX0_DST_COLOR_CTRL + offset, 184962306a36Sopenharmony_ci alpha.dst_color_ctrl.val); 185062306a36Sopenharmony_ci vop2_writel(vop2, RK3568_MIX0_SRC_ALPHA_CTRL + offset, 185162306a36Sopenharmony_ci alpha.src_alpha_ctrl.val); 185262306a36Sopenharmony_ci vop2_writel(vop2, RK3568_MIX0_DST_ALPHA_CTRL + offset, 185362306a36Sopenharmony_ci alpha.dst_alpha_ctrl.val); 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci if (vp->id == 0) { 185762306a36Sopenharmony_ci if (bottom_layer_alpha_en) { 185862306a36Sopenharmony_ci /* Transfer pixel alpha to hdr mix */ 185962306a36Sopenharmony_ci alpha_config.src_premulti_en = gpremulti_en; 186062306a36Sopenharmony_ci alpha_config.dst_premulti_en = true; 186162306a36Sopenharmony_ci alpha_config.src_pixel_alpha_en = true; 186262306a36Sopenharmony_ci alpha_config.src_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; 186362306a36Sopenharmony_ci alpha_config.dst_glb_alpha_value = DRM_BLEND_ALPHA_OPAQUE; 186462306a36Sopenharmony_ci vop2_parse_alpha(&alpha_config, &alpha); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 186762306a36Sopenharmony_ci alpha.src_color_ctrl.val); 186862306a36Sopenharmony_ci vop2_writel(vop2, RK3568_HDR0_DST_COLOR_CTRL, 186962306a36Sopenharmony_ci alpha.dst_color_ctrl.val); 187062306a36Sopenharmony_ci vop2_writel(vop2, RK3568_HDR0_SRC_ALPHA_CTRL, 187162306a36Sopenharmony_ci alpha.src_alpha_ctrl.val); 187262306a36Sopenharmony_ci vop2_writel(vop2, RK3568_HDR0_DST_ALPHA_CTRL, 187362306a36Sopenharmony_ci alpha.dst_alpha_ctrl.val); 187462306a36Sopenharmony_ci } else { 187562306a36Sopenharmony_ci vop2_writel(vop2, RK3568_HDR0_SRC_COLOR_CTRL, 0); 187662306a36Sopenharmony_ci } 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci} 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_cistatic void vop2_setup_layer_mixer(struct vop2_video_port *vp) 188162306a36Sopenharmony_ci{ 188262306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 188362306a36Sopenharmony_ci struct drm_plane *plane; 188462306a36Sopenharmony_ci u32 layer_sel = 0; 188562306a36Sopenharmony_ci u32 port_sel; 188662306a36Sopenharmony_ci unsigned int nlayer, ofs; 188762306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode; 188862306a36Sopenharmony_ci u16 hsync_len; 188962306a36Sopenharmony_ci u16 hdisplay; 189062306a36Sopenharmony_ci u32 bg_dly; 189162306a36Sopenharmony_ci u32 pre_scan_dly; 189262306a36Sopenharmony_ci int i; 189362306a36Sopenharmony_ci struct vop2_video_port *vp0 = &vop2->vps[0]; 189462306a36Sopenharmony_ci struct vop2_video_port *vp1 = &vop2->vps[1]; 189562306a36Sopenharmony_ci struct vop2_video_port *vp2 = &vop2->vps[2]; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci adjusted_mode = &vp->crtc.state->adjusted_mode; 189862306a36Sopenharmony_ci hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; 189962306a36Sopenharmony_ci hdisplay = adjusted_mode->crtc_hdisplay; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci bg_dly = vp->data->pre_scan_max_dly[3]; 190262306a36Sopenharmony_ci vop2_writel(vop2, RK3568_VP_BG_MIX_CTRL(vp->id), 190362306a36Sopenharmony_ci FIELD_PREP(RK3568_VP_BG_MIX_CTRL__BG_DLY, bg_dly)); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci pre_scan_dly = ((bg_dly + (hdisplay >> 1) - 1) << 16) | hsync_len; 190662306a36Sopenharmony_ci vop2_vp_write(vp, RK3568_VP_PRE_SCAN_HTIMING, pre_scan_dly); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci vop2_writel(vop2, RK3568_OVL_CTRL, 0); 190962306a36Sopenharmony_ci port_sel = vop2_readl(vop2, RK3568_OVL_PORT_SEL); 191062306a36Sopenharmony_ci port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci if (vp0->nlayers) 191362306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 191462306a36Sopenharmony_ci vp0->nlayers - 1); 191562306a36Sopenharmony_ci else 191662306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT0_MUX, 8); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci if (vp1->nlayers) 191962306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 192062306a36Sopenharmony_ci (vp0->nlayers + vp1->nlayers - 1)); 192162306a36Sopenharmony_ci else 192262306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci if (vp2->nlayers) 192562306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT2_MUX, 192662306a36Sopenharmony_ci (vp2->nlayers + vp1->nlayers + vp0->nlayers - 1)); 192762306a36Sopenharmony_ci else 192862306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SET__PORT1_MUX, 8); 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci ofs = 0; 193362306a36Sopenharmony_ci for (i = 0; i < vp->id; i++) 193462306a36Sopenharmony_ci ofs += vop2->vps[i].nlayers; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci nlayer = 0; 193762306a36Sopenharmony_ci drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { 193862306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci switch (win->data->phys_id) { 194162306a36Sopenharmony_ci case ROCKCHIP_VOP2_CLUSTER0: 194262306a36Sopenharmony_ci port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER0; 194362306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER0, vp->id); 194462306a36Sopenharmony_ci break; 194562306a36Sopenharmony_ci case ROCKCHIP_VOP2_CLUSTER1: 194662306a36Sopenharmony_ci port_sel &= ~RK3568_OVL_PORT_SEL__CLUSTER1; 194762306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__CLUSTER1, vp->id); 194862306a36Sopenharmony_ci break; 194962306a36Sopenharmony_ci case ROCKCHIP_VOP2_ESMART0: 195062306a36Sopenharmony_ci port_sel &= ~RK3568_OVL_PORT_SEL__ESMART0; 195162306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART0, vp->id); 195262306a36Sopenharmony_ci break; 195362306a36Sopenharmony_ci case ROCKCHIP_VOP2_ESMART1: 195462306a36Sopenharmony_ci port_sel &= ~RK3568_OVL_PORT_SEL__ESMART1; 195562306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__ESMART1, vp->id); 195662306a36Sopenharmony_ci break; 195762306a36Sopenharmony_ci case ROCKCHIP_VOP2_SMART0: 195862306a36Sopenharmony_ci port_sel &= ~RK3568_OVL_PORT_SEL__SMART0; 195962306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART0, vp->id); 196062306a36Sopenharmony_ci break; 196162306a36Sopenharmony_ci case ROCKCHIP_VOP2_SMART1: 196262306a36Sopenharmony_ci port_sel &= ~RK3568_OVL_PORT_SEL__SMART1; 196362306a36Sopenharmony_ci port_sel |= FIELD_PREP(RK3568_OVL_PORT_SEL__SMART1, vp->id); 196462306a36Sopenharmony_ci break; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs, 196862306a36Sopenharmony_ci 0x7); 196962306a36Sopenharmony_ci layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs, 197062306a36Sopenharmony_ci win->data->layer_sel_id); 197162306a36Sopenharmony_ci nlayer++; 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* configure unused layers to 0x5 (reserved) */ 197562306a36Sopenharmony_ci for (; nlayer < vp->nlayers; nlayer++) { 197662306a36Sopenharmony_ci layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7); 197762306a36Sopenharmony_ci layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5); 197862306a36Sopenharmony_ci } 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel); 198162306a36Sopenharmony_ci vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel); 198262306a36Sopenharmony_ci vop2_writel(vop2, RK3568_OVL_CTRL, RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD); 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_cistatic void vop2_setup_dly_for_windows(struct vop2 *vop2) 198662306a36Sopenharmony_ci{ 198762306a36Sopenharmony_ci struct vop2_win *win; 198862306a36Sopenharmony_ci int i = 0; 198962306a36Sopenharmony_ci u32 cdly = 0, sdly = 0; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci for (i = 0; i < vop2->data->win_size; i++) { 199262306a36Sopenharmony_ci u32 dly; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci win = &vop2->win[i]; 199562306a36Sopenharmony_ci dly = win->delay; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci switch (win->data->phys_id) { 199862306a36Sopenharmony_ci case ROCKCHIP_VOP2_CLUSTER0: 199962306a36Sopenharmony_ci cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_0, dly); 200062306a36Sopenharmony_ci cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER0_1, dly); 200162306a36Sopenharmony_ci break; 200262306a36Sopenharmony_ci case ROCKCHIP_VOP2_CLUSTER1: 200362306a36Sopenharmony_ci cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_0, dly); 200462306a36Sopenharmony_ci cdly |= FIELD_PREP(RK3568_CLUSTER_DLY_NUM__CLUSTER1_1, dly); 200562306a36Sopenharmony_ci break; 200662306a36Sopenharmony_ci case ROCKCHIP_VOP2_ESMART0: 200762306a36Sopenharmony_ci sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART0, dly); 200862306a36Sopenharmony_ci break; 200962306a36Sopenharmony_ci case ROCKCHIP_VOP2_ESMART1: 201062306a36Sopenharmony_ci sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly); 201162306a36Sopenharmony_ci break; 201262306a36Sopenharmony_ci case ROCKCHIP_VOP2_SMART0: 201362306a36Sopenharmony_ci sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly); 201462306a36Sopenharmony_ci break; 201562306a36Sopenharmony_ci case ROCKCHIP_VOP2_SMART1: 201662306a36Sopenharmony_ci sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly); 201762306a36Sopenharmony_ci break; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci vop2_writel(vop2, RK3568_CLUSTER_DLY_NUM, cdly); 202262306a36Sopenharmony_ci vop2_writel(vop2, RK3568_SMART_DLY_NUM, sdly); 202362306a36Sopenharmony_ci} 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_cistatic void vop2_crtc_atomic_begin(struct drm_crtc *crtc, 202662306a36Sopenharmony_ci struct drm_atomic_state *state) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 202962306a36Sopenharmony_ci struct vop2 *vop2 = vp->vop2; 203062306a36Sopenharmony_ci struct drm_plane *plane; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci vp->win_mask = 0; 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci drm_atomic_crtc_for_each_plane(plane, crtc) { 203562306a36Sopenharmony_ci struct vop2_win *win = to_vop2_win(plane); 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci win->delay = win->data->dly[VOP2_DLY_MODE_DEFAULT]; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci vp->win_mask |= BIT(win->data->phys_id); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci if (vop2_cluster_window(win)) 204262306a36Sopenharmony_ci vop2_setup_cluster_alpha(vop2, win); 204362306a36Sopenharmony_ci } 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci if (!vp->win_mask) 204662306a36Sopenharmony_ci return; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci vop2_setup_layer_mixer(vp); 204962306a36Sopenharmony_ci vop2_setup_alpha(vp); 205062306a36Sopenharmony_ci vop2_setup_dly_for_windows(vop2); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic void vop2_crtc_atomic_flush(struct drm_crtc *crtc, 205462306a36Sopenharmony_ci struct drm_atomic_state *state) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci struct vop2_video_port *vp = to_vop2_video_port(crtc); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci vop2_post_config(crtc); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci vop2_cfg_done(vp); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci if (crtc->state->event) { 206562306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc)); 206662306a36Sopenharmony_ci vp->event = crtc->state->event; 206762306a36Sopenharmony_ci crtc->state->event = NULL; 206862306a36Sopenharmony_ci } 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { 207462306a36Sopenharmony_ci .mode_fixup = vop2_crtc_mode_fixup, 207562306a36Sopenharmony_ci .atomic_check = vop2_crtc_atomic_check, 207662306a36Sopenharmony_ci .atomic_begin = vop2_crtc_atomic_begin, 207762306a36Sopenharmony_ci .atomic_flush = vop2_crtc_atomic_flush, 207862306a36Sopenharmony_ci .atomic_enable = vop2_crtc_atomic_enable, 207962306a36Sopenharmony_ci .atomic_disable = vop2_crtc_atomic_disable, 208062306a36Sopenharmony_ci}; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_cistatic struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc) 208362306a36Sopenharmony_ci{ 208462306a36Sopenharmony_ci struct rockchip_crtc_state *vcstate; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci if (WARN_ON(!crtc->state)) 208762306a36Sopenharmony_ci return NULL; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci vcstate = kmemdup(to_rockchip_crtc_state(crtc->state), 209062306a36Sopenharmony_ci sizeof(*vcstate), GFP_KERNEL); 209162306a36Sopenharmony_ci if (!vcstate) 209262306a36Sopenharmony_ci return NULL; 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base); 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci return &vcstate->base; 209762306a36Sopenharmony_ci} 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_cistatic void vop2_crtc_destroy_state(struct drm_crtc *crtc, 210062306a36Sopenharmony_ci struct drm_crtc_state *state) 210162306a36Sopenharmony_ci{ 210262306a36Sopenharmony_ci struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(state); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(&vcstate->base); 210562306a36Sopenharmony_ci kfree(vcstate); 210662306a36Sopenharmony_ci} 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_cistatic void vop2_crtc_reset(struct drm_crtc *crtc) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci struct rockchip_crtc_state *vcstate = 211162306a36Sopenharmony_ci kzalloc(sizeof(*vcstate), GFP_KERNEL); 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci if (crtc->state) 211462306a36Sopenharmony_ci vop2_crtc_destroy_state(crtc, crtc->state); 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci if (vcstate) 211762306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &vcstate->base); 211862306a36Sopenharmony_ci else 211962306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, NULL); 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_cistatic const struct drm_crtc_funcs vop2_crtc_funcs = { 212362306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 212462306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 212562306a36Sopenharmony_ci .destroy = drm_crtc_cleanup, 212662306a36Sopenharmony_ci .reset = vop2_crtc_reset, 212762306a36Sopenharmony_ci .atomic_duplicate_state = vop2_crtc_duplicate_state, 212862306a36Sopenharmony_ci .atomic_destroy_state = vop2_crtc_destroy_state, 212962306a36Sopenharmony_ci .enable_vblank = vop2_crtc_enable_vblank, 213062306a36Sopenharmony_ci .disable_vblank = vop2_crtc_disable_vblank, 213162306a36Sopenharmony_ci}; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_cistatic irqreturn_t vop2_isr(int irq, void *data) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci struct vop2 *vop2 = data; 213662306a36Sopenharmony_ci const struct vop2_data *vop2_data = vop2->data; 213762306a36Sopenharmony_ci u32 axi_irqs[VOP2_SYS_AXI_BUS_NUM]; 213862306a36Sopenharmony_ci int ret = IRQ_NONE; 213962306a36Sopenharmony_ci int i; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci /* 214262306a36Sopenharmony_ci * The irq is shared with the iommu. If the runtime-pm state of the 214362306a36Sopenharmony_ci * vop2-device is disabled the irq has to be targeted at the iommu. 214462306a36Sopenharmony_ci */ 214562306a36Sopenharmony_ci if (!pm_runtime_get_if_in_use(vop2->dev)) 214662306a36Sopenharmony_ci return IRQ_NONE; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci for (i = 0; i < vop2_data->nr_vps; i++) { 214962306a36Sopenharmony_ci struct vop2_video_port *vp = &vop2->vps[i]; 215062306a36Sopenharmony_ci struct drm_crtc *crtc = &vp->crtc; 215162306a36Sopenharmony_ci u32 irqs; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci irqs = vop2_readl(vop2, RK3568_VP_INT_STATUS(vp->id)); 215462306a36Sopenharmony_ci vop2_writel(vop2, RK3568_VP_INT_CLR(vp->id), irqs << 16 | irqs); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (irqs & VP_INT_DSP_HOLD_VALID) { 215762306a36Sopenharmony_ci complete(&vp->dsp_hold_completion); 215862306a36Sopenharmony_ci ret = IRQ_HANDLED; 215962306a36Sopenharmony_ci } 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci if (irqs & VP_INT_FS_FIELD) { 216262306a36Sopenharmony_ci drm_crtc_handle_vblank(crtc); 216362306a36Sopenharmony_ci spin_lock(&crtc->dev->event_lock); 216462306a36Sopenharmony_ci if (vp->event) { 216562306a36Sopenharmony_ci u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci if (!(val & BIT(vp->id))) { 216862306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, vp->event); 216962306a36Sopenharmony_ci vp->event = NULL; 217062306a36Sopenharmony_ci drm_crtc_vblank_put(crtc); 217162306a36Sopenharmony_ci } 217262306a36Sopenharmony_ci } 217362306a36Sopenharmony_ci spin_unlock(&crtc->dev->event_lock); 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci ret = IRQ_HANDLED; 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci if (irqs & VP_INT_POST_BUF_EMPTY) { 217962306a36Sopenharmony_ci drm_err_ratelimited(vop2->drm, 218062306a36Sopenharmony_ci "POST_BUF_EMPTY irq err at vp%d\n", 218162306a36Sopenharmony_ci vp->id); 218262306a36Sopenharmony_ci ret = IRQ_HANDLED; 218362306a36Sopenharmony_ci } 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci axi_irqs[0] = vop2_readl(vop2, RK3568_SYS0_INT_STATUS); 218762306a36Sopenharmony_ci vop2_writel(vop2, RK3568_SYS0_INT_CLR, axi_irqs[0] << 16 | axi_irqs[0]); 218862306a36Sopenharmony_ci axi_irqs[1] = vop2_readl(vop2, RK3568_SYS1_INT_STATUS); 218962306a36Sopenharmony_ci vop2_writel(vop2, RK3568_SYS1_INT_CLR, axi_irqs[1] << 16 | axi_irqs[1]); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(axi_irqs); i++) { 219262306a36Sopenharmony_ci if (axi_irqs[i] & VOP2_INT_BUS_ERRPR) { 219362306a36Sopenharmony_ci drm_err_ratelimited(vop2->drm, "BUS_ERROR irq err\n"); 219462306a36Sopenharmony_ci ret = IRQ_HANDLED; 219562306a36Sopenharmony_ci } 219662306a36Sopenharmony_ci } 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci pm_runtime_put(vop2->dev); 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci return ret; 220162306a36Sopenharmony_ci} 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_cistatic int vop2_plane_init(struct vop2 *vop2, struct vop2_win *win, 220462306a36Sopenharmony_ci unsigned long possible_crtcs) 220562306a36Sopenharmony_ci{ 220662306a36Sopenharmony_ci const struct vop2_win_data *win_data = win->data; 220762306a36Sopenharmony_ci unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | 220862306a36Sopenharmony_ci BIT(DRM_MODE_BLEND_PREMULTI) | 220962306a36Sopenharmony_ci BIT(DRM_MODE_BLEND_COVERAGE); 221062306a36Sopenharmony_ci int ret; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci ret = drm_universal_plane_init(vop2->drm, &win->base, possible_crtcs, 221362306a36Sopenharmony_ci &vop2_plane_funcs, win_data->formats, 221462306a36Sopenharmony_ci win_data->nformats, 221562306a36Sopenharmony_ci win_data->format_modifiers, 221662306a36Sopenharmony_ci win->type, win_data->name); 221762306a36Sopenharmony_ci if (ret) { 221862306a36Sopenharmony_ci drm_err(vop2->drm, "failed to initialize plane %d\n", ret); 221962306a36Sopenharmony_ci return ret; 222062306a36Sopenharmony_ci } 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci drm_plane_helper_add(&win->base, &vop2_plane_helper_funcs); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci if (win->data->supported_rotations) 222562306a36Sopenharmony_ci drm_plane_create_rotation_property(&win->base, DRM_MODE_ROTATE_0, 222662306a36Sopenharmony_ci DRM_MODE_ROTATE_0 | 222762306a36Sopenharmony_ci win->data->supported_rotations); 222862306a36Sopenharmony_ci drm_plane_create_alpha_property(&win->base); 222962306a36Sopenharmony_ci drm_plane_create_blend_mode_property(&win->base, blend_caps); 223062306a36Sopenharmony_ci drm_plane_create_zpos_property(&win->base, win->win_id, 0, 223162306a36Sopenharmony_ci vop2->registered_num_wins - 1); 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci return 0; 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_cistatic struct vop2_video_port *find_vp_without_primary(struct vop2 *vop2) 223762306a36Sopenharmony_ci{ 223862306a36Sopenharmony_ci int i; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci for (i = 0; i < vop2->data->nr_vps; i++) { 224162306a36Sopenharmony_ci struct vop2_video_port *vp = &vop2->vps[i]; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci if (!vp->crtc.port) 224462306a36Sopenharmony_ci continue; 224562306a36Sopenharmony_ci if (vp->primary_plane) 224662306a36Sopenharmony_ci continue; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci return vp; 224962306a36Sopenharmony_ci } 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci return NULL; 225262306a36Sopenharmony_ci} 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci#define NR_LAYERS 6 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_cistatic int vop2_create_crtcs(struct vop2 *vop2) 225762306a36Sopenharmony_ci{ 225862306a36Sopenharmony_ci const struct vop2_data *vop2_data = vop2->data; 225962306a36Sopenharmony_ci struct drm_device *drm = vop2->drm; 226062306a36Sopenharmony_ci struct device *dev = vop2->dev; 226162306a36Sopenharmony_ci struct drm_plane *plane; 226262306a36Sopenharmony_ci struct device_node *port; 226362306a36Sopenharmony_ci struct vop2_video_port *vp; 226462306a36Sopenharmony_ci int i, nvp, nvps = 0; 226562306a36Sopenharmony_ci int ret; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci for (i = 0; i < vop2_data->nr_vps; i++) { 226862306a36Sopenharmony_ci const struct vop2_video_port_data *vp_data; 226962306a36Sopenharmony_ci struct device_node *np; 227062306a36Sopenharmony_ci char dclk_name[9]; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci vp_data = &vop2_data->vp[i]; 227362306a36Sopenharmony_ci vp = &vop2->vps[i]; 227462306a36Sopenharmony_ci vp->vop2 = vop2; 227562306a36Sopenharmony_ci vp->id = vp_data->id; 227662306a36Sopenharmony_ci vp->regs = vp_data->regs; 227762306a36Sopenharmony_ci vp->data = vp_data; 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id); 228062306a36Sopenharmony_ci vp->dclk = devm_clk_get(vop2->dev, dclk_name); 228162306a36Sopenharmony_ci if (IS_ERR(vp->dclk)) { 228262306a36Sopenharmony_ci drm_err(vop2->drm, "failed to get %s\n", dclk_name); 228362306a36Sopenharmony_ci return PTR_ERR(vp->dclk); 228462306a36Sopenharmony_ci } 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci np = of_graph_get_remote_node(dev->of_node, i, -1); 228762306a36Sopenharmony_ci if (!np) { 228862306a36Sopenharmony_ci drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i); 228962306a36Sopenharmony_ci continue; 229062306a36Sopenharmony_ci } 229162306a36Sopenharmony_ci of_node_put(np); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci port = of_graph_get_port_by_id(dev->of_node, i); 229462306a36Sopenharmony_ci if (!port) { 229562306a36Sopenharmony_ci drm_err(vop2->drm, "no port node found for video_port%d\n", i); 229662306a36Sopenharmony_ci return -ENOENT; 229762306a36Sopenharmony_ci } 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci vp->crtc.port = port; 230062306a36Sopenharmony_ci nvps++; 230162306a36Sopenharmony_ci } 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci nvp = 0; 230462306a36Sopenharmony_ci for (i = 0; i < vop2->registered_num_wins; i++) { 230562306a36Sopenharmony_ci struct vop2_win *win = &vop2->win[i]; 230662306a36Sopenharmony_ci u32 possible_crtcs = 0; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci if (vop2->data->soc_id == 3566) { 230962306a36Sopenharmony_ci /* 231062306a36Sopenharmony_ci * On RK3566 these windows don't have an independent 231162306a36Sopenharmony_ci * framebuffer. They share the framebuffer with smart0, 231262306a36Sopenharmony_ci * esmart0 and cluster0 respectively. 231362306a36Sopenharmony_ci */ 231462306a36Sopenharmony_ci switch (win->data->phys_id) { 231562306a36Sopenharmony_ci case ROCKCHIP_VOP2_SMART1: 231662306a36Sopenharmony_ci case ROCKCHIP_VOP2_ESMART1: 231762306a36Sopenharmony_ci case ROCKCHIP_VOP2_CLUSTER1: 231862306a36Sopenharmony_ci continue; 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci } 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci if (win->type == DRM_PLANE_TYPE_PRIMARY) { 232362306a36Sopenharmony_ci vp = find_vp_without_primary(vop2); 232462306a36Sopenharmony_ci if (vp) { 232562306a36Sopenharmony_ci possible_crtcs = BIT(nvp); 232662306a36Sopenharmony_ci vp->primary_plane = win; 232762306a36Sopenharmony_ci nvp++; 232862306a36Sopenharmony_ci } else { 232962306a36Sopenharmony_ci /* change the unused primary window to overlay window */ 233062306a36Sopenharmony_ci win->type = DRM_PLANE_TYPE_OVERLAY; 233162306a36Sopenharmony_ci } 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci if (win->type == DRM_PLANE_TYPE_OVERLAY) 233562306a36Sopenharmony_ci possible_crtcs = (1 << nvps) - 1; 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci ret = vop2_plane_init(vop2, win, possible_crtcs); 233862306a36Sopenharmony_ci if (ret) { 233962306a36Sopenharmony_ci drm_err(vop2->drm, "failed to init plane %s: %d\n", 234062306a36Sopenharmony_ci win->data->name, ret); 234162306a36Sopenharmony_ci return ret; 234262306a36Sopenharmony_ci } 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci for (i = 0; i < vop2_data->nr_vps; i++) { 234662306a36Sopenharmony_ci vp = &vop2->vps[i]; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci if (!vp->crtc.port) 234962306a36Sopenharmony_ci continue; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci plane = &vp->primary_plane->base; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(drm, &vp->crtc, plane, NULL, 235462306a36Sopenharmony_ci &vop2_crtc_funcs, 235562306a36Sopenharmony_ci "video_port%d", vp->id); 235662306a36Sopenharmony_ci if (ret) { 235762306a36Sopenharmony_ci drm_err(vop2->drm, "crtc init for video_port%d failed\n", i); 235862306a36Sopenharmony_ci return ret; 235962306a36Sopenharmony_ci } 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci init_completion(&vp->dsp_hold_completion); 236462306a36Sopenharmony_ci } 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci /* 236762306a36Sopenharmony_ci * On the VOP2 it's very hard to change the number of layers on a VP 236862306a36Sopenharmony_ci * during runtime, so we distribute the layers equally over the used 236962306a36Sopenharmony_ci * VPs 237062306a36Sopenharmony_ci */ 237162306a36Sopenharmony_ci for (i = 0; i < vop2->data->nr_vps; i++) { 237262306a36Sopenharmony_ci struct vop2_video_port *vp = &vop2->vps[i]; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci if (vp->crtc.port) 237562306a36Sopenharmony_ci vp->nlayers = NR_LAYERS / nvps; 237662306a36Sopenharmony_ci } 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci return 0; 237962306a36Sopenharmony_ci} 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_cistatic void vop2_destroy_crtcs(struct vop2 *vop2) 238262306a36Sopenharmony_ci{ 238362306a36Sopenharmony_ci struct drm_device *drm = vop2->drm; 238462306a36Sopenharmony_ci struct list_head *crtc_list = &drm->mode_config.crtc_list; 238562306a36Sopenharmony_ci struct list_head *plane_list = &drm->mode_config.plane_list; 238662306a36Sopenharmony_ci struct drm_crtc *crtc, *tmpc; 238762306a36Sopenharmony_ci struct drm_plane *plane, *tmpp; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci list_for_each_entry_safe(plane, tmpp, plane_list, head) 239062306a36Sopenharmony_ci drm_plane_cleanup(plane); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci /* 239362306a36Sopenharmony_ci * Destroy CRTC after vop2_plane_destroy() since vop2_disable_plane() 239462306a36Sopenharmony_ci * references the CRTC. 239562306a36Sopenharmony_ci */ 239662306a36Sopenharmony_ci list_for_each_entry_safe(crtc, tmpc, crtc_list, head) { 239762306a36Sopenharmony_ci of_node_put(crtc->port); 239862306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 239962306a36Sopenharmony_ci } 240062306a36Sopenharmony_ci} 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_cistatic int vop2_find_rgb_encoder(struct vop2 *vop2) 240362306a36Sopenharmony_ci{ 240462306a36Sopenharmony_ci struct device_node *node = vop2->dev->of_node; 240562306a36Sopenharmony_ci struct device_node *endpoint; 240662306a36Sopenharmony_ci int i; 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci for (i = 0; i < vop2->data->nr_vps; i++) { 240962306a36Sopenharmony_ci endpoint = of_graph_get_endpoint_by_regs(node, i, 241062306a36Sopenharmony_ci ROCKCHIP_VOP2_EP_RGB0); 241162306a36Sopenharmony_ci if (!endpoint) 241262306a36Sopenharmony_ci continue; 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci of_node_put(endpoint); 241562306a36Sopenharmony_ci return i; 241662306a36Sopenharmony_ci } 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci return -ENOENT; 241962306a36Sopenharmony_ci} 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_cistatic struct reg_field vop2_cluster_regs[VOP2_WIN_MAX_REG] = { 242262306a36Sopenharmony_ci [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 0, 0), 242362306a36Sopenharmony_ci [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 1, 5), 242462306a36Sopenharmony_ci [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 14, 14), 242562306a36Sopenharmony_ci [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 18, 18), 242662306a36Sopenharmony_ci [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_ACT_INFO, 0, 31), 242762306a36Sopenharmony_ci [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_INFO, 0, 31), 242862306a36Sopenharmony_ci [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_CLUSTER_WIN_DSP_ST, 0, 31), 242962306a36Sopenharmony_ci [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_CLUSTER_WIN_YRGB_MST, 0, 31), 243062306a36Sopenharmony_ci [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_CLUSTER_WIN_CBR_MST, 0, 31), 243162306a36Sopenharmony_ci [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 19, 19), 243262306a36Sopenharmony_ci [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 0, 15), 243362306a36Sopenharmony_ci [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_CLUSTER_WIN_VIR, 16, 31), 243462306a36Sopenharmony_ci [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8), 243562306a36Sopenharmony_ci [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9), 243662306a36Sopenharmony_ci [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11), 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci /* Scale */ 243962306a36Sopenharmony_ci [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15), 244062306a36Sopenharmony_ci [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 16, 31), 244162306a36Sopenharmony_ci [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 14, 15), 244262306a36Sopenharmony_ci [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 12, 13), 244362306a36Sopenharmony_ci [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 2, 3), 244462306a36Sopenharmony_ci [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 28, 28), 244562306a36Sopenharmony_ci [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL1, 29, 29), 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci /* cluster regs */ 244862306a36Sopenharmony_ci [VOP2_WIN_AFBC_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 1, 1), 244962306a36Sopenharmony_ci [VOP2_WIN_CLUSTER_ENABLE] = REG_FIELD(RK3568_CLUSTER_CTRL, 0, 0), 245062306a36Sopenharmony_ci [VOP2_WIN_CLUSTER_LB_MODE] = REG_FIELD(RK3568_CLUSTER_CTRL, 4, 7), 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci /* afbc regs */ 245362306a36Sopenharmony_ci [VOP2_WIN_AFBC_FORMAT] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 2, 6), 245462306a36Sopenharmony_ci [VOP2_WIN_AFBC_RB_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 9, 9), 245562306a36Sopenharmony_ci [VOP2_WIN_AFBC_UV_SWAP] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 10, 10), 245662306a36Sopenharmony_ci [VOP2_WIN_AFBC_AUTO_GATING_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL, 4, 4), 245762306a36Sopenharmony_ci [VOP2_WIN_AFBC_HALF_BLOCK_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 7, 7), 245862306a36Sopenharmony_ci [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_CTRL, 8, 8), 245962306a36Sopenharmony_ci [VOP2_WIN_AFBC_HDR_PTR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_HDR_PTR, 0, 31), 246062306a36Sopenharmony_ci [VOP2_WIN_AFBC_PIC_SIZE] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE, 0, 31), 246162306a36Sopenharmony_ci [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 0, 15), 246262306a36Sopenharmony_ci [VOP2_WIN_AFBC_TILE_NUM] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH, 16, 31), 246362306a36Sopenharmony_ci [VOP2_WIN_AFBC_PIC_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET, 0, 31), 246462306a36Sopenharmony_ci [VOP2_WIN_AFBC_DSP_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET, 0, 31), 246562306a36Sopenharmony_ci [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET, 0, 31), 246662306a36Sopenharmony_ci [VOP2_WIN_AFBC_ROTATE_90] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 0, 0), 246762306a36Sopenharmony_ci [VOP2_WIN_AFBC_ROTATE_270] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 1, 1), 246862306a36Sopenharmony_ci [VOP2_WIN_XMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 2, 2), 246962306a36Sopenharmony_ci [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE, 3, 3), 247062306a36Sopenharmony_ci [VOP2_WIN_UV_SWAP] = { .reg = 0xffffffff }, 247162306a36Sopenharmony_ci [VOP2_WIN_COLOR_KEY] = { .reg = 0xffffffff }, 247262306a36Sopenharmony_ci [VOP2_WIN_COLOR_KEY_EN] = { .reg = 0xffffffff }, 247362306a36Sopenharmony_ci [VOP2_WIN_SCALE_CBCR_X] = { .reg = 0xffffffff }, 247462306a36Sopenharmony_ci [VOP2_WIN_SCALE_CBCR_Y] = { .reg = 0xffffffff }, 247562306a36Sopenharmony_ci [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = { .reg = 0xffffffff }, 247662306a36Sopenharmony_ci [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = { .reg = 0xffffffff }, 247762306a36Sopenharmony_ci [VOP2_WIN_CBCR_VER_SCL_MODE] = { .reg = 0xffffffff }, 247862306a36Sopenharmony_ci [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = { .reg = 0xffffffff }, 247962306a36Sopenharmony_ci [VOP2_WIN_CBCR_HOR_SCL_MODE] = { .reg = 0xffffffff }, 248062306a36Sopenharmony_ci [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = { .reg = 0xffffffff }, 248162306a36Sopenharmony_ci [VOP2_WIN_VSD_CBCR_GT2] = { .reg = 0xffffffff }, 248262306a36Sopenharmony_ci [VOP2_WIN_VSD_CBCR_GT4] = { .reg = 0xffffffff }, 248362306a36Sopenharmony_ci}; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_cistatic int vop2_cluster_init(struct vop2_win *win) 248662306a36Sopenharmony_ci{ 248762306a36Sopenharmony_ci struct vop2 *vop2 = win->vop2; 248862306a36Sopenharmony_ci struct reg_field *cluster_regs; 248962306a36Sopenharmony_ci int ret, i; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci cluster_regs = kmemdup(vop2_cluster_regs, sizeof(vop2_cluster_regs), 249262306a36Sopenharmony_ci GFP_KERNEL); 249362306a36Sopenharmony_ci if (!cluster_regs) 249462306a36Sopenharmony_ci return -ENOMEM; 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vop2_cluster_regs); i++) 249762306a36Sopenharmony_ci if (cluster_regs[i].reg != 0xffffffff) 249862306a36Sopenharmony_ci cluster_regs[i].reg += win->offset; 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg, 250162306a36Sopenharmony_ci cluster_regs, 250262306a36Sopenharmony_ci ARRAY_SIZE(vop2_cluster_regs)); 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci kfree(cluster_regs); 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci return ret; 250762306a36Sopenharmony_ci}; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_cistatic struct reg_field vop2_esmart_regs[VOP2_WIN_MAX_REG] = { 251062306a36Sopenharmony_ci [VOP2_WIN_ENABLE] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 0, 0), 251162306a36Sopenharmony_ci [VOP2_WIN_FORMAT] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 1, 5), 251262306a36Sopenharmony_ci [VOP2_WIN_DITHER_UP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 12, 12), 251362306a36Sopenharmony_ci [VOP2_WIN_RB_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 14, 14), 251462306a36Sopenharmony_ci [VOP2_WIN_UV_SWAP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 16, 16), 251562306a36Sopenharmony_ci [VOP2_WIN_ACT_INFO] = REG_FIELD(RK3568_SMART_REGION0_ACT_INFO, 0, 31), 251662306a36Sopenharmony_ci [VOP2_WIN_DSP_INFO] = REG_FIELD(RK3568_SMART_REGION0_DSP_INFO, 0, 31), 251762306a36Sopenharmony_ci [VOP2_WIN_DSP_ST] = REG_FIELD(RK3568_SMART_REGION0_DSP_ST, 0, 28), 251862306a36Sopenharmony_ci [VOP2_WIN_YRGB_MST] = REG_FIELD(RK3568_SMART_REGION0_YRGB_MST, 0, 31), 251962306a36Sopenharmony_ci [VOP2_WIN_UV_MST] = REG_FIELD(RK3568_SMART_REGION0_CBR_MST, 0, 31), 252062306a36Sopenharmony_ci [VOP2_WIN_YUV_CLIP] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 17, 17), 252162306a36Sopenharmony_ci [VOP2_WIN_YRGB_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 0, 15), 252262306a36Sopenharmony_ci [VOP2_WIN_UV_VIR] = REG_FIELD(RK3568_SMART_REGION0_VIR, 16, 31), 252362306a36Sopenharmony_ci [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_SMART_CTRL0, 0, 0), 252462306a36Sopenharmony_ci [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_SMART_CTRL0, 1, 1), 252562306a36Sopenharmony_ci [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_SMART_CTRL0, 2, 3), 252662306a36Sopenharmony_ci [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31), 252762306a36Sopenharmony_ci [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29), 252862306a36Sopenharmony_ci [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31), 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci /* Scale */ 253162306a36Sopenharmony_ci [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15), 253262306a36Sopenharmony_ci [VOP2_WIN_SCALE_YRGB_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 16, 31), 253362306a36Sopenharmony_ci [VOP2_WIN_SCALE_CBCR_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 0, 15), 253462306a36Sopenharmony_ci [VOP2_WIN_SCALE_CBCR_Y] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_CBR, 16, 31), 253562306a36Sopenharmony_ci [VOP2_WIN_YRGB_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 0, 1), 253662306a36Sopenharmony_ci [VOP2_WIN_YRGB_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 2, 3), 253762306a36Sopenharmony_ci [VOP2_WIN_YRGB_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 4, 5), 253862306a36Sopenharmony_ci [VOP2_WIN_YRGB_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 6, 7), 253962306a36Sopenharmony_ci [VOP2_WIN_CBCR_HOR_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 8, 9), 254062306a36Sopenharmony_ci [VOP2_WIN_CBCR_HSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 10, 11), 254162306a36Sopenharmony_ci [VOP2_WIN_CBCR_VER_SCL_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 12, 13), 254262306a36Sopenharmony_ci [VOP2_WIN_CBCR_VSCL_FILTER_MODE] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 14, 15), 254362306a36Sopenharmony_ci [VOP2_WIN_BIC_COE_SEL] = REG_FIELD(RK3568_SMART_REGION0_SCL_CTRL, 16, 17), 254462306a36Sopenharmony_ci [VOP2_WIN_VSD_YRGB_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 8, 8), 254562306a36Sopenharmony_ci [VOP2_WIN_VSD_YRGB_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 9, 9), 254662306a36Sopenharmony_ci [VOP2_WIN_VSD_CBCR_GT2] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 10, 10), 254762306a36Sopenharmony_ci [VOP2_WIN_VSD_CBCR_GT4] = REG_FIELD(RK3568_SMART_REGION0_CTRL, 11, 11), 254862306a36Sopenharmony_ci [VOP2_WIN_XMIRROR] = { .reg = 0xffffffff }, 254962306a36Sopenharmony_ci [VOP2_WIN_CLUSTER_ENABLE] = { .reg = 0xffffffff }, 255062306a36Sopenharmony_ci [VOP2_WIN_AFBC_ENABLE] = { .reg = 0xffffffff }, 255162306a36Sopenharmony_ci [VOP2_WIN_CLUSTER_LB_MODE] = { .reg = 0xffffffff }, 255262306a36Sopenharmony_ci [VOP2_WIN_AFBC_FORMAT] = { .reg = 0xffffffff }, 255362306a36Sopenharmony_ci [VOP2_WIN_AFBC_RB_SWAP] = { .reg = 0xffffffff }, 255462306a36Sopenharmony_ci [VOP2_WIN_AFBC_UV_SWAP] = { .reg = 0xffffffff }, 255562306a36Sopenharmony_ci [VOP2_WIN_AFBC_AUTO_GATING_EN] = { .reg = 0xffffffff }, 255662306a36Sopenharmony_ci [VOP2_WIN_AFBC_BLOCK_SPLIT_EN] = { .reg = 0xffffffff }, 255762306a36Sopenharmony_ci [VOP2_WIN_AFBC_PIC_VIR_WIDTH] = { .reg = 0xffffffff }, 255862306a36Sopenharmony_ci [VOP2_WIN_AFBC_TILE_NUM] = { .reg = 0xffffffff }, 255962306a36Sopenharmony_ci [VOP2_WIN_AFBC_PIC_OFFSET] = { .reg = 0xffffffff }, 256062306a36Sopenharmony_ci [VOP2_WIN_AFBC_PIC_SIZE] = { .reg = 0xffffffff }, 256162306a36Sopenharmony_ci [VOP2_WIN_AFBC_DSP_OFFSET] = { .reg = 0xffffffff }, 256262306a36Sopenharmony_ci [VOP2_WIN_AFBC_TRANSFORM_OFFSET] = { .reg = 0xffffffff }, 256362306a36Sopenharmony_ci [VOP2_WIN_AFBC_HDR_PTR] = { .reg = 0xffffffff }, 256462306a36Sopenharmony_ci [VOP2_WIN_AFBC_HALF_BLOCK_EN] = { .reg = 0xffffffff }, 256562306a36Sopenharmony_ci [VOP2_WIN_AFBC_ROTATE_270] = { .reg = 0xffffffff }, 256662306a36Sopenharmony_ci [VOP2_WIN_AFBC_ROTATE_90] = { .reg = 0xffffffff }, 256762306a36Sopenharmony_ci}; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_cistatic int vop2_esmart_init(struct vop2_win *win) 257062306a36Sopenharmony_ci{ 257162306a36Sopenharmony_ci struct vop2 *vop2 = win->vop2; 257262306a36Sopenharmony_ci struct reg_field *esmart_regs; 257362306a36Sopenharmony_ci int ret, i; 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci esmart_regs = kmemdup(vop2_esmart_regs, sizeof(vop2_esmart_regs), 257662306a36Sopenharmony_ci GFP_KERNEL); 257762306a36Sopenharmony_ci if (!esmart_regs) 257862306a36Sopenharmony_ci return -ENOMEM; 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vop2_esmart_regs); i++) 258162306a36Sopenharmony_ci if (esmart_regs[i].reg != 0xffffffff) 258262306a36Sopenharmony_ci esmart_regs[i].reg += win->offset; 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci ret = devm_regmap_field_bulk_alloc(vop2->dev, vop2->map, win->reg, 258562306a36Sopenharmony_ci esmart_regs, 258662306a36Sopenharmony_ci ARRAY_SIZE(vop2_esmart_regs)); 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci kfree(esmart_regs); 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci return ret; 259162306a36Sopenharmony_ci}; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_cistatic int vop2_win_init(struct vop2 *vop2) 259462306a36Sopenharmony_ci{ 259562306a36Sopenharmony_ci const struct vop2_data *vop2_data = vop2->data; 259662306a36Sopenharmony_ci struct vop2_win *win; 259762306a36Sopenharmony_ci int i, ret; 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci for (i = 0; i < vop2_data->win_size; i++) { 260062306a36Sopenharmony_ci const struct vop2_win_data *win_data = &vop2_data->win[i]; 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci win = &vop2->win[i]; 260362306a36Sopenharmony_ci win->data = win_data; 260462306a36Sopenharmony_ci win->type = win_data->type; 260562306a36Sopenharmony_ci win->offset = win_data->base; 260662306a36Sopenharmony_ci win->win_id = i; 260762306a36Sopenharmony_ci win->vop2 = vop2; 260862306a36Sopenharmony_ci if (vop2_cluster_window(win)) 260962306a36Sopenharmony_ci ret = vop2_cluster_init(win); 261062306a36Sopenharmony_ci else 261162306a36Sopenharmony_ci ret = vop2_esmart_init(win); 261262306a36Sopenharmony_ci if (ret) 261362306a36Sopenharmony_ci return ret; 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci vop2->registered_num_wins = vop2_data->win_size; 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci return 0; 261962306a36Sopenharmony_ci} 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci/* 262262306a36Sopenharmony_ci * The window registers are only updated when config done is written. 262362306a36Sopenharmony_ci * Until that they read back the old value. As we read-modify-write 262462306a36Sopenharmony_ci * these registers mark them as non-volatile. This makes sure we read 262562306a36Sopenharmony_ci * the new values from the regmap register cache. 262662306a36Sopenharmony_ci */ 262762306a36Sopenharmony_cistatic const struct regmap_range vop2_nonvolatile_range[] = { 262862306a36Sopenharmony_ci regmap_reg_range(0x1000, 0x23ff), 262962306a36Sopenharmony_ci}; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_cistatic const struct regmap_access_table vop2_volatile_table = { 263262306a36Sopenharmony_ci .no_ranges = vop2_nonvolatile_range, 263362306a36Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(vop2_nonvolatile_range), 263462306a36Sopenharmony_ci}; 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_cistatic const struct regmap_config vop2_regmap_config = { 263762306a36Sopenharmony_ci .reg_bits = 32, 263862306a36Sopenharmony_ci .val_bits = 32, 263962306a36Sopenharmony_ci .reg_stride = 4, 264062306a36Sopenharmony_ci .max_register = 0x3000, 264162306a36Sopenharmony_ci .name = "vop2", 264262306a36Sopenharmony_ci .volatile_table = &vop2_volatile_table, 264362306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 264462306a36Sopenharmony_ci}; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_cistatic int vop2_bind(struct device *dev, struct device *master, void *data) 264762306a36Sopenharmony_ci{ 264862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 264962306a36Sopenharmony_ci const struct vop2_data *vop2_data; 265062306a36Sopenharmony_ci struct drm_device *drm = data; 265162306a36Sopenharmony_ci struct vop2 *vop2; 265262306a36Sopenharmony_ci struct resource *res; 265362306a36Sopenharmony_ci size_t alloc_size; 265462306a36Sopenharmony_ci int ret; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci vop2_data = of_device_get_match_data(dev); 265762306a36Sopenharmony_ci if (!vop2_data) 265862306a36Sopenharmony_ci return -ENODEV; 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci /* Allocate vop2 struct and its vop2_win array */ 266162306a36Sopenharmony_ci alloc_size = struct_size(vop2, win, vop2_data->win_size); 266262306a36Sopenharmony_ci vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL); 266362306a36Sopenharmony_ci if (!vop2) 266462306a36Sopenharmony_ci return -ENOMEM; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci vop2->dev = dev; 266762306a36Sopenharmony_ci vop2->data = vop2_data; 266862306a36Sopenharmony_ci vop2->drm = drm; 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci dev_set_drvdata(dev, vop2); 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vop"); 267362306a36Sopenharmony_ci if (!res) { 267462306a36Sopenharmony_ci drm_err(vop2->drm, "failed to get vop2 register byname\n"); 267562306a36Sopenharmony_ci return -EINVAL; 267662306a36Sopenharmony_ci } 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci vop2->regs = devm_ioremap_resource(dev, res); 267962306a36Sopenharmony_ci if (IS_ERR(vop2->regs)) 268062306a36Sopenharmony_ci return PTR_ERR(vop2->regs); 268162306a36Sopenharmony_ci vop2->len = resource_size(res); 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci vop2->map = devm_regmap_init_mmio(dev, vop2->regs, &vop2_regmap_config); 268462306a36Sopenharmony_ci if (IS_ERR(vop2->map)) 268562306a36Sopenharmony_ci return PTR_ERR(vop2->map); 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci ret = vop2_win_init(vop2); 268862306a36Sopenharmony_ci if (ret) 268962306a36Sopenharmony_ci return ret; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma-lut"); 269262306a36Sopenharmony_ci if (res) { 269362306a36Sopenharmony_ci vop2->lut_regs = devm_ioremap_resource(dev, res); 269462306a36Sopenharmony_ci if (IS_ERR(vop2->lut_regs)) 269562306a36Sopenharmony_ci return PTR_ERR(vop2->lut_regs); 269662306a36Sopenharmony_ci } 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci vop2->hclk = devm_clk_get(vop2->dev, "hclk"); 270162306a36Sopenharmony_ci if (IS_ERR(vop2->hclk)) { 270262306a36Sopenharmony_ci drm_err(vop2->drm, "failed to get hclk source\n"); 270362306a36Sopenharmony_ci return PTR_ERR(vop2->hclk); 270462306a36Sopenharmony_ci } 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci vop2->aclk = devm_clk_get(vop2->dev, "aclk"); 270762306a36Sopenharmony_ci if (IS_ERR(vop2->aclk)) { 270862306a36Sopenharmony_ci drm_err(vop2->drm, "failed to get aclk source\n"); 270962306a36Sopenharmony_ci return PTR_ERR(vop2->aclk); 271062306a36Sopenharmony_ci } 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci vop2->irq = platform_get_irq(pdev, 0); 271362306a36Sopenharmony_ci if (vop2->irq < 0) { 271462306a36Sopenharmony_ci drm_err(vop2->drm, "cannot find irq for vop2\n"); 271562306a36Sopenharmony_ci return vop2->irq; 271662306a36Sopenharmony_ci } 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci mutex_init(&vop2->vop2_lock); 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci ret = devm_request_irq(dev, vop2->irq, vop2_isr, IRQF_SHARED, dev_name(dev), vop2); 272162306a36Sopenharmony_ci if (ret) 272262306a36Sopenharmony_ci return ret; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci ret = vop2_create_crtcs(vop2); 272562306a36Sopenharmony_ci if (ret) 272662306a36Sopenharmony_ci return ret; 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci ret = vop2_find_rgb_encoder(vop2); 272962306a36Sopenharmony_ci if (ret >= 0) { 273062306a36Sopenharmony_ci vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc, 273162306a36Sopenharmony_ci vop2->drm, ret); 273262306a36Sopenharmony_ci if (IS_ERR(vop2->rgb)) { 273362306a36Sopenharmony_ci if (PTR_ERR(vop2->rgb) == -EPROBE_DEFER) { 273462306a36Sopenharmony_ci ret = PTR_ERR(vop2->rgb); 273562306a36Sopenharmony_ci goto err_crtcs; 273662306a36Sopenharmony_ci } 273762306a36Sopenharmony_ci vop2->rgb = NULL; 273862306a36Sopenharmony_ci } 273962306a36Sopenharmony_ci } 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci rockchip_drm_dma_init_device(vop2->drm, vop2->dev); 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci return 0; 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_cierr_crtcs: 274862306a36Sopenharmony_ci vop2_destroy_crtcs(vop2); 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci return ret; 275162306a36Sopenharmony_ci} 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_cistatic void vop2_unbind(struct device *dev, struct device *master, void *data) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci struct vop2 *vop2 = dev_get_drvdata(dev); 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci pm_runtime_disable(dev); 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci if (vop2->rgb) 276062306a36Sopenharmony_ci rockchip_rgb_fini(vop2->rgb); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci vop2_destroy_crtcs(vop2); 276362306a36Sopenharmony_ci} 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ciconst struct component_ops vop2_component_ops = { 276662306a36Sopenharmony_ci .bind = vop2_bind, 276762306a36Sopenharmony_ci .unbind = vop2_unbind, 276862306a36Sopenharmony_ci}; 276962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vop2_component_ops); 2770