162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 462306a36Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 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/log2.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/overflow.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1862306a36Sopenharmony_ci#include <linux/reset.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <drm/drm.h> 2162306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2262306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h> 2362306a36Sopenharmony_ci#include <drm/drm_blend.h> 2462306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2562306a36Sopenharmony_ci#include <drm/drm_flip_work.h> 2662306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2762306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 2862306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 2962306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 3062306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3162306a36Sopenharmony_ci#include <drm/drm_self_refresh_helper.h> 3262306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#ifdef CONFIG_DRM_ANALOGIX_DP 3562306a36Sopenharmony_ci#include <drm/bridge/analogix_dp.h> 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "rockchip_drm_drv.h" 3962306a36Sopenharmony_ci#include "rockchip_drm_gem.h" 4062306a36Sopenharmony_ci#include "rockchip_drm_fb.h" 4162306a36Sopenharmony_ci#include "rockchip_drm_vop.h" 4262306a36Sopenharmony_ci#include "rockchip_rgb.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define VOP_WIN_SET(vop, win, name, v) \ 4562306a36Sopenharmony_ci vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) 4662306a36Sopenharmony_ci#define VOP_SCL_SET(vop, win, name, v) \ 4762306a36Sopenharmony_ci vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name) 4862306a36Sopenharmony_ci#define VOP_SCL_SET_EXT(vop, win, name, v) \ 4962306a36Sopenharmony_ci vop_reg_set(vop, &win->phy->scl->ext->name, \ 5062306a36Sopenharmony_ci win->base, ~0, v, #name) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \ 5362306a36Sopenharmony_ci do { \ 5462306a36Sopenharmony_ci if (win_yuv2yuv && win_yuv2yuv->name.mask) \ 5562306a36Sopenharmony_ci vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \ 5662306a36Sopenharmony_ci } while (0) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \ 5962306a36Sopenharmony_ci do { \ 6062306a36Sopenharmony_ci if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \ 6162306a36Sopenharmony_ci vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \ 6262306a36Sopenharmony_ci } while (0) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define VOP_INTR_SET_MASK(vop, name, mask, v) \ 6562306a36Sopenharmony_ci vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define VOP_REG_SET(vop, group, name, v) \ 6862306a36Sopenharmony_ci vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define VOP_HAS_REG(vop, group, name) \ 7162306a36Sopenharmony_ci (!!(vop->data->group->name.mask)) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define VOP_INTR_SET_TYPE(vop, name, type, v) \ 7462306a36Sopenharmony_ci do { \ 7562306a36Sopenharmony_ci int i, reg = 0, mask = 0; \ 7662306a36Sopenharmony_ci for (i = 0; i < vop->data->intr->nintrs; i++) { \ 7762306a36Sopenharmony_ci if (vop->data->intr->intrs[i] & type) { \ 7862306a36Sopenharmony_ci reg |= (v) << i; \ 7962306a36Sopenharmony_ci mask |= 1 << i; \ 8062306a36Sopenharmony_ci } \ 8162306a36Sopenharmony_ci } \ 8262306a36Sopenharmony_ci VOP_INTR_SET_MASK(vop, name, mask, reg); \ 8362306a36Sopenharmony_ci } while (0) 8462306a36Sopenharmony_ci#define VOP_INTR_GET_TYPE(vop, name, type) \ 8562306a36Sopenharmony_ci vop_get_intr_type(vop, &vop->data->intr->name, type) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define VOP_WIN_GET(vop, win, name) \ 8862306a36Sopenharmony_ci vop_read_reg(vop, win->base, &win->phy->name) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define VOP_WIN_HAS_REG(win, name) \ 9162306a36Sopenharmony_ci (!!(win->phy->name.mask)) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define VOP_WIN_GET_YRGBADDR(vop, win) \ 9462306a36Sopenharmony_ci vop_readl(vop, win->base + win->phy->yrgb_mst.offset) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define VOP_WIN_TO_INDEX(vop_win) \ 9762306a36Sopenharmony_ci ((vop_win) - (vop_win)->vop->win) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define VOP_AFBC_SET(vop, name, v) \ 10062306a36Sopenharmony_ci do { \ 10162306a36Sopenharmony_ci if ((vop)->data->afbc) \ 10262306a36Sopenharmony_ci vop_reg_set((vop), &(vop)->data->afbc->name, \ 10362306a36Sopenharmony_ci 0, ~0, v, #name); \ 10462306a36Sopenharmony_ci } while (0) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define to_vop(x) container_of(x, struct vop, crtc) 10762306a36Sopenharmony_ci#define to_vop_win(x) container_of(x, struct vop_win, base) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define AFBC_FMT_RGB565 0x0 11062306a36Sopenharmony_ci#define AFBC_FMT_U8U8U8U8 0x5 11162306a36Sopenharmony_ci#define AFBC_FMT_U8U8U8 0x4 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define AFBC_TILE_16x16 BIT(4) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * The coefficients of the following matrix are all fixed points. 11762306a36Sopenharmony_ci * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets. 11862306a36Sopenharmony_ci * They are all represented in two's complement. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_cistatic const uint32_t bt601_yuv2rgb[] = { 12162306a36Sopenharmony_ci 0x4A8, 0x0, 0x662, 12262306a36Sopenharmony_ci 0x4A8, 0x1E6F, 0x1CBF, 12362306a36Sopenharmony_ci 0x4A8, 0x812, 0x0, 12462306a36Sopenharmony_ci 0x321168, 0x0877CF, 0x2EB127 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cienum vop_pending { 12862306a36Sopenharmony_ci VOP_PENDING_FB_UNREF, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct vop_win { 13262306a36Sopenharmony_ci struct drm_plane base; 13362306a36Sopenharmony_ci const struct vop_win_data *data; 13462306a36Sopenharmony_ci const struct vop_win_yuv2yuv_data *yuv2yuv_data; 13562306a36Sopenharmony_ci struct vop *vop; 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistruct rockchip_rgb; 13962306a36Sopenharmony_cistruct vop { 14062306a36Sopenharmony_ci struct drm_crtc crtc; 14162306a36Sopenharmony_ci struct device *dev; 14262306a36Sopenharmony_ci struct drm_device *drm_dev; 14362306a36Sopenharmony_ci bool is_enabled; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci struct completion dsp_hold_completion; 14662306a36Sopenharmony_ci unsigned int win_enabled; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* protected by dev->event_lock */ 14962306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci struct drm_flip_work fb_unref_work; 15262306a36Sopenharmony_ci unsigned long pending; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci struct completion line_flag_completion; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci const struct vop_data *data; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci uint32_t *regsbak; 15962306a36Sopenharmony_ci void __iomem *regs; 16062306a36Sopenharmony_ci void __iomem *lut_regs; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* physical map length of vop register */ 16362306a36Sopenharmony_ci uint32_t len; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* one time only one process allowed to config the register */ 16662306a36Sopenharmony_ci spinlock_t reg_lock; 16762306a36Sopenharmony_ci /* lock vop irq reg */ 16862306a36Sopenharmony_ci spinlock_t irq_lock; 16962306a36Sopenharmony_ci /* protects crtc enable/disable */ 17062306a36Sopenharmony_ci struct mutex vop_lock; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci unsigned int irq; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* vop AHP clk */ 17562306a36Sopenharmony_ci struct clk *hclk; 17662306a36Sopenharmony_ci /* vop dclk */ 17762306a36Sopenharmony_ci struct clk *dclk; 17862306a36Sopenharmony_ci /* vop share memory frequency */ 17962306a36Sopenharmony_ci struct clk *aclk; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* vop dclk reset */ 18262306a36Sopenharmony_ci struct reset_control *dclk_rst; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* optional internal rgb encoder */ 18562306a36Sopenharmony_ci struct rockchip_rgb *rgb; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci struct vop_win win[]; 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic inline uint32_t vop_readl(struct vop *vop, uint32_t offset) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci return readl(vop->regs + offset); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic inline uint32_t vop_read_reg(struct vop *vop, uint32_t base, 19662306a36Sopenharmony_ci const struct vop_reg *reg) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci return (vop_readl(vop, base + reg->offset) >> reg->shift) & reg->mask; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void vop_reg_set(struct vop *vop, const struct vop_reg *reg, 20262306a36Sopenharmony_ci uint32_t _offset, uint32_t _mask, uint32_t v, 20362306a36Sopenharmony_ci const char *reg_name) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci int offset, mask, shift; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!reg || !reg->mask) { 20862306a36Sopenharmony_ci DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name); 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci offset = reg->offset + _offset; 21362306a36Sopenharmony_ci mask = reg->mask & _mask; 21462306a36Sopenharmony_ci shift = reg->shift; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (reg->write_mask) { 21762306a36Sopenharmony_ci v = ((v << shift) & 0xffff) | (mask << (shift + 16)); 21862306a36Sopenharmony_ci } else { 21962306a36Sopenharmony_ci uint32_t cached_val = vop->regsbak[offset >> 2]; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci v = (cached_val & ~(mask << shift)) | ((v & mask) << shift); 22262306a36Sopenharmony_ci vop->regsbak[offset >> 2] = v; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (reg->relaxed) 22662306a36Sopenharmony_ci writel_relaxed(v, vop->regs + offset); 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci writel(v, vop->regs + offset); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline uint32_t vop_get_intr_type(struct vop *vop, 23262306a36Sopenharmony_ci const struct vop_reg *reg, int type) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci uint32_t i, ret = 0; 23562306a36Sopenharmony_ci uint32_t regs = vop_read_reg(vop, 0, reg); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (i = 0; i < vop->data->intr->nintrs; i++) { 23862306a36Sopenharmony_ci if ((type & vop->data->intr->intrs[i]) && (regs & 1 << i)) 23962306a36Sopenharmony_ci ret |= vop->data->intr->intrs[i]; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic inline void vop_cfg_done(struct vop *vop) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci VOP_REG_SET(vop, common, cfg_done, 1); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic bool has_rb_swapped(uint32_t version, uint32_t format) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci switch (format) { 25362306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 25462306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 25562306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 25662306a36Sopenharmony_ci return true; 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * full framework (IP version 3.x) only need rb swapped for RGB888 and 25962306a36Sopenharmony_ci * little framework (IP version 2.x) only need rb swapped for BGR888, 26062306a36Sopenharmony_ci * check for 3.x to also only rb swap BGR888 for unknown vop version 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 26362306a36Sopenharmony_ci return VOP_MAJOR(version) == 3; 26462306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 26562306a36Sopenharmony_ci return VOP_MAJOR(version) != 3; 26662306a36Sopenharmony_ci default: 26762306a36Sopenharmony_ci return false; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic bool has_uv_swapped(uint32_t format) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci switch (format) { 27462306a36Sopenharmony_ci case DRM_FORMAT_NV21: 27562306a36Sopenharmony_ci case DRM_FORMAT_NV61: 27662306a36Sopenharmony_ci case DRM_FORMAT_NV42: 27762306a36Sopenharmony_ci return true; 27862306a36Sopenharmony_ci default: 27962306a36Sopenharmony_ci return false; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic enum vop_data_format vop_convert_format(uint32_t format) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci switch (format) { 28662306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 28762306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 28862306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 28962306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 29062306a36Sopenharmony_ci return VOP_FMT_ARGB8888; 29162306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 29262306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 29362306a36Sopenharmony_ci return VOP_FMT_RGB888; 29462306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 29562306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 29662306a36Sopenharmony_ci return VOP_FMT_RGB565; 29762306a36Sopenharmony_ci case DRM_FORMAT_NV12: 29862306a36Sopenharmony_ci case DRM_FORMAT_NV21: 29962306a36Sopenharmony_ci return VOP_FMT_YUV420SP; 30062306a36Sopenharmony_ci case DRM_FORMAT_NV16: 30162306a36Sopenharmony_ci case DRM_FORMAT_NV61: 30262306a36Sopenharmony_ci return VOP_FMT_YUV422SP; 30362306a36Sopenharmony_ci case DRM_FORMAT_NV24: 30462306a36Sopenharmony_ci case DRM_FORMAT_NV42: 30562306a36Sopenharmony_ci return VOP_FMT_YUV444SP; 30662306a36Sopenharmony_ci default: 30762306a36Sopenharmony_ci DRM_ERROR("unsupported format[%08x]\n", format); 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int vop_convert_afbc_format(uint32_t format) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci switch (format) { 31562306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 31662306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 31762306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 31862306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 31962306a36Sopenharmony_ci return AFBC_FMT_U8U8U8U8; 32062306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 32162306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 32262306a36Sopenharmony_ci return AFBC_FMT_U8U8U8; 32362306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 32462306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 32562306a36Sopenharmony_ci return AFBC_FMT_RGB565; 32662306a36Sopenharmony_ci default: 32762306a36Sopenharmony_ci DRM_DEBUG_KMS("unsupported AFBC format[%08x]\n", format); 32862306a36Sopenharmony_ci return -EINVAL; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src, 33362306a36Sopenharmony_ci uint32_t dst, bool is_horizontal, 33462306a36Sopenharmony_ci int vsu_mode, int *vskiplines) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (vskiplines) 33962306a36Sopenharmony_ci *vskiplines = 0; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (is_horizontal) { 34262306a36Sopenharmony_ci if (mode == SCALE_UP) 34362306a36Sopenharmony_ci val = GET_SCL_FT_BIC(src, dst); 34462306a36Sopenharmony_ci else if (mode == SCALE_DOWN) 34562306a36Sopenharmony_ci val = GET_SCL_FT_BILI_DN(src, dst); 34662306a36Sopenharmony_ci } else { 34762306a36Sopenharmony_ci if (mode == SCALE_UP) { 34862306a36Sopenharmony_ci if (vsu_mode == SCALE_UP_BIL) 34962306a36Sopenharmony_ci val = GET_SCL_FT_BILI_UP(src, dst); 35062306a36Sopenharmony_ci else 35162306a36Sopenharmony_ci val = GET_SCL_FT_BIC(src, dst); 35262306a36Sopenharmony_ci } else if (mode == SCALE_DOWN) { 35362306a36Sopenharmony_ci if (vskiplines) { 35462306a36Sopenharmony_ci *vskiplines = scl_get_vskiplines(src, dst); 35562306a36Sopenharmony_ci val = scl_get_bili_dn_vskip(src, dst, 35662306a36Sopenharmony_ci *vskiplines); 35762306a36Sopenharmony_ci } else { 35862306a36Sopenharmony_ci val = GET_SCL_FT_BILI_DN(src, dst); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return val; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, 36762306a36Sopenharmony_ci uint32_t src_w, uint32_t src_h, uint32_t dst_w, 36862306a36Sopenharmony_ci uint32_t dst_h, const struct drm_format_info *info) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode; 37162306a36Sopenharmony_ci uint16_t cbcr_hor_scl_mode = SCALE_NONE; 37262306a36Sopenharmony_ci uint16_t cbcr_ver_scl_mode = SCALE_NONE; 37362306a36Sopenharmony_ci bool is_yuv = false; 37462306a36Sopenharmony_ci uint16_t cbcr_src_w = src_w / info->hsub; 37562306a36Sopenharmony_ci uint16_t cbcr_src_h = src_h / info->vsub; 37662306a36Sopenharmony_ci uint16_t vsu_mode; 37762306a36Sopenharmony_ci uint16_t lb_mode; 37862306a36Sopenharmony_ci uint32_t val; 37962306a36Sopenharmony_ci int vskiplines; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (info->is_yuv) 38262306a36Sopenharmony_ci is_yuv = true; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (dst_w > 3840) { 38562306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n"); 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!win->phy->scl->ext) { 39062306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_x, 39162306a36Sopenharmony_ci scl_cal_scale2(src_w, dst_w)); 39262306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_y, 39362306a36Sopenharmony_ci scl_cal_scale2(src_h, dst_h)); 39462306a36Sopenharmony_ci if (is_yuv) { 39562306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_x, 39662306a36Sopenharmony_ci scl_cal_scale2(cbcr_src_w, dst_w)); 39762306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_y, 39862306a36Sopenharmony_ci scl_cal_scale2(cbcr_src_h, dst_h)); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w); 40462306a36Sopenharmony_ci yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (is_yuv) { 40762306a36Sopenharmony_ci cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w); 40862306a36Sopenharmony_ci cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h); 40962306a36Sopenharmony_ci if (cbcr_hor_scl_mode == SCALE_DOWN) 41062306a36Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(dst_w, true); 41162306a36Sopenharmony_ci else 41262306a36Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true); 41362306a36Sopenharmony_ci } else { 41462306a36Sopenharmony_ci if (yrgb_hor_scl_mode == SCALE_DOWN) 41562306a36Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(dst_w, false); 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(src_w, false); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, lb_mode, lb_mode); 42162306a36Sopenharmony_ci if (lb_mode == LB_RGB_3840X2) { 42262306a36Sopenharmony_ci if (yrgb_ver_scl_mode != SCALE_NONE) { 42362306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "not allow yrgb ver scale\n"); 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci if (cbcr_ver_scl_mode != SCALE_NONE) { 42762306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "not allow cbcr ver scale\n"); 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci vsu_mode = SCALE_UP_BIL; 43162306a36Sopenharmony_ci } else if (lb_mode == LB_RGB_2560X4) { 43262306a36Sopenharmony_ci vsu_mode = SCALE_UP_BIL; 43362306a36Sopenharmony_ci } else { 43462306a36Sopenharmony_ci vsu_mode = SCALE_UP_BIC; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w, 43862306a36Sopenharmony_ci true, 0, NULL); 43962306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_x, val); 44062306a36Sopenharmony_ci val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h, 44162306a36Sopenharmony_ci false, vsu_mode, &vskiplines); 44262306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_y, val); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt4, vskiplines == 4); 44562306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt2, vskiplines == 2); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, yrgb_hor_scl_mode); 44862306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, yrgb_ver_scl_mode); 44962306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_hsd_mode, SCALE_DOWN_BIL); 45062306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_vsd_mode, SCALE_DOWN_BIL); 45162306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_vsu_mode, vsu_mode); 45262306a36Sopenharmony_ci if (is_yuv) { 45362306a36Sopenharmony_ci val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w, 45462306a36Sopenharmony_ci dst_w, true, 0, NULL); 45562306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_x, val); 45662306a36Sopenharmony_ci val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h, 45762306a36Sopenharmony_ci dst_h, false, vsu_mode, &vskiplines); 45862306a36Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_y, val); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt4, vskiplines == 4); 46162306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt2, vskiplines == 2); 46262306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, cbcr_hor_scl_mode); 46362306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, cbcr_ver_scl_mode); 46462306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_hsd_mode, SCALE_DOWN_BIL); 46562306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_vsd_mode, SCALE_DOWN_BIL); 46662306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_vsu_mode, vsu_mode); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void vop_dsp_hold_valid_irq_enable(struct vop *vop) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci unsigned long flags; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 47562306a36Sopenharmony_ci return; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1); 48062306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void vop_dsp_hold_valid_irq_disable(struct vop *vop) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci unsigned long flags; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 49062306a36Sopenharmony_ci return; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 0); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* 50062306a36Sopenharmony_ci * (1) each frame starts at the start of the Vsync pulse which is signaled by 50162306a36Sopenharmony_ci * the "FRAME_SYNC" interrupt. 50262306a36Sopenharmony_ci * (2) the active data region of each frame ends at dsp_vact_end 50362306a36Sopenharmony_ci * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num, 50462306a36Sopenharmony_ci * to get "LINE_FLAG" interrupt at the end of the active on screen data. 50562306a36Sopenharmony_ci * 50662306a36Sopenharmony_ci * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end 50762306a36Sopenharmony_ci * Interrupts 50862306a36Sopenharmony_ci * LINE_FLAG -------------------------------+ 50962306a36Sopenharmony_ci * FRAME_SYNC ----+ | 51062306a36Sopenharmony_ci * | | 51162306a36Sopenharmony_ci * v v 51262306a36Sopenharmony_ci * | Vsync | Vbp | Vactive | Vfp | 51362306a36Sopenharmony_ci * ^ ^ ^ ^ 51462306a36Sopenharmony_ci * | | | | 51562306a36Sopenharmony_ci * | | | | 51662306a36Sopenharmony_ci * dsp_vs_end ------------+ | | | VOP_DSP_VTOTAL_VS_END 51762306a36Sopenharmony_ci * dsp_vact_start --------------+ | | VOP_DSP_VACT_ST_END 51862306a36Sopenharmony_ci * dsp_vact_end ----------------------------+ | VOP_DSP_VACT_ST_END 51962306a36Sopenharmony_ci * dsp_total -------------------------------------+ VOP_DSP_VTOTAL_VS_END 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistatic bool vop_line_flag_irq_is_enabled(struct vop *vop) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci uint32_t line_flag_irq; 52462306a36Sopenharmony_ci unsigned long flags; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return !!line_flag_irq; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void vop_line_flag_irq_enable(struct vop *vop) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci unsigned long flags; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 54062306a36Sopenharmony_ci return; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1); 54562306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void vop_line_flag_irq_disable(struct vop *vop) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci unsigned long flags; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int vop_core_clks_enable(struct vop *vop) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci int ret; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ret = clk_enable(vop->hclk); 56962306a36Sopenharmony_ci if (ret < 0) 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = clk_enable(vop->aclk); 57362306a36Sopenharmony_ci if (ret < 0) 57462306a36Sopenharmony_ci goto err_disable_hclk; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cierr_disable_hclk: 57962306a36Sopenharmony_ci clk_disable(vop->hclk); 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic void vop_core_clks_disable(struct vop *vop) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci clk_disable(vop->aclk); 58662306a36Sopenharmony_ci clk_disable(vop->hclk); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic void vop_win_disable(struct vop *vop, const struct vop_win *vop_win) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (win->phy->scl && win->phy->scl->ext) { 59462306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE); 59562306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE); 59662306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE); 59762306a36Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci VOP_WIN_SET(vop, win, enable, 0); 60162306a36Sopenharmony_ci vop->win_enabled &= ~BIT(VOP_WIN_TO_INDEX(vop_win)); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 60762306a36Sopenharmony_ci int ret, i; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(vop->dev); 61062306a36Sopenharmony_ci if (ret < 0) { 61162306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); 61262306a36Sopenharmony_ci return ret; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ret = vop_core_clks_enable(vop); 61662306a36Sopenharmony_ci if (WARN_ON(ret < 0)) 61762306a36Sopenharmony_ci goto err_put_pm_runtime; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci ret = clk_enable(vop->dclk); 62062306a36Sopenharmony_ci if (WARN_ON(ret < 0)) 62162306a36Sopenharmony_ci goto err_disable_core; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* 62462306a36Sopenharmony_ci * Slave iommu shares power, irq and clock with vop. It was associated 62562306a36Sopenharmony_ci * automatically with this master device via common driver code. 62662306a36Sopenharmony_ci * Now that we have enabled the clock we attach it to the shared drm 62762306a36Sopenharmony_ci * mapping. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev); 63062306a36Sopenharmony_ci if (ret) { 63162306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, 63262306a36Sopenharmony_ci "failed to attach dma mapping, %d\n", ret); 63362306a36Sopenharmony_ci goto err_disable_dclk; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 63762306a36Sopenharmony_ci for (i = 0; i < vop->len; i += 4) 63862306a36Sopenharmony_ci writel_relaxed(vop->regsbak[i / 4], vop->regs + i); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * We need to make sure that all windows are disabled before we 64262306a36Sopenharmony_ci * enable the crtc. Otherwise we might try to scan from a destroyed 64362306a36Sopenharmony_ci * buffer later. 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * In the case of enable-after-PSR, we don't need to worry about this 64662306a36Sopenharmony_ci * case since the buffer is guaranteed to be valid and disabling the 64762306a36Sopenharmony_ci * window will result in screen glitches on PSR exit. 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci if (!old_state || !old_state->self_refresh_active) { 65062306a36Sopenharmony_ci for (i = 0; i < vop->data->win_size; i++) { 65162306a36Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci vop_win_disable(vop, vop_win); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (vop->data->afbc) { 65862306a36Sopenharmony_ci struct rockchip_crtc_state *s; 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * Disable AFBC and forget there was a vop window with AFBC 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ci VOP_AFBC_SET(vop, enable, 0); 66362306a36Sopenharmony_ci s = to_rockchip_crtc_state(crtc->state); 66462306a36Sopenharmony_ci s->enable_afbc = false; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci vop_cfg_done(vop); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * At here, vop clock & iommu is enable, R/W vop regs would be safe. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci vop->is_enabled = true; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci VOP_REG_SET(vop, common, standby, 1); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cierr_disable_dclk: 68762306a36Sopenharmony_ci clk_disable(vop->dclk); 68862306a36Sopenharmony_cierr_disable_core: 68962306a36Sopenharmony_ci vop_core_clks_disable(vop); 69062306a36Sopenharmony_cierr_put_pm_runtime: 69162306a36Sopenharmony_ci pm_runtime_put_sync(vop->dev); 69262306a36Sopenharmony_ci return ret; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic void rockchip_drm_set_win_enabled(struct drm_crtc *crtc, bool enabled) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 69862306a36Sopenharmony_ci int i; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci for (i = 0; i < vop->data->win_size; i++) { 70362306a36Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 70462306a36Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci VOP_WIN_SET(vop, win, enable, 70762306a36Sopenharmony_ci enabled && (vop->win_enabled & BIT(i))); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci vop_cfg_done(vop); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic void vop_crtc_atomic_disable(struct drm_crtc *crtc, 71562306a36Sopenharmony_ci struct drm_atomic_state *state) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci WARN_ON(vop->event); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (crtc->state->self_refresh_active) 72262306a36Sopenharmony_ci rockchip_drm_set_win_enabled(crtc, false); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (crtc->state->self_refresh_active) 72562306a36Sopenharmony_ci goto out; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci mutex_lock(&vop->vop_lock); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Vop standby will take effect at end of current frame, 73362306a36Sopenharmony_ci * if dsp hold valid irq happen, it means standby complete. 73462306a36Sopenharmony_ci * 73562306a36Sopenharmony_ci * we must wait standby complete when we want to disable aclk, 73662306a36Sopenharmony_ci * if not, memory bus maybe dead. 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_ci reinit_completion(&vop->dsp_hold_completion); 73962306a36Sopenharmony_ci vop_dsp_hold_valid_irq_enable(vop); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci VOP_REG_SET(vop, common, standby, 1); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (!wait_for_completion_timeout(&vop->dsp_hold_completion, 74862306a36Sopenharmony_ci msecs_to_jiffies(200))) 74962306a36Sopenharmony_ci WARN(1, "%s: timed out waiting for DSP hold", crtc->name); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci vop_dsp_hold_valid_irq_disable(vop); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci vop->is_enabled = false; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* 75662306a36Sopenharmony_ci * vop standby complete, so iommu detach is safe. 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_ci rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci clk_disable(vop->dclk); 76162306a36Sopenharmony_ci vop_core_clks_disable(vop); 76262306a36Sopenharmony_ci pm_runtime_put(vop->dev); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci mutex_unlock(&vop->vop_lock); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciout: 76762306a36Sopenharmony_ci if (crtc->state->event && !crtc->state->active) { 76862306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 76962306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 77062306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci crtc->state->event = NULL; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic void vop_plane_destroy(struct drm_plane *plane) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci drm_plane_cleanup(plane); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic inline bool rockchip_afbc(u64 modifier) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci return modifier == ROCKCHIP_AFBC_MOD; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic bool rockchip_mod_supported(struct drm_plane *plane, 78762306a36Sopenharmony_ci u32 format, u64 modifier) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_LINEAR) 79062306a36Sopenharmony_ci return true; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (!rockchip_afbc(modifier)) { 79362306a36Sopenharmony_ci DRM_DEBUG_KMS("Unsupported format modifier 0x%llx\n", modifier); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return false; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return vop_convert_afbc_format(format) >= 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic int vop_plane_atomic_check(struct drm_plane *plane, 80262306a36Sopenharmony_ci struct drm_atomic_state *state) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 80562306a36Sopenharmony_ci plane); 80662306a36Sopenharmony_ci struct drm_crtc *crtc = new_plane_state->crtc; 80762306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 80862306a36Sopenharmony_ci struct drm_framebuffer *fb = new_plane_state->fb; 80962306a36Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 81062306a36Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 81162306a36Sopenharmony_ci int ret; 81262306a36Sopenharmony_ci int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : 81362306a36Sopenharmony_ci DRM_PLANE_NO_SCALING; 81462306a36Sopenharmony_ci int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : 81562306a36Sopenharmony_ci DRM_PLANE_NO_SCALING; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (!crtc || WARN_ON(!fb)) 81862306a36Sopenharmony_ci return 0; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state, 82162306a36Sopenharmony_ci crtc); 82262306a36Sopenharmony_ci if (WARN_ON(!crtc_state)) 82362306a36Sopenharmony_ci return -EINVAL; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 82662306a36Sopenharmony_ci min_scale, max_scale, 82762306a36Sopenharmony_ci true, true); 82862306a36Sopenharmony_ci if (ret) 82962306a36Sopenharmony_ci return ret; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (!new_plane_state->visible) 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ret = vop_convert_format(fb->format->format); 83562306a36Sopenharmony_ci if (ret < 0) 83662306a36Sopenharmony_ci return ret; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * Src.x1 can be odd when do clip, but yuv plane start point 84062306a36Sopenharmony_ci * need align with 2 pixel. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci if (fb->format->is_yuv && ((new_plane_state->src.x1 >> 16) % 2)) { 84362306a36Sopenharmony_ci DRM_DEBUG_KMS("Invalid Source: Yuv format not support odd xpos\n"); 84462306a36Sopenharmony_ci return -EINVAL; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (fb->format->is_yuv && new_plane_state->rotation & DRM_MODE_REFLECT_Y) { 84862306a36Sopenharmony_ci DRM_DEBUG_KMS("Invalid Source: Yuv format does not support this rotation\n"); 84962306a36Sopenharmony_ci return -EINVAL; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (rockchip_afbc(fb->modifier)) { 85362306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (!vop->data->afbc) { 85662306a36Sopenharmony_ci DRM_DEBUG_KMS("vop does not support AFBC\n"); 85762306a36Sopenharmony_ci return -EINVAL; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci ret = vop_convert_afbc_format(fb->format->format); 86162306a36Sopenharmony_ci if (ret < 0) 86262306a36Sopenharmony_ci return ret; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (new_plane_state->src.x1 || new_plane_state->src.y1) { 86562306a36Sopenharmony_ci DRM_DEBUG_KMS("AFBC does not support offset display, " \ 86662306a36Sopenharmony_ci "xpos=%d, ypos=%d, offset=%d\n", 86762306a36Sopenharmony_ci new_plane_state->src.x1, new_plane_state->src.y1, 86862306a36Sopenharmony_ci fb->offsets[0]); 86962306a36Sopenharmony_ci return -EINVAL; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (new_plane_state->rotation && new_plane_state->rotation != DRM_MODE_ROTATE_0) { 87362306a36Sopenharmony_ci DRM_DEBUG_KMS("No rotation support in AFBC, rotation=%d\n", 87462306a36Sopenharmony_ci new_plane_state->rotation); 87562306a36Sopenharmony_ci return -EINVAL; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic void vop_plane_atomic_disable(struct drm_plane *plane, 88362306a36Sopenharmony_ci struct drm_atomic_state *state) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 88662306a36Sopenharmony_ci plane); 88762306a36Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 88862306a36Sopenharmony_ci struct vop *vop = to_vop(old_state->crtc); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (!old_state->crtc) 89162306a36Sopenharmony_ci return; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci vop_win_disable(vop, vop_win); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic void vop_plane_atomic_update(struct drm_plane *plane, 90162306a36Sopenharmony_ci struct drm_atomic_state *state) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 90462306a36Sopenharmony_ci plane); 90562306a36Sopenharmony_ci struct drm_crtc *crtc = new_state->crtc; 90662306a36Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 90762306a36Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 90862306a36Sopenharmony_ci const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data; 90962306a36Sopenharmony_ci struct vop *vop = to_vop(new_state->crtc); 91062306a36Sopenharmony_ci struct drm_framebuffer *fb = new_state->fb; 91162306a36Sopenharmony_ci unsigned int actual_w, actual_h; 91262306a36Sopenharmony_ci unsigned int dsp_stx, dsp_sty; 91362306a36Sopenharmony_ci uint32_t act_info, dsp_info, dsp_st; 91462306a36Sopenharmony_ci struct drm_rect *src = &new_state->src; 91562306a36Sopenharmony_ci struct drm_rect *dest = &new_state->dst; 91662306a36Sopenharmony_ci struct drm_gem_object *obj, *uv_obj; 91762306a36Sopenharmony_ci struct rockchip_gem_object *rk_obj, *rk_uv_obj; 91862306a36Sopenharmony_ci unsigned long offset; 91962306a36Sopenharmony_ci dma_addr_t dma_addr; 92062306a36Sopenharmony_ci uint32_t val; 92162306a36Sopenharmony_ci bool rb_swap, uv_swap; 92262306a36Sopenharmony_ci int win_index = VOP_WIN_TO_INDEX(vop_win); 92362306a36Sopenharmony_ci int format; 92462306a36Sopenharmony_ci int is_yuv = fb->format->is_yuv; 92562306a36Sopenharmony_ci int i; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* 92862306a36Sopenharmony_ci * can't update plane when vop is disabled. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_ci if (WARN_ON(!crtc)) 93162306a36Sopenharmony_ci return; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 93462306a36Sopenharmony_ci return; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (!new_state->visible) { 93762306a36Sopenharmony_ci vop_plane_atomic_disable(plane, state); 93862306a36Sopenharmony_ci return; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci obj = fb->obj[0]; 94262306a36Sopenharmony_ci rk_obj = to_rockchip_obj(obj); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci actual_w = drm_rect_width(src) >> 16; 94562306a36Sopenharmony_ci actual_h = drm_rect_height(src) >> 16; 94662306a36Sopenharmony_ci act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci dsp_info = (drm_rect_height(dest) - 1) << 16; 94962306a36Sopenharmony_ci dsp_info |= (drm_rect_width(dest) - 1) & 0xffff; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci dsp_stx = dest->x1 + crtc->mode.htotal - crtc->mode.hsync_start; 95262306a36Sopenharmony_ci dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start; 95362306a36Sopenharmony_ci dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci offset = (src->x1 >> 16) * fb->format->cpp[0]; 95662306a36Sopenharmony_ci offset += (src->y1 >> 16) * fb->pitches[0]; 95762306a36Sopenharmony_ci dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* 96062306a36Sopenharmony_ci * For y-mirroring we need to move address 96162306a36Sopenharmony_ci * to the beginning of the last line. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci if (new_state->rotation & DRM_MODE_REFLECT_Y) 96462306a36Sopenharmony_ci dma_addr += (actual_h - 1) * fb->pitches[0]; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci format = vop_convert_format(fb->format->format); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (rockchip_afbc(fb->modifier)) { 97162306a36Sopenharmony_ci int afbc_format = vop_convert_afbc_format(fb->format->format); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16); 97462306a36Sopenharmony_ci VOP_AFBC_SET(vop, hreg_block_split, 0); 97562306a36Sopenharmony_ci VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win)); 97662306a36Sopenharmony_ci VOP_AFBC_SET(vop, hdr_ptr, dma_addr); 97762306a36Sopenharmony_ci VOP_AFBC_SET(vop, pic_size, act_info); 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci VOP_WIN_SET(vop, win, format, format); 98162306a36Sopenharmony_ci VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); 98262306a36Sopenharmony_ci VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); 98362306a36Sopenharmony_ci VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv); 98462306a36Sopenharmony_ci VOP_WIN_SET(vop, win, y_mir_en, 98562306a36Sopenharmony_ci (new_state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0); 98662306a36Sopenharmony_ci VOP_WIN_SET(vop, win, x_mir_en, 98762306a36Sopenharmony_ci (new_state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (is_yuv) { 99062306a36Sopenharmony_ci int hsub = fb->format->hsub; 99162306a36Sopenharmony_ci int vsub = fb->format->vsub; 99262306a36Sopenharmony_ci int bpp = fb->format->cpp[1]; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci uv_obj = fb->obj[1]; 99562306a36Sopenharmony_ci rk_uv_obj = to_rockchip_obj(uv_obj); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci offset = (src->x1 >> 16) * bpp / hsub; 99862306a36Sopenharmony_ci offset += (src->y1 >> 16) * fb->pitches[1] / vsub; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; 100162306a36Sopenharmony_ci VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); 100262306a36Sopenharmony_ci VOP_WIN_SET(vop, win, uv_mst, dma_addr); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) { 100562306a36Sopenharmony_ci VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, 100662306a36Sopenharmony_ci win_yuv2yuv, 100762306a36Sopenharmony_ci y2r_coefficients[i], 100862306a36Sopenharmony_ci bt601_yuv2rgb[i]); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci uv_swap = has_uv_swapped(fb->format->format); 101262306a36Sopenharmony_ci VOP_WIN_SET(vop, win, uv_swap, uv_swap); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (win->phy->scl) 101662306a36Sopenharmony_ci scl_vop_cal_scl_fac(vop, win, actual_w, actual_h, 101762306a36Sopenharmony_ci drm_rect_width(dest), drm_rect_height(dest), 101862306a36Sopenharmony_ci fb->format); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci VOP_WIN_SET(vop, win, act_info, act_info); 102162306a36Sopenharmony_ci VOP_WIN_SET(vop, win, dsp_info, dsp_info); 102262306a36Sopenharmony_ci VOP_WIN_SET(vop, win, dsp_st, dsp_st); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci rb_swap = has_rb_swapped(vop->data->version, fb->format->format); 102562306a36Sopenharmony_ci VOP_WIN_SET(vop, win, rb_swap, rb_swap); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* 102862306a36Sopenharmony_ci * Blending win0 with the background color doesn't seem to work 102962306a36Sopenharmony_ci * correctly. We only get the background color, no matter the contents 103062306a36Sopenharmony_ci * of the win0 framebuffer. However, blending pre-multiplied color 103162306a36Sopenharmony_ci * with the default opaque black default background color is a no-op, 103262306a36Sopenharmony_ci * so we can just disable blending to get the correct result. 103362306a36Sopenharmony_ci */ 103462306a36Sopenharmony_ci if (fb->format->has_alpha && win_index > 0) { 103562306a36Sopenharmony_ci VOP_WIN_SET(vop, win, dst_alpha_ctl, 103662306a36Sopenharmony_ci DST_FACTOR_M0(ALPHA_SRC_INVERSE)); 103762306a36Sopenharmony_ci val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) | 103862306a36Sopenharmony_ci SRC_ALPHA_M0(ALPHA_STRAIGHT) | 103962306a36Sopenharmony_ci SRC_BLEND_M0(ALPHA_PER_PIX) | 104062306a36Sopenharmony_ci SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) | 104162306a36Sopenharmony_ci SRC_FACTOR_M0(ALPHA_ONE); 104262306a36Sopenharmony_ci VOP_WIN_SET(vop, win, src_alpha_ctl, val); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_pre_mul, ALPHA_SRC_PRE_MUL); 104562306a36Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_mode, ALPHA_PER_PIX); 104662306a36Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_en, 1); 104762306a36Sopenharmony_ci } else { 104862306a36Sopenharmony_ci VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0)); 104962306a36Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_en, 0); 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci VOP_WIN_SET(vop, win, enable, 1); 105362306a36Sopenharmony_ci vop->win_enabled |= BIT(win_index); 105462306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic int vop_plane_atomic_async_check(struct drm_plane *plane, 105862306a36Sopenharmony_ci struct drm_atomic_state *state) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 106162306a36Sopenharmony_ci plane); 106262306a36Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 106362306a36Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 106462306a36Sopenharmony_ci int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : 106562306a36Sopenharmony_ci DRM_PLANE_NO_SCALING; 106662306a36Sopenharmony_ci int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : 106762306a36Sopenharmony_ci DRM_PLANE_NO_SCALING; 106862306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (plane != new_plane_state->crtc->cursor) 107162306a36Sopenharmony_ci return -EINVAL; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (!plane->state) 107462306a36Sopenharmony_ci return -EINVAL; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (!plane->state->fb) 107762306a36Sopenharmony_ci return -EINVAL; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (state) 108062306a36Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state, 108162306a36Sopenharmony_ci new_plane_state->crtc); 108262306a36Sopenharmony_ci else /* Special case for asynchronous cursor updates. */ 108362306a36Sopenharmony_ci crtc_state = plane->crtc->state; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return drm_atomic_helper_check_plane_state(plane->state, crtc_state, 108662306a36Sopenharmony_ci min_scale, max_scale, 108762306a36Sopenharmony_ci true, true); 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic void vop_plane_atomic_async_update(struct drm_plane *plane, 109162306a36Sopenharmony_ci struct drm_atomic_state *state) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 109462306a36Sopenharmony_ci plane); 109562306a36Sopenharmony_ci struct vop *vop = to_vop(plane->state->crtc); 109662306a36Sopenharmony_ci struct drm_framebuffer *old_fb = plane->state->fb; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci plane->state->crtc_x = new_state->crtc_x; 109962306a36Sopenharmony_ci plane->state->crtc_y = new_state->crtc_y; 110062306a36Sopenharmony_ci plane->state->crtc_h = new_state->crtc_h; 110162306a36Sopenharmony_ci plane->state->crtc_w = new_state->crtc_w; 110262306a36Sopenharmony_ci plane->state->src_x = new_state->src_x; 110362306a36Sopenharmony_ci plane->state->src_y = new_state->src_y; 110462306a36Sopenharmony_ci plane->state->src_h = new_state->src_h; 110562306a36Sopenharmony_ci plane->state->src_w = new_state->src_w; 110662306a36Sopenharmony_ci swap(plane->state->fb, new_state->fb); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (vop->is_enabled) { 110962306a36Sopenharmony_ci vop_plane_atomic_update(plane, state); 111062306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 111162306a36Sopenharmony_ci vop_cfg_done(vop); 111262306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* 111562306a36Sopenharmony_ci * A scanout can still be occurring, so we can't drop the 111662306a36Sopenharmony_ci * reference to the old framebuffer. To solve this we get a 111762306a36Sopenharmony_ci * reference to old_fb and set a worker to release it later. 111862306a36Sopenharmony_ci * FIXME: if we perform 500 async_update calls before the 111962306a36Sopenharmony_ci * vblank, then we can have 500 different framebuffers waiting 112062306a36Sopenharmony_ci * to be released. 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci if (old_fb && plane->state->fb != old_fb) { 112362306a36Sopenharmony_ci drm_framebuffer_get(old_fb); 112462306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0); 112562306a36Sopenharmony_ci drm_flip_work_queue(&vop->fb_unref_work, old_fb); 112662306a36Sopenharmony_ci set_bit(VOP_PENDING_FB_UNREF, &vop->pending); 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs plane_helper_funcs = { 113262306a36Sopenharmony_ci .atomic_check = vop_plane_atomic_check, 113362306a36Sopenharmony_ci .atomic_update = vop_plane_atomic_update, 113462306a36Sopenharmony_ci .atomic_disable = vop_plane_atomic_disable, 113562306a36Sopenharmony_ci .atomic_async_check = vop_plane_atomic_async_check, 113662306a36Sopenharmony_ci .atomic_async_update = vop_plane_atomic_async_update, 113762306a36Sopenharmony_ci}; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic const struct drm_plane_funcs vop_plane_funcs = { 114062306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 114162306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 114262306a36Sopenharmony_ci .destroy = vop_plane_destroy, 114362306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 114462306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 114562306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 114662306a36Sopenharmony_ci .format_mod_supported = rockchip_mod_supported, 114762306a36Sopenharmony_ci}; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic int vop_crtc_enable_vblank(struct drm_crtc *crtc) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 115262306a36Sopenharmony_ci unsigned long flags; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 115562306a36Sopenharmony_ci return -EPERM; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1); 116062306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return 0; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic void vop_crtc_disable_vblank(struct drm_crtc *crtc) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 117062306a36Sopenharmony_ci unsigned long flags; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 117362306a36Sopenharmony_ci return; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc, 118362306a36Sopenharmony_ci const struct drm_display_mode *mode) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (vop->data->max_output.width && mode->hdisplay > vop->data->max_output.width) 118862306a36Sopenharmony_ci return MODE_BAD_HVALUE; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci return MODE_OK; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic bool vop_crtc_mode_fixup(struct drm_crtc *crtc, 119462306a36Sopenharmony_ci const struct drm_display_mode *mode, 119562306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 119862306a36Sopenharmony_ci unsigned long rate; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci /* 120162306a36Sopenharmony_ci * Clock craziness. 120262306a36Sopenharmony_ci * 120362306a36Sopenharmony_ci * Key points: 120462306a36Sopenharmony_ci * 120562306a36Sopenharmony_ci * - DRM works in kHz. 120662306a36Sopenharmony_ci * - Clock framework works in Hz. 120762306a36Sopenharmony_ci * - Rockchip's clock driver picks the clock rate that is the 120862306a36Sopenharmony_ci * same _OR LOWER_ than the one requested. 120962306a36Sopenharmony_ci * 121062306a36Sopenharmony_ci * Action plan: 121162306a36Sopenharmony_ci * 121262306a36Sopenharmony_ci * 1. Try to set the exact rate first, and confirm the clock framework 121362306a36Sopenharmony_ci * can provide it. 121462306a36Sopenharmony_ci * 121562306a36Sopenharmony_ci * 2. If the clock framework cannot provide the exact rate, we should 121662306a36Sopenharmony_ci * add 999 Hz to the requested rate. That way if the clock we need 121762306a36Sopenharmony_ci * is 60000001 Hz (~60 MHz) and DRM tells us to make 60000 kHz then 121862306a36Sopenharmony_ci * the clock framework will actually give us the right clock. 121962306a36Sopenharmony_ci * 122062306a36Sopenharmony_ci * 3. Get the clock framework to round the rate for us to tell us 122162306a36Sopenharmony_ci * what it will actually make. 122262306a36Sopenharmony_ci * 122362306a36Sopenharmony_ci * 4. Store the rounded up rate so that we don't need to worry about 122462306a36Sopenharmony_ci * this in the actual clk_set_rate(). 122562306a36Sopenharmony_ci */ 122662306a36Sopenharmony_ci rate = clk_round_rate(vop->dclk, adjusted_mode->clock * 1000); 122762306a36Sopenharmony_ci if (rate / 1000 != adjusted_mode->clock) 122862306a36Sopenharmony_ci rate = clk_round_rate(vop->dclk, 122962306a36Sopenharmony_ci adjusted_mode->clock * 1000 + 999); 123062306a36Sopenharmony_ci adjusted_mode->clock = DIV_ROUND_UP(rate, 1000); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci return true; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic bool vop_dsp_lut_is_enabled(struct vop *vop) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en); 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic u32 vop_lut_buffer_index(struct vop *vop) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci return vop_read_reg(vop, 0, &vop->data->common->lut_buffer_index); 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct drm_color_lut *lut = crtc->state->gamma_lut->data; 124862306a36Sopenharmony_ci unsigned int i, bpc = ilog2(vop->data->lut_size); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci for (i = 0; i < crtc->gamma_size; i++) { 125162306a36Sopenharmony_ci u32 word; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci word = (drm_color_lut_extract(lut[i].red, bpc) << (2 * bpc)) | 125462306a36Sopenharmony_ci (drm_color_lut_extract(lut[i].green, bpc) << bpc) | 125562306a36Sopenharmony_ci drm_color_lut_extract(lut[i].blue, bpc); 125662306a36Sopenharmony_ci writel(word, vop->lut_regs + i * 4); 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc, 126162306a36Sopenharmony_ci struct drm_crtc_state *old_state) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct drm_crtc_state *state = crtc->state; 126462306a36Sopenharmony_ci unsigned int idle; 126562306a36Sopenharmony_ci u32 lut_idx, old_idx; 126662306a36Sopenharmony_ci int ret; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (!vop->lut_regs) 126962306a36Sopenharmony_ci return; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (!state->gamma_lut || !VOP_HAS_REG(vop, common, update_gamma_lut)) { 127262306a36Sopenharmony_ci /* 127362306a36Sopenharmony_ci * To disable gamma (gamma_lut is null) or to write 127462306a36Sopenharmony_ci * an update to the LUT, clear dsp_lut_en. 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 127762306a36Sopenharmony_ci VOP_REG_SET(vop, common, dsp_lut_en, 0); 127862306a36Sopenharmony_ci vop_cfg_done(vop); 127962306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci /* 128262306a36Sopenharmony_ci * In order to write the LUT to the internal memory, 128362306a36Sopenharmony_ci * we need to first make sure the dsp_lut_en bit is cleared. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_ci ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop, 128662306a36Sopenharmony_ci idle, !idle, 5, 30 * 1000); 128762306a36Sopenharmony_ci if (ret) { 128862306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n"); 128962306a36Sopenharmony_ci return; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (!state->gamma_lut) 129362306a36Sopenharmony_ci return; 129462306a36Sopenharmony_ci } else { 129562306a36Sopenharmony_ci /* 129662306a36Sopenharmony_ci * On RK3399 the gamma LUT can updated without clearing dsp_lut_en, 129762306a36Sopenharmony_ci * by setting update_gamma_lut then waiting for lut_buffer_index change 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_ci old_idx = vop_lut_buffer_index(vop); 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 130362306a36Sopenharmony_ci vop_crtc_write_gamma_lut(vop, crtc); 130462306a36Sopenharmony_ci VOP_REG_SET(vop, common, dsp_lut_en, 1); 130562306a36Sopenharmony_ci VOP_REG_SET(vop, common, update_gamma_lut, 1); 130662306a36Sopenharmony_ci vop_cfg_done(vop); 130762306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (VOP_HAS_REG(vop, common, update_gamma_lut)) { 131062306a36Sopenharmony_ci ret = readx_poll_timeout(vop_lut_buffer_index, vop, 131162306a36Sopenharmony_ci lut_idx, lut_idx != old_idx, 5, 30 * 1000); 131262306a36Sopenharmony_ci if (ret) { 131362306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "gamma LUT update timeout!\n"); 131462306a36Sopenharmony_ci return; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* 131862306a36Sopenharmony_ci * update_gamma_lut is auto cleared by HW, but write 0 to clear the bit 131962306a36Sopenharmony_ci * in our backup of the regs. 132062306a36Sopenharmony_ci */ 132162306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 132262306a36Sopenharmony_ci VOP_REG_SET(vop, common, update_gamma_lut, 0); 132362306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic void vop_crtc_atomic_begin(struct drm_crtc *crtc, 132862306a36Sopenharmony_ci struct drm_atomic_state *state) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 133162306a36Sopenharmony_ci crtc); 133262306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, 133362306a36Sopenharmony_ci crtc); 133462306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci /* 133762306a36Sopenharmony_ci * Only update GAMMA if the 'active' flag is not changed, 133862306a36Sopenharmony_ci * otherwise it's updated by .atomic_enable. 133962306a36Sopenharmony_ci */ 134062306a36Sopenharmony_ci if (crtc_state->color_mgmt_changed && 134162306a36Sopenharmony_ci !crtc_state->active_changed) 134262306a36Sopenharmony_ci vop_crtc_gamma_set(vop, crtc, old_crtc_state); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic void vop_crtc_atomic_enable(struct drm_crtc *crtc, 134662306a36Sopenharmony_ci struct drm_atomic_state *state) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, 134962306a36Sopenharmony_ci crtc); 135062306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 135162306a36Sopenharmony_ci const struct vop_data *vop_data = vop->data; 135262306a36Sopenharmony_ci struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); 135362306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; 135462306a36Sopenharmony_ci u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start; 135562306a36Sopenharmony_ci u16 hdisplay = adjusted_mode->hdisplay; 135662306a36Sopenharmony_ci u16 htotal = adjusted_mode->htotal; 135762306a36Sopenharmony_ci u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start; 135862306a36Sopenharmony_ci u16 hact_end = hact_st + hdisplay; 135962306a36Sopenharmony_ci u16 vdisplay = adjusted_mode->vdisplay; 136062306a36Sopenharmony_ci u16 vtotal = adjusted_mode->vtotal; 136162306a36Sopenharmony_ci u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start; 136262306a36Sopenharmony_ci u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start; 136362306a36Sopenharmony_ci u16 vact_end = vact_st + vdisplay; 136462306a36Sopenharmony_ci uint32_t pin_pol, val; 136562306a36Sopenharmony_ci int dither_bpc = s->output_bpc ? s->output_bpc : 10; 136662306a36Sopenharmony_ci int ret; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci if (old_state && old_state->self_refresh_active) { 136962306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 137062306a36Sopenharmony_ci rockchip_drm_set_win_enabled(crtc, true); 137162306a36Sopenharmony_ci return; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci mutex_lock(&vop->vop_lock); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci WARN_ON(vop->event); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci ret = vop_enable(crtc, old_state); 137962306a36Sopenharmony_ci if (ret) { 138062306a36Sopenharmony_ci mutex_unlock(&vop->vop_lock); 138162306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret); 138262306a36Sopenharmony_ci return; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci pin_pol = (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ? 138562306a36Sopenharmony_ci BIT(HSYNC_POSITIVE) : 0; 138662306a36Sopenharmony_ci pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ? 138762306a36Sopenharmony_ci BIT(VSYNC_POSITIVE) : 0; 138862306a36Sopenharmony_ci VOP_REG_SET(vop, output, pin_pol, pin_pol); 138962306a36Sopenharmony_ci VOP_REG_SET(vop, output, mipi_dual_channel_en, 0); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci switch (s->output_type) { 139262306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_LVDS: 139362306a36Sopenharmony_ci VOP_REG_SET(vop, output, rgb_dclk_pol, 1); 139462306a36Sopenharmony_ci VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol); 139562306a36Sopenharmony_ci VOP_REG_SET(vop, output, rgb_en, 1); 139662306a36Sopenharmony_ci break; 139762306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_eDP: 139862306a36Sopenharmony_ci VOP_REG_SET(vop, output, edp_dclk_pol, 1); 139962306a36Sopenharmony_ci VOP_REG_SET(vop, output, edp_pin_pol, pin_pol); 140062306a36Sopenharmony_ci VOP_REG_SET(vop, output, edp_en, 1); 140162306a36Sopenharmony_ci break; 140262306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_HDMIA: 140362306a36Sopenharmony_ci VOP_REG_SET(vop, output, hdmi_dclk_pol, 1); 140462306a36Sopenharmony_ci VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol); 140562306a36Sopenharmony_ci VOP_REG_SET(vop, output, hdmi_en, 1); 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_DSI: 140862306a36Sopenharmony_ci VOP_REG_SET(vop, output, mipi_dclk_pol, 1); 140962306a36Sopenharmony_ci VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol); 141062306a36Sopenharmony_ci VOP_REG_SET(vop, output, mipi_en, 1); 141162306a36Sopenharmony_ci VOP_REG_SET(vop, output, mipi_dual_channel_en, 141262306a36Sopenharmony_ci !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL)); 141362306a36Sopenharmony_ci break; 141462306a36Sopenharmony_ci case DRM_MODE_CONNECTOR_DisplayPort: 141562306a36Sopenharmony_ci VOP_REG_SET(vop, output, dp_dclk_pol, 0); 141662306a36Sopenharmony_ci VOP_REG_SET(vop, output, dp_pin_pol, pin_pol); 141762306a36Sopenharmony_ci VOP_REG_SET(vop, output, dp_en, 1); 141862306a36Sopenharmony_ci break; 141962306a36Sopenharmony_ci default: 142062306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n", 142162306a36Sopenharmony_ci s->output_type); 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* 142562306a36Sopenharmony_ci * if vop is not support RGB10 output, need force RGB10 to RGB888. 142662306a36Sopenharmony_ci */ 142762306a36Sopenharmony_ci if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && 142862306a36Sopenharmony_ci !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10)) 142962306a36Sopenharmony_ci s->output_mode = ROCKCHIP_OUT_MODE_P888; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8) 143262306a36Sopenharmony_ci VOP_REG_SET(vop, common, pre_dither_down, 1); 143362306a36Sopenharmony_ci else 143462306a36Sopenharmony_ci VOP_REG_SET(vop, common, pre_dither_down, 0); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci if (dither_bpc == 6) { 143762306a36Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_sel, DITHER_DOWN_ALLEGRO); 143862306a36Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_mode, RGB888_TO_RGB666); 143962306a36Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_en, 1); 144062306a36Sopenharmony_ci } else { 144162306a36Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_en, 0); 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci VOP_REG_SET(vop, common, out_mode, s->output_mode); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len); 144762306a36Sopenharmony_ci val = hact_st << 16; 144862306a36Sopenharmony_ci val |= hact_end; 144962306a36Sopenharmony_ci VOP_REG_SET(vop, modeset, hact_st_end, val); 145062306a36Sopenharmony_ci VOP_REG_SET(vop, modeset, hpost_st_end, val); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci VOP_REG_SET(vop, modeset, vtotal_pw, (vtotal << 16) | vsync_len); 145362306a36Sopenharmony_ci val = vact_st << 16; 145462306a36Sopenharmony_ci val |= vact_end; 145562306a36Sopenharmony_ci VOP_REG_SET(vop, modeset, vact_st_end, val); 145662306a36Sopenharmony_ci VOP_REG_SET(vop, modeset, vpost_st_end, val); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci VOP_REG_SET(vop, intr, line_flag_num[0], vact_end); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci VOP_REG_SET(vop, common, standby, 0); 146362306a36Sopenharmony_ci mutex_unlock(&vop->vop_lock); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* 146662306a36Sopenharmony_ci * If we have a GAMMA LUT in the state, then let's make sure 146762306a36Sopenharmony_ci * it's updated. We might be coming out of suspend, 146862306a36Sopenharmony_ci * which means the LUT internal memory needs to be re-written. 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_ci if (crtc->state->gamma_lut) 147162306a36Sopenharmony_ci vop_crtc_gamma_set(vop, crtc, old_state); 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic bool vop_fs_irq_is_pending(struct vop *vop) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci return VOP_INTR_GET_TYPE(vop, status, FS_INTR); 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic void vop_wait_for_irq_handler(struct vop *vop) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci bool pending; 148262306a36Sopenharmony_ci int ret; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* 148562306a36Sopenharmony_ci * Spin until frame start interrupt status bit goes low, which means 148662306a36Sopenharmony_ci * that interrupt handler was invoked and cleared it. The timeout of 148762306a36Sopenharmony_ci * 10 msecs is really too long, but it is just a safety measure if 148862306a36Sopenharmony_ci * something goes really wrong. The wait will only happen in the very 148962306a36Sopenharmony_ci * unlikely case of a vblank happening exactly at the same time and 149062306a36Sopenharmony_ci * shouldn't exceed microseconds range. 149162306a36Sopenharmony_ci */ 149262306a36Sopenharmony_ci ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending, 149362306a36Sopenharmony_ci !pending, 0, 10 * 1000); 149462306a36Sopenharmony_ci if (ret) 149562306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n"); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci synchronize_irq(vop->irq); 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic int vop_crtc_atomic_check(struct drm_crtc *crtc, 150162306a36Sopenharmony_ci struct drm_atomic_state *state) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 150462306a36Sopenharmony_ci crtc); 150562306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 150662306a36Sopenharmony_ci struct drm_plane *plane; 150762306a36Sopenharmony_ci struct drm_plane_state *plane_state; 150862306a36Sopenharmony_ci struct rockchip_crtc_state *s; 150962306a36Sopenharmony_ci int afbc_planes = 0; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci if (vop->lut_regs && crtc_state->color_mgmt_changed && 151262306a36Sopenharmony_ci crtc_state->gamma_lut) { 151362306a36Sopenharmony_ci unsigned int len; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci len = drm_color_lut_size(crtc_state->gamma_lut); 151662306a36Sopenharmony_ci if (len != crtc->gamma_size) { 151762306a36Sopenharmony_ci DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n", 151862306a36Sopenharmony_ci len, crtc->gamma_size); 151962306a36Sopenharmony_ci return -EINVAL; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { 152462306a36Sopenharmony_ci plane_state = 152562306a36Sopenharmony_ci drm_atomic_get_plane_state(crtc_state->state, plane); 152662306a36Sopenharmony_ci if (IS_ERR(plane_state)) { 152762306a36Sopenharmony_ci DRM_DEBUG_KMS("Cannot get plane state for plane %s\n", 152862306a36Sopenharmony_ci plane->name); 152962306a36Sopenharmony_ci return PTR_ERR(plane_state); 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (drm_is_afbc(plane_state->fb->modifier)) 153362306a36Sopenharmony_ci ++afbc_planes; 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci if (afbc_planes > 1) { 153762306a36Sopenharmony_ci DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes); 153862306a36Sopenharmony_ci return -EINVAL; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci s = to_rockchip_crtc_state(crtc_state); 154262306a36Sopenharmony_ci s->enable_afbc = afbc_planes > 0; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci return 0; 154562306a36Sopenharmony_ci} 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_cistatic void vop_crtc_atomic_flush(struct drm_crtc *crtc, 154862306a36Sopenharmony_ci struct drm_atomic_state *state) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, 155162306a36Sopenharmony_ci crtc); 155262306a36Sopenharmony_ci struct drm_atomic_state *old_state = old_crtc_state->state; 155362306a36Sopenharmony_ci struct drm_plane_state *old_plane_state, *new_plane_state; 155462306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 155562306a36Sopenharmony_ci struct drm_plane *plane; 155662306a36Sopenharmony_ci struct rockchip_crtc_state *s; 155762306a36Sopenharmony_ci int i; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 156062306a36Sopenharmony_ci return; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci spin_lock(&vop->reg_lock); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci /* Enable AFBC if there is some AFBC window, disable otherwise. */ 156562306a36Sopenharmony_ci s = to_rockchip_crtc_state(crtc->state); 156662306a36Sopenharmony_ci VOP_AFBC_SET(vop, enable, s->enable_afbc); 156762306a36Sopenharmony_ci vop_cfg_done(vop); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci spin_unlock(&vop->reg_lock); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* 157262306a36Sopenharmony_ci * There is a (rather unlikely) possiblity that a vblank interrupt 157362306a36Sopenharmony_ci * fired before we set the cfg_done bit. To avoid spuriously 157462306a36Sopenharmony_ci * signalling flip completion we need to wait for it to finish. 157562306a36Sopenharmony_ci */ 157662306a36Sopenharmony_ci vop_wait_for_irq_handler(vop); 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 157962306a36Sopenharmony_ci if (crtc->state->event) { 158062306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 158162306a36Sopenharmony_ci WARN_ON(vop->event); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci vop->event = crtc->state->event; 158462306a36Sopenharmony_ci crtc->state->event = NULL; 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, 158962306a36Sopenharmony_ci new_plane_state, i) { 159062306a36Sopenharmony_ci if (!old_plane_state->fb) 159162306a36Sopenharmony_ci continue; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci if (old_plane_state->fb == new_plane_state->fb) 159462306a36Sopenharmony_ci continue; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci drm_framebuffer_get(old_plane_state->fb); 159762306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 159862306a36Sopenharmony_ci drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb); 159962306a36Sopenharmony_ci set_bit(VOP_PENDING_FB_UNREF, &vop->pending); 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci} 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { 160462306a36Sopenharmony_ci .mode_valid = vop_crtc_mode_valid, 160562306a36Sopenharmony_ci .mode_fixup = vop_crtc_mode_fixup, 160662306a36Sopenharmony_ci .atomic_check = vop_crtc_atomic_check, 160762306a36Sopenharmony_ci .atomic_begin = vop_crtc_atomic_begin, 160862306a36Sopenharmony_ci .atomic_flush = vop_crtc_atomic_flush, 160962306a36Sopenharmony_ci .atomic_enable = vop_crtc_atomic_enable, 161062306a36Sopenharmony_ci .atomic_disable = vop_crtc_atomic_disable, 161162306a36Sopenharmony_ci}; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic void vop_crtc_destroy(struct drm_crtc *crtc) 161462306a36Sopenharmony_ci{ 161562306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci struct rockchip_crtc_state *rockchip_state; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci if (WARN_ON(!crtc->state)) 162362306a36Sopenharmony_ci return NULL; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci rockchip_state = kmemdup(to_rockchip_crtc_state(crtc->state), 162662306a36Sopenharmony_ci sizeof(*rockchip_state), GFP_KERNEL); 162762306a36Sopenharmony_ci if (!rockchip_state) 162862306a36Sopenharmony_ci return NULL; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &rockchip_state->base); 163162306a36Sopenharmony_ci return &rockchip_state->base; 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic void vop_crtc_destroy_state(struct drm_crtc *crtc, 163562306a36Sopenharmony_ci struct drm_crtc_state *state) 163662306a36Sopenharmony_ci{ 163762306a36Sopenharmony_ci struct rockchip_crtc_state *s = to_rockchip_crtc_state(state); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(&s->base); 164062306a36Sopenharmony_ci kfree(s); 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistatic void vop_crtc_reset(struct drm_crtc *crtc) 164462306a36Sopenharmony_ci{ 164562306a36Sopenharmony_ci struct rockchip_crtc_state *crtc_state = 164662306a36Sopenharmony_ci kzalloc(sizeof(*crtc_state), GFP_KERNEL); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (crtc->state) 164962306a36Sopenharmony_ci vop_crtc_destroy_state(crtc, crtc->state); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (crtc_state) 165262306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base); 165362306a36Sopenharmony_ci else 165462306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, NULL); 165562306a36Sopenharmony_ci} 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci#ifdef CONFIG_DRM_ANALOGIX_DP 165862306a36Sopenharmony_cistatic struct drm_connector *vop_get_edp_connector(struct vop *vop) 165962306a36Sopenharmony_ci{ 166062306a36Sopenharmony_ci struct drm_connector *connector; 166162306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci drm_connector_list_iter_begin(vop->drm_dev, &conn_iter); 166462306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 166562306a36Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { 166662306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 166762306a36Sopenharmony_ci return connector; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci return NULL; 167362306a36Sopenharmony_ci} 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_cistatic int vop_crtc_set_crc_source(struct drm_crtc *crtc, 167662306a36Sopenharmony_ci const char *source_name) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 167962306a36Sopenharmony_ci struct drm_connector *connector; 168062306a36Sopenharmony_ci int ret; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci connector = vop_get_edp_connector(vop); 168362306a36Sopenharmony_ci if (!connector) 168462306a36Sopenharmony_ci return -EINVAL; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (source_name && strcmp(source_name, "auto") == 0) 168762306a36Sopenharmony_ci ret = analogix_dp_start_crc(connector); 168862306a36Sopenharmony_ci else if (!source_name) 168962306a36Sopenharmony_ci ret = analogix_dp_stop_crc(connector); 169062306a36Sopenharmony_ci else 169162306a36Sopenharmony_ci ret = -EINVAL; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci return ret; 169462306a36Sopenharmony_ci} 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_cistatic int 169762306a36Sopenharmony_civop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, 169862306a36Sopenharmony_ci size_t *values_cnt) 169962306a36Sopenharmony_ci{ 170062306a36Sopenharmony_ci if (source_name && strcmp(source_name, "auto") != 0) 170162306a36Sopenharmony_ci return -EINVAL; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci *values_cnt = 3; 170462306a36Sopenharmony_ci return 0; 170562306a36Sopenharmony_ci} 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci#else 170862306a36Sopenharmony_cistatic int vop_crtc_set_crc_source(struct drm_crtc *crtc, 170962306a36Sopenharmony_ci const char *source_name) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci return -ENODEV; 171262306a36Sopenharmony_ci} 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_cistatic int 171562306a36Sopenharmony_civop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, 171662306a36Sopenharmony_ci size_t *values_cnt) 171762306a36Sopenharmony_ci{ 171862306a36Sopenharmony_ci return -ENODEV; 171962306a36Sopenharmony_ci} 172062306a36Sopenharmony_ci#endif 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_cistatic const struct drm_crtc_funcs vop_crtc_funcs = { 172362306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 172462306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 172562306a36Sopenharmony_ci .destroy = vop_crtc_destroy, 172662306a36Sopenharmony_ci .reset = vop_crtc_reset, 172762306a36Sopenharmony_ci .atomic_duplicate_state = vop_crtc_duplicate_state, 172862306a36Sopenharmony_ci .atomic_destroy_state = vop_crtc_destroy_state, 172962306a36Sopenharmony_ci .enable_vblank = vop_crtc_enable_vblank, 173062306a36Sopenharmony_ci .disable_vblank = vop_crtc_disable_vblank, 173162306a36Sopenharmony_ci .set_crc_source = vop_crtc_set_crc_source, 173262306a36Sopenharmony_ci .verify_crc_source = vop_crtc_verify_crc_source, 173362306a36Sopenharmony_ci}; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic void vop_fb_unref_worker(struct drm_flip_work *work, void *val) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci struct vop *vop = container_of(work, struct vop, fb_unref_work); 173862306a36Sopenharmony_ci struct drm_framebuffer *fb = val; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci drm_crtc_vblank_put(&vop->crtc); 174162306a36Sopenharmony_ci drm_framebuffer_put(fb); 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cistatic void vop_handle_vblank(struct vop *vop) 174562306a36Sopenharmony_ci{ 174662306a36Sopenharmony_ci struct drm_device *drm = vop->drm_dev; 174762306a36Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci spin_lock(&drm->event_lock); 175062306a36Sopenharmony_ci if (vop->event) { 175162306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, vop->event); 175262306a36Sopenharmony_ci drm_crtc_vblank_put(crtc); 175362306a36Sopenharmony_ci vop->event = NULL; 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci spin_unlock(&drm->event_lock); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) 175862306a36Sopenharmony_ci drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); 175962306a36Sopenharmony_ci} 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_cistatic irqreturn_t vop_isr(int irq, void *data) 176262306a36Sopenharmony_ci{ 176362306a36Sopenharmony_ci struct vop *vop = data; 176462306a36Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 176562306a36Sopenharmony_ci uint32_t active_irqs; 176662306a36Sopenharmony_ci int ret = IRQ_NONE; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci /* 176962306a36Sopenharmony_ci * The irq is shared with the iommu. If the runtime-pm state of the 177062306a36Sopenharmony_ci * vop-device is disabled the irq has to be targeted at the iommu. 177162306a36Sopenharmony_ci */ 177262306a36Sopenharmony_ci if (!pm_runtime_get_if_in_use(vop->dev)) 177362306a36Sopenharmony_ci return IRQ_NONE; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (vop_core_clks_enable(vop)) { 177662306a36Sopenharmony_ci DRM_DEV_ERROR_RATELIMITED(vop->dev, "couldn't enable clocks\n"); 177762306a36Sopenharmony_ci goto out; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci /* 178162306a36Sopenharmony_ci * interrupt register has interrupt status, enable and clear bits, we 178262306a36Sopenharmony_ci * must hold irq_lock to avoid a race with enable/disable_vblank(). 178362306a36Sopenharmony_ci */ 178462306a36Sopenharmony_ci spin_lock(&vop->irq_lock); 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci active_irqs = VOP_INTR_GET_TYPE(vop, status, INTR_MASK); 178762306a36Sopenharmony_ci /* Clear all active interrupt sources */ 178862306a36Sopenharmony_ci if (active_irqs) 178962306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci spin_unlock(&vop->irq_lock); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci /* This is expected for vop iommu irqs, since the irq is shared */ 179462306a36Sopenharmony_ci if (!active_irqs) 179562306a36Sopenharmony_ci goto out_disable; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (active_irqs & DSP_HOLD_VALID_INTR) { 179862306a36Sopenharmony_ci complete(&vop->dsp_hold_completion); 179962306a36Sopenharmony_ci active_irqs &= ~DSP_HOLD_VALID_INTR; 180062306a36Sopenharmony_ci ret = IRQ_HANDLED; 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (active_irqs & LINE_FLAG_INTR) { 180462306a36Sopenharmony_ci complete(&vop->line_flag_completion); 180562306a36Sopenharmony_ci active_irqs &= ~LINE_FLAG_INTR; 180662306a36Sopenharmony_ci ret = IRQ_HANDLED; 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (active_irqs & FS_INTR) { 181062306a36Sopenharmony_ci drm_crtc_handle_vblank(crtc); 181162306a36Sopenharmony_ci vop_handle_vblank(vop); 181262306a36Sopenharmony_ci active_irqs &= ~FS_INTR; 181362306a36Sopenharmony_ci ret = IRQ_HANDLED; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci /* Unhandled irqs are spurious. */ 181762306a36Sopenharmony_ci if (active_irqs) 181862306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n", 181962306a36Sopenharmony_ci active_irqs); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ciout_disable: 182262306a36Sopenharmony_ci vop_core_clks_disable(vop); 182362306a36Sopenharmony_ciout: 182462306a36Sopenharmony_ci pm_runtime_put(vop->dev); 182562306a36Sopenharmony_ci return ret; 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_cistatic void vop_plane_add_properties(struct drm_plane *plane, 182962306a36Sopenharmony_ci const struct vop_win_data *win_data) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci unsigned int flags = 0; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0; 183462306a36Sopenharmony_ci flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0; 183562306a36Sopenharmony_ci if (flags) 183662306a36Sopenharmony_ci drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, 183762306a36Sopenharmony_ci DRM_MODE_ROTATE_0 | flags); 183862306a36Sopenharmony_ci} 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_cistatic int vop_create_crtc(struct vop *vop) 184162306a36Sopenharmony_ci{ 184262306a36Sopenharmony_ci const struct vop_data *vop_data = vop->data; 184362306a36Sopenharmony_ci struct device *dev = vop->dev; 184462306a36Sopenharmony_ci struct drm_device *drm_dev = vop->drm_dev; 184562306a36Sopenharmony_ci struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp; 184662306a36Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 184762306a36Sopenharmony_ci struct device_node *port; 184862306a36Sopenharmony_ci int ret; 184962306a36Sopenharmony_ci int i; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* 185262306a36Sopenharmony_ci * Create drm_plane for primary and cursor planes first, since we need 185362306a36Sopenharmony_ci * to pass them to drm_crtc_init_with_planes, which sets the 185462306a36Sopenharmony_ci * "possible_crtcs" to the newly initialized crtc. 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_ci for (i = 0; i < vop_data->win_size; i++) { 185762306a36Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 185862306a36Sopenharmony_ci const struct vop_win_data *win_data = vop_win->data; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci if (win_data->type != DRM_PLANE_TYPE_PRIMARY && 186162306a36Sopenharmony_ci win_data->type != DRM_PLANE_TYPE_CURSOR) 186262306a36Sopenharmony_ci continue; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base, 186562306a36Sopenharmony_ci 0, &vop_plane_funcs, 186662306a36Sopenharmony_ci win_data->phy->data_formats, 186762306a36Sopenharmony_ci win_data->phy->nformats, 186862306a36Sopenharmony_ci win_data->phy->format_modifiers, 186962306a36Sopenharmony_ci win_data->type, NULL); 187062306a36Sopenharmony_ci if (ret) { 187162306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n", 187262306a36Sopenharmony_ci ret); 187362306a36Sopenharmony_ci goto err_cleanup_planes; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci plane = &vop_win->base; 187762306a36Sopenharmony_ci drm_plane_helper_add(plane, &plane_helper_funcs); 187862306a36Sopenharmony_ci vop_plane_add_properties(plane, win_data); 187962306a36Sopenharmony_ci if (plane->type == DRM_PLANE_TYPE_PRIMARY) 188062306a36Sopenharmony_ci primary = plane; 188162306a36Sopenharmony_ci else if (plane->type == DRM_PLANE_TYPE_CURSOR) 188262306a36Sopenharmony_ci cursor = plane; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, 188662306a36Sopenharmony_ci &vop_crtc_funcs, NULL); 188762306a36Sopenharmony_ci if (ret) 188862306a36Sopenharmony_ci goto err_cleanup_planes; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs); 189162306a36Sopenharmony_ci if (vop->lut_regs) { 189262306a36Sopenharmony_ci drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size); 189362306a36Sopenharmony_ci drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size); 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci /* 189762306a36Sopenharmony_ci * Create drm_planes for overlay windows with possible_crtcs restricted 189862306a36Sopenharmony_ci * to the newly created crtc. 189962306a36Sopenharmony_ci */ 190062306a36Sopenharmony_ci for (i = 0; i < vop_data->win_size; i++) { 190162306a36Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 190262306a36Sopenharmony_ci const struct vop_win_data *win_data = vop_win->data; 190362306a36Sopenharmony_ci unsigned long possible_crtcs = drm_crtc_mask(crtc); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci if (win_data->type != DRM_PLANE_TYPE_OVERLAY) 190662306a36Sopenharmony_ci continue; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base, 190962306a36Sopenharmony_ci possible_crtcs, 191062306a36Sopenharmony_ci &vop_plane_funcs, 191162306a36Sopenharmony_ci win_data->phy->data_formats, 191262306a36Sopenharmony_ci win_data->phy->nformats, 191362306a36Sopenharmony_ci win_data->phy->format_modifiers, 191462306a36Sopenharmony_ci win_data->type, NULL); 191562306a36Sopenharmony_ci if (ret) { 191662306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n", 191762306a36Sopenharmony_ci ret); 191862306a36Sopenharmony_ci goto err_cleanup_crtc; 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci drm_plane_helper_add(&vop_win->base, &plane_helper_funcs); 192162306a36Sopenharmony_ci vop_plane_add_properties(&vop_win->base, win_data); 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci port = of_get_child_by_name(dev->of_node, "port"); 192562306a36Sopenharmony_ci if (!port) { 192662306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "no port node found in %pOF\n", 192762306a36Sopenharmony_ci dev->of_node); 192862306a36Sopenharmony_ci ret = -ENOENT; 192962306a36Sopenharmony_ci goto err_cleanup_crtc; 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci drm_flip_work_init(&vop->fb_unref_work, "fb_unref", 193362306a36Sopenharmony_ci vop_fb_unref_worker); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci init_completion(&vop->dsp_hold_completion); 193662306a36Sopenharmony_ci init_completion(&vop->line_flag_completion); 193762306a36Sopenharmony_ci crtc->port = port; 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci ret = drm_self_refresh_helper_init(crtc); 194062306a36Sopenharmony_ci if (ret) 194162306a36Sopenharmony_ci DRM_DEV_DEBUG_KMS(vop->dev, 194262306a36Sopenharmony_ci "Failed to init %s with SR helpers %d, ignoring\n", 194362306a36Sopenharmony_ci crtc->name, ret); 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci return 0; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_cierr_cleanup_crtc: 194862306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 194962306a36Sopenharmony_cierr_cleanup_planes: 195062306a36Sopenharmony_ci list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list, 195162306a36Sopenharmony_ci head) 195262306a36Sopenharmony_ci drm_plane_cleanup(plane); 195362306a36Sopenharmony_ci return ret; 195462306a36Sopenharmony_ci} 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_cistatic void vop_destroy_crtc(struct vop *vop) 195762306a36Sopenharmony_ci{ 195862306a36Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 195962306a36Sopenharmony_ci struct drm_device *drm_dev = vop->drm_dev; 196062306a36Sopenharmony_ci struct drm_plane *plane, *tmp; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci drm_self_refresh_helper_cleanup(crtc); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci of_node_put(crtc->port); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* 196762306a36Sopenharmony_ci * We need to cleanup the planes now. Why? 196862306a36Sopenharmony_ci * 196962306a36Sopenharmony_ci * The planes are "&vop->win[i].base". That means the memory is 197062306a36Sopenharmony_ci * all part of the big "struct vop" chunk of memory. That memory 197162306a36Sopenharmony_ci * was devm allocated and associated with this component. We need to 197262306a36Sopenharmony_ci * free it ourselves before vop_unbind() finishes. 197362306a36Sopenharmony_ci */ 197462306a36Sopenharmony_ci list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list, 197562306a36Sopenharmony_ci head) 197662306a36Sopenharmony_ci vop_plane_destroy(plane); 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci /* 197962306a36Sopenharmony_ci * Destroy CRTC after vop_plane_destroy() since vop_disable_plane() 198062306a36Sopenharmony_ci * references the CRTC. 198162306a36Sopenharmony_ci */ 198262306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 198362306a36Sopenharmony_ci drm_flip_work_cleanup(&vop->fb_unref_work); 198462306a36Sopenharmony_ci} 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_cistatic int vop_initial(struct vop *vop) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci struct reset_control *ahb_rst; 198962306a36Sopenharmony_ci int i, ret; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci vop->hclk = devm_clk_get(vop->dev, "hclk_vop"); 199262306a36Sopenharmony_ci if (IS_ERR(vop->hclk)) { 199362306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n"); 199462306a36Sopenharmony_ci return PTR_ERR(vop->hclk); 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci vop->aclk = devm_clk_get(vop->dev, "aclk_vop"); 199762306a36Sopenharmony_ci if (IS_ERR(vop->aclk)) { 199862306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n"); 199962306a36Sopenharmony_ci return PTR_ERR(vop->aclk); 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci vop->dclk = devm_clk_get(vop->dev, "dclk_vop"); 200262306a36Sopenharmony_ci if (IS_ERR(vop->dclk)) { 200362306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n"); 200462306a36Sopenharmony_ci return PTR_ERR(vop->dclk); 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(vop->dev); 200862306a36Sopenharmony_ci if (ret < 0) { 200962306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); 201062306a36Sopenharmony_ci return ret; 201162306a36Sopenharmony_ci } 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci ret = clk_prepare(vop->dclk); 201462306a36Sopenharmony_ci if (ret < 0) { 201562306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n"); 201662306a36Sopenharmony_ci goto err_put_pm_runtime; 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci /* Enable both the hclk and aclk to setup the vop */ 202062306a36Sopenharmony_ci ret = clk_prepare_enable(vop->hclk); 202162306a36Sopenharmony_ci if (ret < 0) { 202262306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n"); 202362306a36Sopenharmony_ci goto err_unprepare_dclk; 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci ret = clk_prepare_enable(vop->aclk); 202762306a36Sopenharmony_ci if (ret < 0) { 202862306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n"); 202962306a36Sopenharmony_ci goto err_disable_hclk; 203062306a36Sopenharmony_ci } 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci /* 203362306a36Sopenharmony_ci * do hclk_reset, reset all vop registers. 203462306a36Sopenharmony_ci */ 203562306a36Sopenharmony_ci ahb_rst = devm_reset_control_get(vop->dev, "ahb"); 203662306a36Sopenharmony_ci if (IS_ERR(ahb_rst)) { 203762306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n"); 203862306a36Sopenharmony_ci ret = PTR_ERR(ahb_rst); 203962306a36Sopenharmony_ci goto err_disable_aclk; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci reset_control_assert(ahb_rst); 204262306a36Sopenharmony_ci usleep_range(10, 20); 204362306a36Sopenharmony_ci reset_control_deassert(ahb_rst); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1); 204662306a36Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0); 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci for (i = 0; i < vop->len; i += sizeof(u32)) 204962306a36Sopenharmony_ci vop->regsbak[i / 4] = readl_relaxed(vop->regs + i); 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci VOP_REG_SET(vop, misc, global_regdone_en, 1); 205262306a36Sopenharmony_ci VOP_REG_SET(vop, common, dsp_blank, 0); 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci for (i = 0; i < vop->data->win_size; i++) { 205562306a36Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 205662306a36Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 205762306a36Sopenharmony_ci int channel = i * 2 + 1; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel); 206062306a36Sopenharmony_ci vop_win_disable(vop, vop_win); 206162306a36Sopenharmony_ci VOP_WIN_SET(vop, win, gate, 1); 206262306a36Sopenharmony_ci } 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci vop_cfg_done(vop); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci /* 206762306a36Sopenharmony_ci * do dclk_reset, let all config take affect. 206862306a36Sopenharmony_ci */ 206962306a36Sopenharmony_ci vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk"); 207062306a36Sopenharmony_ci if (IS_ERR(vop->dclk_rst)) { 207162306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n"); 207262306a36Sopenharmony_ci ret = PTR_ERR(vop->dclk_rst); 207362306a36Sopenharmony_ci goto err_disable_aclk; 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci reset_control_assert(vop->dclk_rst); 207662306a36Sopenharmony_ci usleep_range(10, 20); 207762306a36Sopenharmony_ci reset_control_deassert(vop->dclk_rst); 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci clk_disable(vop->hclk); 208062306a36Sopenharmony_ci clk_disable(vop->aclk); 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci vop->is_enabled = false; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci pm_runtime_put_sync(vop->dev); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci return 0; 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_cierr_disable_aclk: 208962306a36Sopenharmony_ci clk_disable_unprepare(vop->aclk); 209062306a36Sopenharmony_cierr_disable_hclk: 209162306a36Sopenharmony_ci clk_disable_unprepare(vop->hclk); 209262306a36Sopenharmony_cierr_unprepare_dclk: 209362306a36Sopenharmony_ci clk_unprepare(vop->dclk); 209462306a36Sopenharmony_cierr_put_pm_runtime: 209562306a36Sopenharmony_ci pm_runtime_put_sync(vop->dev); 209662306a36Sopenharmony_ci return ret; 209762306a36Sopenharmony_ci} 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci/* 210062306a36Sopenharmony_ci * Initialize the vop->win array elements. 210162306a36Sopenharmony_ci */ 210262306a36Sopenharmony_cistatic void vop_win_init(struct vop *vop) 210362306a36Sopenharmony_ci{ 210462306a36Sopenharmony_ci const struct vop_data *vop_data = vop->data; 210562306a36Sopenharmony_ci unsigned int i; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci for (i = 0; i < vop_data->win_size; i++) { 210862306a36Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 210962306a36Sopenharmony_ci const struct vop_win_data *win_data = &vop_data->win[i]; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci vop_win->data = win_data; 211262306a36Sopenharmony_ci vop_win->vop = vop; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci if (vop_data->win_yuv2yuv) 211562306a36Sopenharmony_ci vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i]; 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci} 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci/** 212062306a36Sopenharmony_ci * rockchip_drm_wait_vact_end 212162306a36Sopenharmony_ci * @crtc: CRTC to enable line flag 212262306a36Sopenharmony_ci * @mstimeout: millisecond for timeout 212362306a36Sopenharmony_ci * 212462306a36Sopenharmony_ci * Wait for vact_end line flag irq or timeout. 212562306a36Sopenharmony_ci * 212662306a36Sopenharmony_ci * Returns: 212762306a36Sopenharmony_ci * Zero on success, negative errno on failure. 212862306a36Sopenharmony_ci */ 212962306a36Sopenharmony_ciint rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci struct vop *vop = to_vop(crtc); 213262306a36Sopenharmony_ci unsigned long jiffies_left; 213362306a36Sopenharmony_ci int ret = 0; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci if (!crtc || !vop->is_enabled) 213662306a36Sopenharmony_ci return -ENODEV; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci mutex_lock(&vop->vop_lock); 213962306a36Sopenharmony_ci if (mstimeout <= 0) { 214062306a36Sopenharmony_ci ret = -EINVAL; 214162306a36Sopenharmony_ci goto out; 214262306a36Sopenharmony_ci } 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci if (vop_line_flag_irq_is_enabled(vop)) { 214562306a36Sopenharmony_ci ret = -EBUSY; 214662306a36Sopenharmony_ci goto out; 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci reinit_completion(&vop->line_flag_completion); 215062306a36Sopenharmony_ci vop_line_flag_irq_enable(vop); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion, 215362306a36Sopenharmony_ci msecs_to_jiffies(mstimeout)); 215462306a36Sopenharmony_ci vop_line_flag_irq_disable(vop); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (jiffies_left == 0) { 215762306a36Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n"); 215862306a36Sopenharmony_ci ret = -ETIMEDOUT; 215962306a36Sopenharmony_ci goto out; 216062306a36Sopenharmony_ci } 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ciout: 216362306a36Sopenharmony_ci mutex_unlock(&vop->vop_lock); 216462306a36Sopenharmony_ci return ret; 216562306a36Sopenharmony_ci} 216662306a36Sopenharmony_ciEXPORT_SYMBOL(rockchip_drm_wait_vact_end); 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_cistatic int vop_bind(struct device *dev, struct device *master, void *data) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 217162306a36Sopenharmony_ci const struct vop_data *vop_data; 217262306a36Sopenharmony_ci struct drm_device *drm_dev = data; 217362306a36Sopenharmony_ci struct vop *vop; 217462306a36Sopenharmony_ci struct resource *res; 217562306a36Sopenharmony_ci int ret, irq; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci vop_data = of_device_get_match_data(dev); 217862306a36Sopenharmony_ci if (!vop_data) 217962306a36Sopenharmony_ci return -ENODEV; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci /* Allocate vop struct and its vop_win array */ 218262306a36Sopenharmony_ci vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size), 218362306a36Sopenharmony_ci GFP_KERNEL); 218462306a36Sopenharmony_ci if (!vop) 218562306a36Sopenharmony_ci return -ENOMEM; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci vop->dev = dev; 218862306a36Sopenharmony_ci vop->data = vop_data; 218962306a36Sopenharmony_ci vop->drm_dev = drm_dev; 219062306a36Sopenharmony_ci dev_set_drvdata(dev, vop); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci vop_win_init(vop); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 219562306a36Sopenharmony_ci vop->regs = devm_ioremap_resource(dev, res); 219662306a36Sopenharmony_ci if (IS_ERR(vop->regs)) 219762306a36Sopenharmony_ci return PTR_ERR(vop->regs); 219862306a36Sopenharmony_ci vop->len = resource_size(res); 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 220162306a36Sopenharmony_ci if (res) { 220262306a36Sopenharmony_ci if (vop_data->lut_size != 1024 && vop_data->lut_size != 256) { 220362306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "unsupported gamma LUT size %d\n", vop_data->lut_size); 220462306a36Sopenharmony_ci return -EINVAL; 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci vop->lut_regs = devm_ioremap_resource(dev, res); 220762306a36Sopenharmony_ci if (IS_ERR(vop->lut_regs)) 220862306a36Sopenharmony_ci return PTR_ERR(vop->lut_regs); 220962306a36Sopenharmony_ci } 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL); 221262306a36Sopenharmony_ci if (!vop->regsbak) 221362306a36Sopenharmony_ci return -ENOMEM; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 221662306a36Sopenharmony_ci if (irq < 0) { 221762306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "cannot find irq for vop\n"); 221862306a36Sopenharmony_ci return irq; 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci vop->irq = (unsigned int)irq; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci spin_lock_init(&vop->reg_lock); 222362306a36Sopenharmony_ci spin_lock_init(&vop->irq_lock); 222462306a36Sopenharmony_ci mutex_init(&vop->vop_lock); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci ret = vop_create_crtc(vop); 222762306a36Sopenharmony_ci if (ret) 222862306a36Sopenharmony_ci return ret; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci ret = vop_initial(vop); 223362306a36Sopenharmony_ci if (ret < 0) { 223462306a36Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, 223562306a36Sopenharmony_ci "cannot initial vop dev - err %d\n", ret); 223662306a36Sopenharmony_ci goto err_disable_pm_runtime; 223762306a36Sopenharmony_ci } 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci ret = devm_request_irq(dev, vop->irq, vop_isr, 224062306a36Sopenharmony_ci IRQF_SHARED, dev_name(dev), vop); 224162306a36Sopenharmony_ci if (ret) 224262306a36Sopenharmony_ci goto err_disable_pm_runtime; 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) { 224562306a36Sopenharmony_ci vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev, 0); 224662306a36Sopenharmony_ci if (IS_ERR(vop->rgb)) { 224762306a36Sopenharmony_ci ret = PTR_ERR(vop->rgb); 224862306a36Sopenharmony_ci goto err_disable_pm_runtime; 224962306a36Sopenharmony_ci } 225062306a36Sopenharmony_ci } 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci rockchip_drm_dma_init_device(drm_dev, dev); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci return 0; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_cierr_disable_pm_runtime: 225762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 225862306a36Sopenharmony_ci vop_destroy_crtc(vop); 225962306a36Sopenharmony_ci return ret; 226062306a36Sopenharmony_ci} 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_cistatic void vop_unbind(struct device *dev, struct device *master, void *data) 226362306a36Sopenharmony_ci{ 226462306a36Sopenharmony_ci struct vop *vop = dev_get_drvdata(dev); 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci if (vop->rgb) 226762306a36Sopenharmony_ci rockchip_rgb_fini(vop->rgb); 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci pm_runtime_disable(dev); 227062306a36Sopenharmony_ci vop_destroy_crtc(vop); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci clk_unprepare(vop->aclk); 227362306a36Sopenharmony_ci clk_unprepare(vop->hclk); 227462306a36Sopenharmony_ci clk_unprepare(vop->dclk); 227562306a36Sopenharmony_ci} 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ciconst struct component_ops vop_component_ops = { 227862306a36Sopenharmony_ci .bind = vop_bind, 227962306a36Sopenharmony_ci .unbind = vop_unbind, 228062306a36Sopenharmony_ci}; 228162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vop_component_ops); 2282