18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 48c2ecf20Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/component.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/overflow.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <linux/reset.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <drm/drm.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_atomic_uapi.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_flip_work.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 278c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_self_refresh_helper.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_ANALOGIX_DP 338c2ecf20Sopenharmony_ci#include <drm/bridge/analogix_dp.h> 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "rockchip_drm_drv.h" 378c2ecf20Sopenharmony_ci#include "rockchip_drm_gem.h" 388c2ecf20Sopenharmony_ci#include "rockchip_drm_fb.h" 398c2ecf20Sopenharmony_ci#include "rockchip_drm_vop.h" 408c2ecf20Sopenharmony_ci#include "rockchip_rgb.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define VOP_WIN_SET(vop, win, name, v) \ 438c2ecf20Sopenharmony_ci vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) 448c2ecf20Sopenharmony_ci#define VOP_SCL_SET(vop, win, name, v) \ 458c2ecf20Sopenharmony_ci vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name) 468c2ecf20Sopenharmony_ci#define VOP_SCL_SET_EXT(vop, win, name, v) \ 478c2ecf20Sopenharmony_ci vop_reg_set(vop, &win->phy->scl->ext->name, \ 488c2ecf20Sopenharmony_ci win->base, ~0, v, #name) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \ 518c2ecf20Sopenharmony_ci do { \ 528c2ecf20Sopenharmony_ci if (win_yuv2yuv && win_yuv2yuv->name.mask) \ 538c2ecf20Sopenharmony_ci vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \ 548c2ecf20Sopenharmony_ci } while (0) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \ 578c2ecf20Sopenharmony_ci do { \ 588c2ecf20Sopenharmony_ci if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \ 598c2ecf20Sopenharmony_ci vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \ 608c2ecf20Sopenharmony_ci } while (0) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define VOP_INTR_SET_MASK(vop, name, mask, v) \ 638c2ecf20Sopenharmony_ci vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define VOP_REG_SET(vop, group, name, v) \ 668c2ecf20Sopenharmony_ci vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define VOP_INTR_SET_TYPE(vop, name, type, v) \ 698c2ecf20Sopenharmony_ci do { \ 708c2ecf20Sopenharmony_ci int i, reg = 0, mask = 0; \ 718c2ecf20Sopenharmony_ci for (i = 0; i < vop->data->intr->nintrs; i++) { \ 728c2ecf20Sopenharmony_ci if (vop->data->intr->intrs[i] & type) { \ 738c2ecf20Sopenharmony_ci reg |= (v) << i; \ 748c2ecf20Sopenharmony_ci mask |= 1 << i; \ 758c2ecf20Sopenharmony_ci } \ 768c2ecf20Sopenharmony_ci } \ 778c2ecf20Sopenharmony_ci VOP_INTR_SET_MASK(vop, name, mask, reg); \ 788c2ecf20Sopenharmony_ci } while (0) 798c2ecf20Sopenharmony_ci#define VOP_INTR_GET_TYPE(vop, name, type) \ 808c2ecf20Sopenharmony_ci vop_get_intr_type(vop, &vop->data->intr->name, type) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define VOP_WIN_GET(vop, win, name) \ 838c2ecf20Sopenharmony_ci vop_read_reg(vop, win->base, &win->phy->name) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define VOP_WIN_HAS_REG(win, name) \ 868c2ecf20Sopenharmony_ci (!!(win->phy->name.mask)) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define VOP_WIN_GET_YRGBADDR(vop, win) \ 898c2ecf20Sopenharmony_ci vop_readl(vop, win->base + win->phy->yrgb_mst.offset) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define VOP_WIN_TO_INDEX(vop_win) \ 928c2ecf20Sopenharmony_ci ((vop_win) - (vop_win)->vop->win) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define VOP_AFBC_SET(vop, name, v) \ 958c2ecf20Sopenharmony_ci do { \ 968c2ecf20Sopenharmony_ci if ((vop)->data->afbc) \ 978c2ecf20Sopenharmony_ci vop_reg_set((vop), &(vop)->data->afbc->name, \ 988c2ecf20Sopenharmony_ci 0, ~0, v, #name); \ 998c2ecf20Sopenharmony_ci } while (0) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define to_vop(x) container_of(x, struct vop, crtc) 1028c2ecf20Sopenharmony_ci#define to_vop_win(x) container_of(x, struct vop_win, base) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define AFBC_FMT_RGB565 0x0 1058c2ecf20Sopenharmony_ci#define AFBC_FMT_U8U8U8U8 0x5 1068c2ecf20Sopenharmony_ci#define AFBC_FMT_U8U8U8 0x4 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define AFBC_TILE_16x16 BIT(4) 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * The coefficients of the following matrix are all fixed points. 1128c2ecf20Sopenharmony_ci * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets. 1138c2ecf20Sopenharmony_ci * They are all represented in two's complement. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic const uint32_t bt601_yuv2rgb[] = { 1168c2ecf20Sopenharmony_ci 0x4A8, 0x0, 0x662, 1178c2ecf20Sopenharmony_ci 0x4A8, 0x1E6F, 0x1CBF, 1188c2ecf20Sopenharmony_ci 0x4A8, 0x812, 0x0, 1198c2ecf20Sopenharmony_ci 0x321168, 0x0877CF, 0x2EB127 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cienum vop_pending { 1238c2ecf20Sopenharmony_ci VOP_PENDING_FB_UNREF, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct vop_win { 1278c2ecf20Sopenharmony_ci struct drm_plane base; 1288c2ecf20Sopenharmony_ci const struct vop_win_data *data; 1298c2ecf20Sopenharmony_ci const struct vop_win_yuv2yuv_data *yuv2yuv_data; 1308c2ecf20Sopenharmony_ci struct vop *vop; 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistruct rockchip_rgb; 1348c2ecf20Sopenharmony_cistruct vop { 1358c2ecf20Sopenharmony_ci struct drm_crtc crtc; 1368c2ecf20Sopenharmony_ci struct device *dev; 1378c2ecf20Sopenharmony_ci struct drm_device *drm_dev; 1388c2ecf20Sopenharmony_ci bool is_enabled; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci struct completion dsp_hold_completion; 1418c2ecf20Sopenharmony_ci unsigned int win_enabled; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* protected by dev->event_lock */ 1448c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci struct drm_flip_work fb_unref_work; 1478c2ecf20Sopenharmony_ci unsigned long pending; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci struct completion line_flag_completion; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci const struct vop_data *data; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci uint32_t *regsbak; 1548c2ecf20Sopenharmony_ci void __iomem *regs; 1558c2ecf20Sopenharmony_ci void __iomem *lut_regs; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* physical map length of vop register */ 1588c2ecf20Sopenharmony_ci uint32_t len; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* one time only one process allowed to config the register */ 1618c2ecf20Sopenharmony_ci spinlock_t reg_lock; 1628c2ecf20Sopenharmony_ci /* lock vop irq reg */ 1638c2ecf20Sopenharmony_ci spinlock_t irq_lock; 1648c2ecf20Sopenharmony_ci /* protects crtc enable/disable */ 1658c2ecf20Sopenharmony_ci struct mutex vop_lock; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci unsigned int irq; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* vop AHP clk */ 1708c2ecf20Sopenharmony_ci struct clk *hclk; 1718c2ecf20Sopenharmony_ci /* vop dclk */ 1728c2ecf20Sopenharmony_ci struct clk *dclk; 1738c2ecf20Sopenharmony_ci /* vop share memory frequency */ 1748c2ecf20Sopenharmony_ci struct clk *aclk; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* vop dclk reset */ 1778c2ecf20Sopenharmony_ci struct reset_control *dclk_rst; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* optional internal rgb encoder */ 1808c2ecf20Sopenharmony_ci struct rockchip_rgb *rgb; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci struct vop_win win[]; 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic inline void vop_writel(struct vop *vop, uint32_t offset, uint32_t v) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci writel(v, vop->regs + offset); 1888c2ecf20Sopenharmony_ci vop->regsbak[offset >> 2] = v; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic inline uint32_t vop_readl(struct vop *vop, uint32_t offset) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci return readl(vop->regs + offset); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic inline uint32_t vop_read_reg(struct vop *vop, uint32_t base, 1978c2ecf20Sopenharmony_ci const struct vop_reg *reg) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return (vop_readl(vop, base + reg->offset) >> reg->shift) & reg->mask; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void vop_reg_set(struct vop *vop, const struct vop_reg *reg, 2038c2ecf20Sopenharmony_ci uint32_t _offset, uint32_t _mask, uint32_t v, 2048c2ecf20Sopenharmony_ci const char *reg_name) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int offset, mask, shift; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (!reg || !reg->mask) { 2098c2ecf20Sopenharmony_ci DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name); 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci offset = reg->offset + _offset; 2148c2ecf20Sopenharmony_ci mask = reg->mask & _mask; 2158c2ecf20Sopenharmony_ci shift = reg->shift; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (reg->write_mask) { 2188c2ecf20Sopenharmony_ci v = ((v << shift) & 0xffff) | (mask << (shift + 16)); 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci uint32_t cached_val = vop->regsbak[offset >> 2]; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci v = (cached_val & ~(mask << shift)) | ((v & mask) << shift); 2238c2ecf20Sopenharmony_ci vop->regsbak[offset >> 2] = v; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (reg->relaxed) 2278c2ecf20Sopenharmony_ci writel_relaxed(v, vop->regs + offset); 2288c2ecf20Sopenharmony_ci else 2298c2ecf20Sopenharmony_ci writel(v, vop->regs + offset); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic inline uint32_t vop_get_intr_type(struct vop *vop, 2338c2ecf20Sopenharmony_ci const struct vop_reg *reg, int type) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci uint32_t i, ret = 0; 2368c2ecf20Sopenharmony_ci uint32_t regs = vop_read_reg(vop, 0, reg); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci for (i = 0; i < vop->data->intr->nintrs; i++) { 2398c2ecf20Sopenharmony_ci if ((type & vop->data->intr->intrs[i]) && (regs & 1 << i)) 2408c2ecf20Sopenharmony_ci ret |= vop->data->intr->intrs[i]; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic inline void vop_cfg_done(struct vop *vop) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, cfg_done, 1); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic bool has_rb_swapped(uint32_t version, uint32_t format) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci switch (format) { 2548c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR8888: 2558c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR8888: 2568c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR565: 2578c2ecf20Sopenharmony_ci return true; 2588c2ecf20Sopenharmony_ci /* 2598c2ecf20Sopenharmony_ci * full framework (IP version 3.x) only need rb swapped for RGB888 and 2608c2ecf20Sopenharmony_ci * little framework (IP version 2.x) only need rb swapped for BGR888, 2618c2ecf20Sopenharmony_ci * check for 3.x to also only rb swap BGR888 for unknown vop version 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB888: 2648c2ecf20Sopenharmony_ci return VOP_MAJOR(version) == 3; 2658c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR888: 2668c2ecf20Sopenharmony_ci return VOP_MAJOR(version) != 3; 2678c2ecf20Sopenharmony_ci default: 2688c2ecf20Sopenharmony_ci return false; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic enum vop_data_format vop_convert_format(uint32_t format) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci switch (format) { 2758c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 2768c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 2778c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR8888: 2788c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR8888: 2798c2ecf20Sopenharmony_ci return VOP_FMT_ARGB8888; 2808c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB888: 2818c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR888: 2828c2ecf20Sopenharmony_ci return VOP_FMT_RGB888; 2838c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 2848c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR565: 2858c2ecf20Sopenharmony_ci return VOP_FMT_RGB565; 2868c2ecf20Sopenharmony_ci case DRM_FORMAT_NV12: 2878c2ecf20Sopenharmony_ci return VOP_FMT_YUV420SP; 2888c2ecf20Sopenharmony_ci case DRM_FORMAT_NV16: 2898c2ecf20Sopenharmony_ci return VOP_FMT_YUV422SP; 2908c2ecf20Sopenharmony_ci case DRM_FORMAT_NV24: 2918c2ecf20Sopenharmony_ci return VOP_FMT_YUV444SP; 2928c2ecf20Sopenharmony_ci default: 2938c2ecf20Sopenharmony_ci DRM_ERROR("unsupported format[%08x]\n", format); 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int vop_convert_afbc_format(uint32_t format) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci switch (format) { 3018c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 3028c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 3038c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR8888: 3048c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR8888: 3058c2ecf20Sopenharmony_ci return AFBC_FMT_U8U8U8U8; 3068c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB888: 3078c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR888: 3088c2ecf20Sopenharmony_ci return AFBC_FMT_U8U8U8; 3098c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 3108c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR565: 3118c2ecf20Sopenharmony_ci return AFBC_FMT_RGB565; 3128c2ecf20Sopenharmony_ci /* either of the below should not be reachable */ 3138c2ecf20Sopenharmony_ci default: 3148c2ecf20Sopenharmony_ci DRM_WARN_ONCE("unsupported AFBC format[%08x]\n", format); 3158c2ecf20Sopenharmony_ci return -EINVAL; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src, 3228c2ecf20Sopenharmony_ci uint32_t dst, bool is_horizontal, 3238c2ecf20Sopenharmony_ci int vsu_mode, int *vskiplines) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (vskiplines) 3288c2ecf20Sopenharmony_ci *vskiplines = 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (is_horizontal) { 3318c2ecf20Sopenharmony_ci if (mode == SCALE_UP) 3328c2ecf20Sopenharmony_ci val = GET_SCL_FT_BIC(src, dst); 3338c2ecf20Sopenharmony_ci else if (mode == SCALE_DOWN) 3348c2ecf20Sopenharmony_ci val = GET_SCL_FT_BILI_DN(src, dst); 3358c2ecf20Sopenharmony_ci } else { 3368c2ecf20Sopenharmony_ci if (mode == SCALE_UP) { 3378c2ecf20Sopenharmony_ci if (vsu_mode == SCALE_UP_BIL) 3388c2ecf20Sopenharmony_ci val = GET_SCL_FT_BILI_UP(src, dst); 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci val = GET_SCL_FT_BIC(src, dst); 3418c2ecf20Sopenharmony_ci } else if (mode == SCALE_DOWN) { 3428c2ecf20Sopenharmony_ci if (vskiplines) { 3438c2ecf20Sopenharmony_ci *vskiplines = scl_get_vskiplines(src, dst); 3448c2ecf20Sopenharmony_ci val = scl_get_bili_dn_vskip(src, dst, 3458c2ecf20Sopenharmony_ci *vskiplines); 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci val = GET_SCL_FT_BILI_DN(src, dst); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return val; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, 3568c2ecf20Sopenharmony_ci uint32_t src_w, uint32_t src_h, uint32_t dst_w, 3578c2ecf20Sopenharmony_ci uint32_t dst_h, const struct drm_format_info *info) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode; 3608c2ecf20Sopenharmony_ci uint16_t cbcr_hor_scl_mode = SCALE_NONE; 3618c2ecf20Sopenharmony_ci uint16_t cbcr_ver_scl_mode = SCALE_NONE; 3628c2ecf20Sopenharmony_ci bool is_yuv = false; 3638c2ecf20Sopenharmony_ci uint16_t cbcr_src_w = src_w / info->hsub; 3648c2ecf20Sopenharmony_ci uint16_t cbcr_src_h = src_h / info->vsub; 3658c2ecf20Sopenharmony_ci uint16_t vsu_mode; 3668c2ecf20Sopenharmony_ci uint16_t lb_mode; 3678c2ecf20Sopenharmony_ci uint32_t val; 3688c2ecf20Sopenharmony_ci int vskiplines; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (info->is_yuv) 3718c2ecf20Sopenharmony_ci is_yuv = true; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (dst_w > 3840) { 3748c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n"); 3758c2ecf20Sopenharmony_ci return; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!win->phy->scl->ext) { 3798c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_x, 3808c2ecf20Sopenharmony_ci scl_cal_scale2(src_w, dst_w)); 3818c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_y, 3828c2ecf20Sopenharmony_ci scl_cal_scale2(src_h, dst_h)); 3838c2ecf20Sopenharmony_ci if (is_yuv) { 3848c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_x, 3858c2ecf20Sopenharmony_ci scl_cal_scale2(cbcr_src_w, dst_w)); 3868c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_y, 3878c2ecf20Sopenharmony_ci scl_cal_scale2(cbcr_src_h, dst_h)); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci return; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w); 3938c2ecf20Sopenharmony_ci yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (is_yuv) { 3968c2ecf20Sopenharmony_ci cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w); 3978c2ecf20Sopenharmony_ci cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h); 3988c2ecf20Sopenharmony_ci if (cbcr_hor_scl_mode == SCALE_DOWN) 3998c2ecf20Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(dst_w, true); 4008c2ecf20Sopenharmony_ci else 4018c2ecf20Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true); 4028c2ecf20Sopenharmony_ci } else { 4038c2ecf20Sopenharmony_ci if (yrgb_hor_scl_mode == SCALE_DOWN) 4048c2ecf20Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(dst_w, false); 4058c2ecf20Sopenharmony_ci else 4068c2ecf20Sopenharmony_ci lb_mode = scl_vop_cal_lb_mode(src_w, false); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, lb_mode, lb_mode); 4108c2ecf20Sopenharmony_ci if (lb_mode == LB_RGB_3840X2) { 4118c2ecf20Sopenharmony_ci if (yrgb_ver_scl_mode != SCALE_NONE) { 4128c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "not allow yrgb ver scale\n"); 4138c2ecf20Sopenharmony_ci return; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci if (cbcr_ver_scl_mode != SCALE_NONE) { 4168c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "not allow cbcr ver scale\n"); 4178c2ecf20Sopenharmony_ci return; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci vsu_mode = SCALE_UP_BIL; 4208c2ecf20Sopenharmony_ci } else if (lb_mode == LB_RGB_2560X4) { 4218c2ecf20Sopenharmony_ci vsu_mode = SCALE_UP_BIL; 4228c2ecf20Sopenharmony_ci } else { 4238c2ecf20Sopenharmony_ci vsu_mode = SCALE_UP_BIC; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w, 4278c2ecf20Sopenharmony_ci true, 0, NULL); 4288c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_x, val); 4298c2ecf20Sopenharmony_ci val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h, 4308c2ecf20Sopenharmony_ci false, vsu_mode, &vskiplines); 4318c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_yrgb_y, val); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt4, vskiplines == 4); 4348c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt2, vskiplines == 2); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, yrgb_hor_scl_mode); 4378c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, yrgb_ver_scl_mode); 4388c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_hsd_mode, SCALE_DOWN_BIL); 4398c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_vsd_mode, SCALE_DOWN_BIL); 4408c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_vsu_mode, vsu_mode); 4418c2ecf20Sopenharmony_ci if (is_yuv) { 4428c2ecf20Sopenharmony_ci val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w, 4438c2ecf20Sopenharmony_ci dst_w, true, 0, NULL); 4448c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_x, val); 4458c2ecf20Sopenharmony_ci val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h, 4468c2ecf20Sopenharmony_ci dst_h, false, vsu_mode, &vskiplines); 4478c2ecf20Sopenharmony_ci VOP_SCL_SET(vop, win, scale_cbcr_y, val); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt4, vskiplines == 4); 4508c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt2, vskiplines == 2); 4518c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, cbcr_hor_scl_mode); 4528c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, cbcr_ver_scl_mode); 4538c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_hsd_mode, SCALE_DOWN_BIL); 4548c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_vsd_mode, SCALE_DOWN_BIL); 4558c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_vsu_mode, vsu_mode); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic void vop_dsp_hold_valid_irq_enable(struct vop *vop) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci unsigned long flags; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 4648c2ecf20Sopenharmony_ci return; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1); 4698c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void vop_dsp_hold_valid_irq_disable(struct vop *vop) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci unsigned long flags; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 0); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* 4898c2ecf20Sopenharmony_ci * (1) each frame starts at the start of the Vsync pulse which is signaled by 4908c2ecf20Sopenharmony_ci * the "FRAME_SYNC" interrupt. 4918c2ecf20Sopenharmony_ci * (2) the active data region of each frame ends at dsp_vact_end 4928c2ecf20Sopenharmony_ci * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num, 4938c2ecf20Sopenharmony_ci * to get "LINE_FLAG" interrupt at the end of the active on screen data. 4948c2ecf20Sopenharmony_ci * 4958c2ecf20Sopenharmony_ci * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end 4968c2ecf20Sopenharmony_ci * Interrupts 4978c2ecf20Sopenharmony_ci * LINE_FLAG -------------------------------+ 4988c2ecf20Sopenharmony_ci * FRAME_SYNC ----+ | 4998c2ecf20Sopenharmony_ci * | | 5008c2ecf20Sopenharmony_ci * v v 5018c2ecf20Sopenharmony_ci * | Vsync | Vbp | Vactive | Vfp | 5028c2ecf20Sopenharmony_ci * ^ ^ ^ ^ 5038c2ecf20Sopenharmony_ci * | | | | 5048c2ecf20Sopenharmony_ci * | | | | 5058c2ecf20Sopenharmony_ci * dsp_vs_end ------------+ | | | VOP_DSP_VTOTAL_VS_END 5068c2ecf20Sopenharmony_ci * dsp_vact_start --------------+ | | VOP_DSP_VACT_ST_END 5078c2ecf20Sopenharmony_ci * dsp_vact_end ----------------------------+ | VOP_DSP_VACT_ST_END 5088c2ecf20Sopenharmony_ci * dsp_total -------------------------------------+ VOP_DSP_VTOTAL_VS_END 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_cistatic bool vop_line_flag_irq_is_enabled(struct vop *vop) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci uint32_t line_flag_irq; 5138c2ecf20Sopenharmony_ci unsigned long flags; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return !!line_flag_irq; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic void vop_line_flag_irq_enable(struct vop *vop) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci unsigned long flags; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 5298c2ecf20Sopenharmony_ci return; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1); 5348c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void vop_line_flag_irq_disable(struct vop *vop) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci unsigned long flags; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 5448c2ecf20Sopenharmony_ci return; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int vop_core_clks_enable(struct vop *vop) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci int ret; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = clk_enable(vop->hclk); 5588c2ecf20Sopenharmony_ci if (ret < 0) 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci ret = clk_enable(vop->aclk); 5628c2ecf20Sopenharmony_ci if (ret < 0) 5638c2ecf20Sopenharmony_ci goto err_disable_hclk; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cierr_disable_hclk: 5688c2ecf20Sopenharmony_ci clk_disable(vop->hclk); 5698c2ecf20Sopenharmony_ci return ret; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic void vop_core_clks_disable(struct vop *vop) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci clk_disable(vop->aclk); 5758c2ecf20Sopenharmony_ci clk_disable(vop->hclk); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic void vop_win_disable(struct vop *vop, const struct vop_win *vop_win) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (win->phy->scl && win->phy->scl->ext) { 5838c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE); 5848c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE); 5858c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE); 5868c2ecf20Sopenharmony_ci VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, enable, 0); 5908c2ecf20Sopenharmony_ci vop->win_enabled &= ~BIT(VOP_WIN_TO_INDEX(vop_win)); 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 5968c2ecf20Sopenharmony_ci int ret, i; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(vop->dev); 5998c2ecf20Sopenharmony_ci if (ret < 0) { 6008c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); 6018c2ecf20Sopenharmony_ci return ret; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci ret = vop_core_clks_enable(vop); 6058c2ecf20Sopenharmony_ci if (WARN_ON(ret < 0)) 6068c2ecf20Sopenharmony_ci goto err_put_pm_runtime; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci ret = clk_enable(vop->dclk); 6098c2ecf20Sopenharmony_ci if (WARN_ON(ret < 0)) 6108c2ecf20Sopenharmony_ci goto err_disable_core; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* 6138c2ecf20Sopenharmony_ci * Slave iommu shares power, irq and clock with vop. It was associated 6148c2ecf20Sopenharmony_ci * automatically with this master device via common driver code. 6158c2ecf20Sopenharmony_ci * Now that we have enabled the clock we attach it to the shared drm 6168c2ecf20Sopenharmony_ci * mapping. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev); 6198c2ecf20Sopenharmony_ci if (ret) { 6208c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, 6218c2ecf20Sopenharmony_ci "failed to attach dma mapping, %d\n", ret); 6228c2ecf20Sopenharmony_ci goto err_disable_dclk; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 6268c2ecf20Sopenharmony_ci for (i = 0; i < vop->len; i += 4) 6278c2ecf20Sopenharmony_ci writel_relaxed(vop->regsbak[i / 4], vop->regs + i); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * We need to make sure that all windows are disabled before we 6318c2ecf20Sopenharmony_ci * enable the crtc. Otherwise we might try to scan from a destroyed 6328c2ecf20Sopenharmony_ci * buffer later. 6338c2ecf20Sopenharmony_ci * 6348c2ecf20Sopenharmony_ci * In the case of enable-after-PSR, we don't need to worry about this 6358c2ecf20Sopenharmony_ci * case since the buffer is guaranteed to be valid and disabling the 6368c2ecf20Sopenharmony_ci * window will result in screen glitches on PSR exit. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci if (!old_state || !old_state->self_refresh_active) { 6398c2ecf20Sopenharmony_ci for (i = 0; i < vop->data->win_size; i++) { 6408c2ecf20Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci vop_win_disable(vop, vop_win); 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (vop->data->afbc) { 6478c2ecf20Sopenharmony_ci struct rockchip_crtc_state *s; 6488c2ecf20Sopenharmony_ci /* 6498c2ecf20Sopenharmony_ci * Disable AFBC and forget there was a vop window with AFBC 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci VOP_AFBC_SET(vop, enable, 0); 6528c2ecf20Sopenharmony_ci s = to_rockchip_crtc_state(crtc->state); 6538c2ecf20Sopenharmony_ci s->enable_afbc = false; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci vop_cfg_done(vop); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* 6618c2ecf20Sopenharmony_ci * At here, vop clock & iommu is enable, R/W vop regs would be safe. 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci vop->is_enabled = true; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, standby, 1); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cierr_disable_dclk: 6768c2ecf20Sopenharmony_ci clk_disable(vop->dclk); 6778c2ecf20Sopenharmony_cierr_disable_core: 6788c2ecf20Sopenharmony_ci vop_core_clks_disable(vop); 6798c2ecf20Sopenharmony_cierr_put_pm_runtime: 6808c2ecf20Sopenharmony_ci pm_runtime_put_sync(vop->dev); 6818c2ecf20Sopenharmony_ci return ret; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic void rockchip_drm_set_win_enabled(struct drm_crtc *crtc, bool enabled) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 6878c2ecf20Sopenharmony_ci int i; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci for (i = 0; i < vop->data->win_size; i++) { 6928c2ecf20Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 6938c2ecf20Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, enable, 6968c2ecf20Sopenharmony_ci enabled && (vop->win_enabled & BIT(i))); 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci vop_cfg_done(vop); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic void vop_crtc_atomic_disable(struct drm_crtc *crtc, 7048c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci WARN_ON(vop->event); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (crtc->state->self_refresh_active) 7118c2ecf20Sopenharmony_ci rockchip_drm_set_win_enabled(crtc, false); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (crtc->state->self_refresh_active) 7148c2ecf20Sopenharmony_ci goto out; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci mutex_lock(&vop->vop_lock); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* 7218c2ecf20Sopenharmony_ci * Vop standby will take effect at end of current frame, 7228c2ecf20Sopenharmony_ci * if dsp hold valid irq happen, it means standby complete. 7238c2ecf20Sopenharmony_ci * 7248c2ecf20Sopenharmony_ci * we must wait standby complete when we want to disable aclk, 7258c2ecf20Sopenharmony_ci * if not, memory bus maybe dead. 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ci reinit_completion(&vop->dsp_hold_completion); 7288c2ecf20Sopenharmony_ci vop_dsp_hold_valid_irq_enable(vop); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, standby, 1); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci wait_for_completion(&vop->dsp_hold_completion); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci vop_dsp_hold_valid_irq_disable(vop); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci vop->is_enabled = false; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* 7438c2ecf20Sopenharmony_ci * vop standby complete, so iommu detach is safe. 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci clk_disable(vop->dclk); 7488c2ecf20Sopenharmony_ci vop_core_clks_disable(vop); 7498c2ecf20Sopenharmony_ci pm_runtime_put(vop->dev); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci mutex_unlock(&vop->vop_lock); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ciout: 7548c2ecf20Sopenharmony_ci if (crtc->state->event && !crtc->state->active) { 7558c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 7568c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 7578c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci crtc->state->event = NULL; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic void vop_plane_destroy(struct drm_plane *plane) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci drm_plane_cleanup(plane); 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic inline bool rockchip_afbc(u64 modifier) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci return modifier == ROCKCHIP_AFBC_MOD; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic bool rockchip_mod_supported(struct drm_plane *plane, 7748c2ecf20Sopenharmony_ci u32 format, u64 modifier) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_LINEAR) 7778c2ecf20Sopenharmony_ci return true; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (!rockchip_afbc(modifier)) { 7808c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Unsupported format modifier 0x%llx\n", modifier); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci return false; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return vop_convert_afbc_format(format) >= 0; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int vop_plane_atomic_check(struct drm_plane *plane, 7898c2ecf20Sopenharmony_ci struct drm_plane_state *state) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct drm_crtc *crtc = state->crtc; 7928c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 7938c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 7948c2ecf20Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 7958c2ecf20Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 7968c2ecf20Sopenharmony_ci int ret; 7978c2ecf20Sopenharmony_ci int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : 7988c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING; 7998c2ecf20Sopenharmony_ci int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : 8008c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (!crtc || WARN_ON(!fb)) 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); 8068c2ecf20Sopenharmony_ci if (WARN_ON(!crtc_state)) 8078c2ecf20Sopenharmony_ci return -EINVAL; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(state, crtc_state, 8108c2ecf20Sopenharmony_ci min_scale, max_scale, 8118c2ecf20Sopenharmony_ci true, true); 8128c2ecf20Sopenharmony_ci if (ret) 8138c2ecf20Sopenharmony_ci return ret; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (!state->visible) 8168c2ecf20Sopenharmony_ci return 0; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci ret = vop_convert_format(fb->format->format); 8198c2ecf20Sopenharmony_ci if (ret < 0) 8208c2ecf20Sopenharmony_ci return ret; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* 8238c2ecf20Sopenharmony_ci * Src.x1 can be odd when do clip, but yuv plane start point 8248c2ecf20Sopenharmony_ci * need align with 2 pixel. 8258c2ecf20Sopenharmony_ci */ 8268c2ecf20Sopenharmony_ci if (fb->format->is_yuv && ((state->src.x1 >> 16) % 2)) { 8278c2ecf20Sopenharmony_ci DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n"); 8288c2ecf20Sopenharmony_ci return -EINVAL; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (fb->format->is_yuv && state->rotation & DRM_MODE_REFLECT_Y) { 8328c2ecf20Sopenharmony_ci DRM_ERROR("Invalid Source: Yuv format does not support this rotation\n"); 8338c2ecf20Sopenharmony_ci return -EINVAL; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (rockchip_afbc(fb->modifier)) { 8378c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (!vop->data->afbc) { 8408c2ecf20Sopenharmony_ci DRM_ERROR("vop does not support AFBC\n"); 8418c2ecf20Sopenharmony_ci return -EINVAL; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci ret = vop_convert_afbc_format(fb->format->format); 8458c2ecf20Sopenharmony_ci if (ret < 0) 8468c2ecf20Sopenharmony_ci return ret; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (state->src.x1 || state->src.y1) { 8498c2ecf20Sopenharmony_ci DRM_ERROR("AFBC does not support offset display, xpos=%d, ypos=%d, offset=%d\n", state->src.x1, state->src.y1, fb->offsets[0]); 8508c2ecf20Sopenharmony_ci return -EINVAL; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (state->rotation && state->rotation != DRM_MODE_ROTATE_0) { 8548c2ecf20Sopenharmony_ci DRM_ERROR("No rotation support in AFBC, rotation=%d\n", 8558c2ecf20Sopenharmony_ci state->rotation); 8568c2ecf20Sopenharmony_ci return -EINVAL; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return 0; 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic void vop_plane_atomic_disable(struct drm_plane *plane, 8648c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 8678c2ecf20Sopenharmony_ci struct vop *vop = to_vop(old_state->crtc); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (!old_state->crtc) 8708c2ecf20Sopenharmony_ci return; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci vop_win_disable(vop, vop_win); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic void vop_plane_atomic_update(struct drm_plane *plane, 8808c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 8838c2ecf20Sopenharmony_ci struct drm_crtc *crtc = state->crtc; 8848c2ecf20Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 8858c2ecf20Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 8868c2ecf20Sopenharmony_ci const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data; 8878c2ecf20Sopenharmony_ci struct vop *vop = to_vop(state->crtc); 8888c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 8898c2ecf20Sopenharmony_ci unsigned int actual_w, actual_h; 8908c2ecf20Sopenharmony_ci unsigned int dsp_stx, dsp_sty; 8918c2ecf20Sopenharmony_ci uint32_t act_info, dsp_info, dsp_st; 8928c2ecf20Sopenharmony_ci struct drm_rect *src = &state->src; 8938c2ecf20Sopenharmony_ci struct drm_rect *dest = &state->dst; 8948c2ecf20Sopenharmony_ci struct drm_gem_object *obj, *uv_obj; 8958c2ecf20Sopenharmony_ci struct rockchip_gem_object *rk_obj, *rk_uv_obj; 8968c2ecf20Sopenharmony_ci unsigned long offset; 8978c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 8988c2ecf20Sopenharmony_ci uint32_t val; 8998c2ecf20Sopenharmony_ci bool rb_swap; 9008c2ecf20Sopenharmony_ci int win_index = VOP_WIN_TO_INDEX(vop_win); 9018c2ecf20Sopenharmony_ci int format; 9028c2ecf20Sopenharmony_ci int is_yuv = fb->format->is_yuv; 9038c2ecf20Sopenharmony_ci int i; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* 9068c2ecf20Sopenharmony_ci * can't update plane when vop is disabled. 9078c2ecf20Sopenharmony_ci */ 9088c2ecf20Sopenharmony_ci if (WARN_ON(!crtc)) 9098c2ecf20Sopenharmony_ci return; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 9128c2ecf20Sopenharmony_ci return; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (!state->visible) { 9158c2ecf20Sopenharmony_ci vop_plane_atomic_disable(plane, old_state); 9168c2ecf20Sopenharmony_ci return; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci obj = fb->obj[0]; 9208c2ecf20Sopenharmony_ci rk_obj = to_rockchip_obj(obj); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci actual_w = drm_rect_width(src) >> 16; 9238c2ecf20Sopenharmony_ci actual_h = drm_rect_height(src) >> 16; 9248c2ecf20Sopenharmony_ci act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci dsp_info = (drm_rect_height(dest) - 1) << 16; 9278c2ecf20Sopenharmony_ci dsp_info |= (drm_rect_width(dest) - 1) & 0xffff; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci dsp_stx = dest->x1 + crtc->mode.htotal - crtc->mode.hsync_start; 9308c2ecf20Sopenharmony_ci dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start; 9318c2ecf20Sopenharmony_ci dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci offset = (src->x1 >> 16) * fb->format->cpp[0]; 9348c2ecf20Sopenharmony_ci offset += (src->y1 >> 16) * fb->pitches[0]; 9358c2ecf20Sopenharmony_ci dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* 9388c2ecf20Sopenharmony_ci * For y-mirroring we need to move address 9398c2ecf20Sopenharmony_ci * to the beginning of the last line. 9408c2ecf20Sopenharmony_ci */ 9418c2ecf20Sopenharmony_ci if (state->rotation & DRM_MODE_REFLECT_Y) 9428c2ecf20Sopenharmony_ci dma_addr += (actual_h - 1) * fb->pitches[0]; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci format = vop_convert_format(fb->format->format); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (rockchip_afbc(fb->modifier)) { 9498c2ecf20Sopenharmony_ci int afbc_format = vop_convert_afbc_format(fb->format->format); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16); 9528c2ecf20Sopenharmony_ci VOP_AFBC_SET(vop, hreg_block_split, 0); 9538c2ecf20Sopenharmony_ci VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win)); 9548c2ecf20Sopenharmony_ci VOP_AFBC_SET(vop, hdr_ptr, dma_addr); 9558c2ecf20Sopenharmony_ci VOP_AFBC_SET(vop, pic_size, act_info); 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, format, format); 9598c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); 9608c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); 9618c2ecf20Sopenharmony_ci VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv); 9628c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, y_mir_en, 9638c2ecf20Sopenharmony_ci (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0); 9648c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, x_mir_en, 9658c2ecf20Sopenharmony_ci (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (is_yuv) { 9688c2ecf20Sopenharmony_ci int hsub = fb->format->hsub; 9698c2ecf20Sopenharmony_ci int vsub = fb->format->vsub; 9708c2ecf20Sopenharmony_ci int bpp = fb->format->cpp[1]; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci uv_obj = fb->obj[1]; 9738c2ecf20Sopenharmony_ci rk_uv_obj = to_rockchip_obj(uv_obj); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci offset = (src->x1 >> 16) * bpp / hsub; 9768c2ecf20Sopenharmony_ci offset += (src->y1 >> 16) * fb->pitches[1] / vsub; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; 9798c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); 9808c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, uv_mst, dma_addr); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) { 9838c2ecf20Sopenharmony_ci VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, 9848c2ecf20Sopenharmony_ci win_yuv2yuv, 9858c2ecf20Sopenharmony_ci y2r_coefficients[i], 9868c2ecf20Sopenharmony_ci bt601_yuv2rgb[i]); 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (win->phy->scl) 9918c2ecf20Sopenharmony_ci scl_vop_cal_scl_fac(vop, win, actual_w, actual_h, 9928c2ecf20Sopenharmony_ci drm_rect_width(dest), drm_rect_height(dest), 9938c2ecf20Sopenharmony_ci fb->format); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, act_info, act_info); 9968c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, dsp_info, dsp_info); 9978c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, dsp_st, dsp_st); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci rb_swap = has_rb_swapped(vop->data->version, fb->format->format); 10008c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, rb_swap, rb_swap); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* 10038c2ecf20Sopenharmony_ci * Blending win0 with the background color doesn't seem to work 10048c2ecf20Sopenharmony_ci * correctly. We only get the background color, no matter the contents 10058c2ecf20Sopenharmony_ci * of the win0 framebuffer. However, blending pre-multiplied color 10068c2ecf20Sopenharmony_ci * with the default opaque black default background color is a no-op, 10078c2ecf20Sopenharmony_ci * so we can just disable blending to get the correct result. 10088c2ecf20Sopenharmony_ci */ 10098c2ecf20Sopenharmony_ci if (fb->format->has_alpha && win_index > 0) { 10108c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, dst_alpha_ctl, 10118c2ecf20Sopenharmony_ci DST_FACTOR_M0(ALPHA_SRC_INVERSE)); 10128c2ecf20Sopenharmony_ci val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) | 10138c2ecf20Sopenharmony_ci SRC_ALPHA_M0(ALPHA_STRAIGHT) | 10148c2ecf20Sopenharmony_ci SRC_BLEND_M0(ALPHA_PER_PIX) | 10158c2ecf20Sopenharmony_ci SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) | 10168c2ecf20Sopenharmony_ci SRC_FACTOR_M0(ALPHA_ONE); 10178c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, src_alpha_ctl, val); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_pre_mul, ALPHA_SRC_PRE_MUL); 10208c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_mode, ALPHA_PER_PIX); 10218c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_en, 1); 10228c2ecf20Sopenharmony_ci } else { 10238c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0)); 10248c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, alpha_en, 0); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, enable, 1); 10288c2ecf20Sopenharmony_ci vop->win_enabled |= BIT(win_index); 10298c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic int vop_plane_atomic_async_check(struct drm_plane *plane, 10338c2ecf20Sopenharmony_ci struct drm_plane_state *state) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct vop_win *vop_win = to_vop_win(plane); 10368c2ecf20Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 10378c2ecf20Sopenharmony_ci int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : 10388c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING; 10398c2ecf20Sopenharmony_ci int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : 10408c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING; 10418c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (plane != state->crtc->cursor) 10448c2ecf20Sopenharmony_ci return -EINVAL; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (!plane->state) 10478c2ecf20Sopenharmony_ci return -EINVAL; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (!plane->state->fb) 10508c2ecf20Sopenharmony_ci return -EINVAL; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (state->state) 10538c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state->state, 10548c2ecf20Sopenharmony_ci state->crtc); 10558c2ecf20Sopenharmony_ci else /* Special case for asynchronous cursor updates. */ 10568c2ecf20Sopenharmony_ci crtc_state = plane->crtc->state; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci return drm_atomic_helper_check_plane_state(plane->state, crtc_state, 10598c2ecf20Sopenharmony_ci min_scale, max_scale, 10608c2ecf20Sopenharmony_ci true, true); 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic void vop_plane_atomic_async_update(struct drm_plane *plane, 10648c2ecf20Sopenharmony_ci struct drm_plane_state *new_state) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci struct vop *vop = to_vop(plane->state->crtc); 10678c2ecf20Sopenharmony_ci struct drm_framebuffer *old_fb = plane->state->fb; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci plane->state->crtc_x = new_state->crtc_x; 10708c2ecf20Sopenharmony_ci plane->state->crtc_y = new_state->crtc_y; 10718c2ecf20Sopenharmony_ci plane->state->crtc_h = new_state->crtc_h; 10728c2ecf20Sopenharmony_ci plane->state->crtc_w = new_state->crtc_w; 10738c2ecf20Sopenharmony_ci plane->state->src_x = new_state->src_x; 10748c2ecf20Sopenharmony_ci plane->state->src_y = new_state->src_y; 10758c2ecf20Sopenharmony_ci plane->state->src_h = new_state->src_h; 10768c2ecf20Sopenharmony_ci plane->state->src_w = new_state->src_w; 10778c2ecf20Sopenharmony_ci swap(plane->state->fb, new_state->fb); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (vop->is_enabled) { 10808c2ecf20Sopenharmony_ci vop_plane_atomic_update(plane, plane->state); 10818c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 10828c2ecf20Sopenharmony_ci vop_cfg_done(vop); 10838c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* 10868c2ecf20Sopenharmony_ci * A scanout can still be occurring, so we can't drop the 10878c2ecf20Sopenharmony_ci * reference to the old framebuffer. To solve this we get a 10888c2ecf20Sopenharmony_ci * reference to old_fb and set a worker to release it later. 10898c2ecf20Sopenharmony_ci * FIXME: if we perform 500 async_update calls before the 10908c2ecf20Sopenharmony_ci * vblank, then we can have 500 different framebuffers waiting 10918c2ecf20Sopenharmony_ci * to be released. 10928c2ecf20Sopenharmony_ci */ 10938c2ecf20Sopenharmony_ci if (old_fb && plane->state->fb != old_fb) { 10948c2ecf20Sopenharmony_ci drm_framebuffer_get(old_fb); 10958c2ecf20Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0); 10968c2ecf20Sopenharmony_ci drm_flip_work_queue(&vop->fb_unref_work, old_fb); 10978c2ecf20Sopenharmony_ci set_bit(VOP_PENDING_FB_UNREF, &vop->pending); 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs plane_helper_funcs = { 11038c2ecf20Sopenharmony_ci .atomic_check = vop_plane_atomic_check, 11048c2ecf20Sopenharmony_ci .atomic_update = vop_plane_atomic_update, 11058c2ecf20Sopenharmony_ci .atomic_disable = vop_plane_atomic_disable, 11068c2ecf20Sopenharmony_ci .atomic_async_check = vop_plane_atomic_async_check, 11078c2ecf20Sopenharmony_ci .atomic_async_update = vop_plane_atomic_async_update, 11088c2ecf20Sopenharmony_ci .prepare_fb = drm_gem_fb_prepare_fb, 11098c2ecf20Sopenharmony_ci}; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs vop_plane_funcs = { 11128c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 11138c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 11148c2ecf20Sopenharmony_ci .destroy = vop_plane_destroy, 11158c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 11168c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 11178c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 11188c2ecf20Sopenharmony_ci .format_mod_supported = rockchip_mod_supported, 11198c2ecf20Sopenharmony_ci}; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic int vop_crtc_enable_vblank(struct drm_crtc *crtc) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 11248c2ecf20Sopenharmony_ci unsigned long flags; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 11278c2ecf20Sopenharmony_ci return -EPERM; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1); 11328c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic void vop_crtc_disable_vblank(struct drm_crtc *crtc) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 11428c2ecf20Sopenharmony_ci unsigned long flags; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 11458c2ecf20Sopenharmony_ci return; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci spin_lock_irqsave(&vop->irq_lock, flags); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vop->irq_lock, flags); 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic bool vop_crtc_mode_fixup(struct drm_crtc *crtc, 11558c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 11568c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 11598c2ecf20Sopenharmony_ci unsigned long rate; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* 11628c2ecf20Sopenharmony_ci * Clock craziness. 11638c2ecf20Sopenharmony_ci * 11648c2ecf20Sopenharmony_ci * Key points: 11658c2ecf20Sopenharmony_ci * 11668c2ecf20Sopenharmony_ci * - DRM works in in kHz. 11678c2ecf20Sopenharmony_ci * - Clock framework works in Hz. 11688c2ecf20Sopenharmony_ci * - Rockchip's clock driver picks the clock rate that is the 11698c2ecf20Sopenharmony_ci * same _OR LOWER_ than the one requested. 11708c2ecf20Sopenharmony_ci * 11718c2ecf20Sopenharmony_ci * Action plan: 11728c2ecf20Sopenharmony_ci * 11738c2ecf20Sopenharmony_ci * 1. When DRM gives us a mode, we should add 999 Hz to it. That way 11748c2ecf20Sopenharmony_ci * if the clock we need is 60000001 Hz (~60 MHz) and DRM tells us to 11758c2ecf20Sopenharmony_ci * make 60000 kHz then the clock framework will actually give us 11768c2ecf20Sopenharmony_ci * the right clock. 11778c2ecf20Sopenharmony_ci * 11788c2ecf20Sopenharmony_ci * NOTE: if the PLL (maybe through a divider) could actually make 11798c2ecf20Sopenharmony_ci * a clock rate 999 Hz higher instead of the one we want then this 11808c2ecf20Sopenharmony_ci * could be a problem. Unfortunately there's not much we can do 11818c2ecf20Sopenharmony_ci * since it's baked into DRM to use kHz. It shouldn't matter in 11828c2ecf20Sopenharmony_ci * practice since Rockchip PLLs are controlled by tables and 11838c2ecf20Sopenharmony_ci * even if there is a divider in the middle I wouldn't expect PLL 11848c2ecf20Sopenharmony_ci * rates in the table that are just a few kHz different. 11858c2ecf20Sopenharmony_ci * 11868c2ecf20Sopenharmony_ci * 2. Get the clock framework to round the rate for us to tell us 11878c2ecf20Sopenharmony_ci * what it will actually make. 11888c2ecf20Sopenharmony_ci * 11898c2ecf20Sopenharmony_ci * 3. Store the rounded up rate so that we don't need to worry about 11908c2ecf20Sopenharmony_ci * this in the actual clk_set_rate(). 11918c2ecf20Sopenharmony_ci */ 11928c2ecf20Sopenharmony_ci rate = clk_round_rate(vop->dclk, adjusted_mode->clock * 1000 + 999); 11938c2ecf20Sopenharmony_ci adjusted_mode->clock = DIV_ROUND_UP(rate, 1000); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return true; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic bool vop_dsp_lut_is_enabled(struct vop *vop) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en); 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistatic void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct drm_color_lut *lut = crtc->state->gamma_lut->data; 12068c2ecf20Sopenharmony_ci unsigned int i; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci for (i = 0; i < crtc->gamma_size; i++) { 12098c2ecf20Sopenharmony_ci u32 word; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci word = (drm_color_lut_extract(lut[i].red, 10) << 20) | 12128c2ecf20Sopenharmony_ci (drm_color_lut_extract(lut[i].green, 10) << 10) | 12138c2ecf20Sopenharmony_ci drm_color_lut_extract(lut[i].blue, 10); 12148c2ecf20Sopenharmony_ci writel(word, vop->lut_regs + i * 4); 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc, 12198c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct drm_crtc_state *state = crtc->state; 12228c2ecf20Sopenharmony_ci unsigned int idle; 12238c2ecf20Sopenharmony_ci int ret; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (!vop->lut_regs) 12268c2ecf20Sopenharmony_ci return; 12278c2ecf20Sopenharmony_ci /* 12288c2ecf20Sopenharmony_ci * To disable gamma (gamma_lut is null) or to write 12298c2ecf20Sopenharmony_ci * an update to the LUT, clear dsp_lut_en. 12308c2ecf20Sopenharmony_ci */ 12318c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 12328c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, dsp_lut_en, 0); 12338c2ecf20Sopenharmony_ci vop_cfg_done(vop); 12348c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci /* 12378c2ecf20Sopenharmony_ci * In order to write the LUT to the internal memory, 12388c2ecf20Sopenharmony_ci * we need to first make sure the dsp_lut_en bit is cleared. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ci ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop, 12418c2ecf20Sopenharmony_ci idle, !idle, 5, 30 * 1000); 12428c2ecf20Sopenharmony_ci if (ret) { 12438c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n"); 12448c2ecf20Sopenharmony_ci return; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (!state->gamma_lut) 12488c2ecf20Sopenharmony_ci return; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 12518c2ecf20Sopenharmony_ci vop_crtc_write_gamma_lut(vop, crtc); 12528c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, dsp_lut_en, 1); 12538c2ecf20Sopenharmony_ci vop_cfg_done(vop); 12548c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic void vop_crtc_atomic_begin(struct drm_crtc *crtc, 12588c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* 12638c2ecf20Sopenharmony_ci * Only update GAMMA if the 'active' flag is not changed, 12648c2ecf20Sopenharmony_ci * otherwise it's updated by .atomic_enable. 12658c2ecf20Sopenharmony_ci */ 12668c2ecf20Sopenharmony_ci if (crtc->state->color_mgmt_changed && 12678c2ecf20Sopenharmony_ci !crtc->state->active_changed) 12688c2ecf20Sopenharmony_ci vop_crtc_gamma_set(vop, crtc, old_crtc_state); 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic void vop_crtc_atomic_enable(struct drm_crtc *crtc, 12728c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 12758c2ecf20Sopenharmony_ci const struct vop_data *vop_data = vop->data; 12768c2ecf20Sopenharmony_ci struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); 12778c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; 12788c2ecf20Sopenharmony_ci u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start; 12798c2ecf20Sopenharmony_ci u16 hdisplay = adjusted_mode->hdisplay; 12808c2ecf20Sopenharmony_ci u16 htotal = adjusted_mode->htotal; 12818c2ecf20Sopenharmony_ci u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start; 12828c2ecf20Sopenharmony_ci u16 hact_end = hact_st + hdisplay; 12838c2ecf20Sopenharmony_ci u16 vdisplay = adjusted_mode->vdisplay; 12848c2ecf20Sopenharmony_ci u16 vtotal = adjusted_mode->vtotal; 12858c2ecf20Sopenharmony_ci u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start; 12868c2ecf20Sopenharmony_ci u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start; 12878c2ecf20Sopenharmony_ci u16 vact_end = vact_st + vdisplay; 12888c2ecf20Sopenharmony_ci uint32_t pin_pol, val; 12898c2ecf20Sopenharmony_ci int dither_bpc = s->output_bpc ? s->output_bpc : 10; 12908c2ecf20Sopenharmony_ci int ret; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (old_state && old_state->self_refresh_active) { 12938c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 12948c2ecf20Sopenharmony_ci rockchip_drm_set_win_enabled(crtc, true); 12958c2ecf20Sopenharmony_ci return; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* 12998c2ecf20Sopenharmony_ci * If we have a GAMMA LUT in the state, then let's make sure 13008c2ecf20Sopenharmony_ci * it's updated. We might be coming out of suspend, 13018c2ecf20Sopenharmony_ci * which means the LUT internal memory needs to be re-written. 13028c2ecf20Sopenharmony_ci */ 13038c2ecf20Sopenharmony_ci if (crtc->state->gamma_lut) 13048c2ecf20Sopenharmony_ci vop_crtc_gamma_set(vop, crtc, old_state); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci mutex_lock(&vop->vop_lock); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci WARN_ON(vop->event); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci ret = vop_enable(crtc, old_state); 13118c2ecf20Sopenharmony_ci if (ret) { 13128c2ecf20Sopenharmony_ci mutex_unlock(&vop->vop_lock); 13138c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret); 13148c2ecf20Sopenharmony_ci return; 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci pin_pol = (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ? 13178c2ecf20Sopenharmony_ci BIT(HSYNC_POSITIVE) : 0; 13188c2ecf20Sopenharmony_ci pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ? 13198c2ecf20Sopenharmony_ci BIT(VSYNC_POSITIVE) : 0; 13208c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, pin_pol, pin_pol); 13218c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, mipi_dual_channel_en, 0); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci switch (s->output_type) { 13248c2ecf20Sopenharmony_ci case DRM_MODE_CONNECTOR_LVDS: 13258c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, rgb_dclk_pol, 1); 13268c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol); 13278c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, rgb_en, 1); 13288c2ecf20Sopenharmony_ci break; 13298c2ecf20Sopenharmony_ci case DRM_MODE_CONNECTOR_eDP: 13308c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, edp_dclk_pol, 1); 13318c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, edp_pin_pol, pin_pol); 13328c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, edp_en, 1); 13338c2ecf20Sopenharmony_ci break; 13348c2ecf20Sopenharmony_ci case DRM_MODE_CONNECTOR_HDMIA: 13358c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, hdmi_dclk_pol, 1); 13368c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol); 13378c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, hdmi_en, 1); 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci case DRM_MODE_CONNECTOR_DSI: 13408c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, mipi_dclk_pol, 1); 13418c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol); 13428c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, mipi_en, 1); 13438c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, mipi_dual_channel_en, 13448c2ecf20Sopenharmony_ci !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL)); 13458c2ecf20Sopenharmony_ci break; 13468c2ecf20Sopenharmony_ci case DRM_MODE_CONNECTOR_DisplayPort: 13478c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, dp_dclk_pol, 0); 13488c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, dp_pin_pol, pin_pol); 13498c2ecf20Sopenharmony_ci VOP_REG_SET(vop, output, dp_en, 1); 13508c2ecf20Sopenharmony_ci break; 13518c2ecf20Sopenharmony_ci default: 13528c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n", 13538c2ecf20Sopenharmony_ci s->output_type); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* 13578c2ecf20Sopenharmony_ci * if vop is not support RGB10 output, need force RGB10 to RGB888. 13588c2ecf20Sopenharmony_ci */ 13598c2ecf20Sopenharmony_ci if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && 13608c2ecf20Sopenharmony_ci !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10)) 13618c2ecf20Sopenharmony_ci s->output_mode = ROCKCHIP_OUT_MODE_P888; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8) 13648c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, pre_dither_down, 1); 13658c2ecf20Sopenharmony_ci else 13668c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, pre_dither_down, 0); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (dither_bpc == 6) { 13698c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_sel, DITHER_DOWN_ALLEGRO); 13708c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_mode, RGB888_TO_RGB666); 13718c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_en, 1); 13728c2ecf20Sopenharmony_ci } else { 13738c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, dither_down_en, 0); 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, out_mode, s->output_mode); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len); 13798c2ecf20Sopenharmony_ci val = hact_st << 16; 13808c2ecf20Sopenharmony_ci val |= hact_end; 13818c2ecf20Sopenharmony_ci VOP_REG_SET(vop, modeset, hact_st_end, val); 13828c2ecf20Sopenharmony_ci VOP_REG_SET(vop, modeset, hpost_st_end, val); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci VOP_REG_SET(vop, modeset, vtotal_pw, (vtotal << 16) | vsync_len); 13858c2ecf20Sopenharmony_ci val = vact_st << 16; 13868c2ecf20Sopenharmony_ci val |= vact_end; 13878c2ecf20Sopenharmony_ci VOP_REG_SET(vop, modeset, vact_st_end, val); 13888c2ecf20Sopenharmony_ci VOP_REG_SET(vop, modeset, vpost_st_end, val); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci VOP_REG_SET(vop, intr, line_flag_num[0], vact_end); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, standby, 0); 13958c2ecf20Sopenharmony_ci mutex_unlock(&vop->vop_lock); 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic bool vop_fs_irq_is_pending(struct vop *vop) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci return VOP_INTR_GET_TYPE(vop, status, FS_INTR); 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_cistatic void vop_wait_for_irq_handler(struct vop *vop) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci bool pending; 14068c2ecf20Sopenharmony_ci int ret; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci /* 14098c2ecf20Sopenharmony_ci * Spin until frame start interrupt status bit goes low, which means 14108c2ecf20Sopenharmony_ci * that interrupt handler was invoked and cleared it. The timeout of 14118c2ecf20Sopenharmony_ci * 10 msecs is really too long, but it is just a safety measure if 14128c2ecf20Sopenharmony_ci * something goes really wrong. The wait will only happen in the very 14138c2ecf20Sopenharmony_ci * unlikely case of a vblank happening exactly at the same time and 14148c2ecf20Sopenharmony_ci * shouldn't exceed microseconds range. 14158c2ecf20Sopenharmony_ci */ 14168c2ecf20Sopenharmony_ci ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending, 14178c2ecf20Sopenharmony_ci !pending, 0, 10 * 1000); 14188c2ecf20Sopenharmony_ci if (ret) 14198c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n"); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci synchronize_irq(vop->irq); 14228c2ecf20Sopenharmony_ci} 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_cistatic int vop_crtc_atomic_check(struct drm_crtc *crtc, 14258c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 14288c2ecf20Sopenharmony_ci struct drm_plane *plane; 14298c2ecf20Sopenharmony_ci struct drm_plane_state *plane_state; 14308c2ecf20Sopenharmony_ci struct rockchip_crtc_state *s; 14318c2ecf20Sopenharmony_ci int afbc_planes = 0; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (vop->lut_regs && crtc_state->color_mgmt_changed && 14348c2ecf20Sopenharmony_ci crtc_state->gamma_lut) { 14358c2ecf20Sopenharmony_ci unsigned int len; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci len = drm_color_lut_size(crtc_state->gamma_lut); 14388c2ecf20Sopenharmony_ci if (len != crtc->gamma_size) { 14398c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n", 14408c2ecf20Sopenharmony_ci len, crtc->gamma_size); 14418c2ecf20Sopenharmony_ci return -EINVAL; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci drm_atomic_crtc_state_for_each_plane(plane, crtc_state) { 14468c2ecf20Sopenharmony_ci plane_state = 14478c2ecf20Sopenharmony_ci drm_atomic_get_plane_state(crtc_state->state, plane); 14488c2ecf20Sopenharmony_ci if (IS_ERR(plane_state)) { 14498c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Cannot get plane state for plane %s\n", 14508c2ecf20Sopenharmony_ci plane->name); 14518c2ecf20Sopenharmony_ci return PTR_ERR(plane_state); 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci if (drm_is_afbc(plane_state->fb->modifier)) 14558c2ecf20Sopenharmony_ci ++afbc_planes; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci if (afbc_planes > 1) { 14598c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes); 14608c2ecf20Sopenharmony_ci return -EINVAL; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci s = to_rockchip_crtc_state(crtc_state); 14648c2ecf20Sopenharmony_ci s->enable_afbc = afbc_planes > 0; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return 0; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic void vop_crtc_atomic_flush(struct drm_crtc *crtc, 14708c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 14718c2ecf20Sopenharmony_ci{ 14728c2ecf20Sopenharmony_ci struct drm_atomic_state *old_state = old_crtc_state->state; 14738c2ecf20Sopenharmony_ci struct drm_plane_state *old_plane_state, *new_plane_state; 14748c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 14758c2ecf20Sopenharmony_ci struct drm_plane *plane; 14768c2ecf20Sopenharmony_ci struct rockchip_crtc_state *s; 14778c2ecf20Sopenharmony_ci int i; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci if (WARN_ON(!vop->is_enabled)) 14808c2ecf20Sopenharmony_ci return; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci spin_lock(&vop->reg_lock); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci /* Enable AFBC if there is some AFBC window, disable otherwise. */ 14858c2ecf20Sopenharmony_ci s = to_rockchip_crtc_state(crtc->state); 14868c2ecf20Sopenharmony_ci VOP_AFBC_SET(vop, enable, s->enable_afbc); 14878c2ecf20Sopenharmony_ci vop_cfg_done(vop); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci spin_unlock(&vop->reg_lock); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci /* 14928c2ecf20Sopenharmony_ci * There is a (rather unlikely) possiblity that a vblank interrupt 14938c2ecf20Sopenharmony_ci * fired before we set the cfg_done bit. To avoid spuriously 14948c2ecf20Sopenharmony_ci * signalling flip completion we need to wait for it to finish. 14958c2ecf20Sopenharmony_ci */ 14968c2ecf20Sopenharmony_ci vop_wait_for_irq_handler(vop); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 14998c2ecf20Sopenharmony_ci if (crtc->state->event) { 15008c2ecf20Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 15018c2ecf20Sopenharmony_ci WARN_ON(vop->event); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci vop->event = crtc->state->event; 15048c2ecf20Sopenharmony_ci crtc->state->event = NULL; 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, 15098c2ecf20Sopenharmony_ci new_plane_state, i) { 15108c2ecf20Sopenharmony_ci if (!old_plane_state->fb) 15118c2ecf20Sopenharmony_ci continue; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci if (old_plane_state->fb == new_plane_state->fb) 15148c2ecf20Sopenharmony_ci continue; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci drm_framebuffer_get(old_plane_state->fb); 15178c2ecf20Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 15188c2ecf20Sopenharmony_ci drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb); 15198c2ecf20Sopenharmony_ci set_bit(VOP_PENDING_FB_UNREF, &vop->pending); 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { 15248c2ecf20Sopenharmony_ci .mode_fixup = vop_crtc_mode_fixup, 15258c2ecf20Sopenharmony_ci .atomic_check = vop_crtc_atomic_check, 15268c2ecf20Sopenharmony_ci .atomic_begin = vop_crtc_atomic_begin, 15278c2ecf20Sopenharmony_ci .atomic_flush = vop_crtc_atomic_flush, 15288c2ecf20Sopenharmony_ci .atomic_enable = vop_crtc_atomic_enable, 15298c2ecf20Sopenharmony_ci .atomic_disable = vop_crtc_atomic_disable, 15308c2ecf20Sopenharmony_ci}; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic void vop_crtc_destroy(struct drm_crtc *crtc) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 15358c2ecf20Sopenharmony_ci} 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_cistatic struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc) 15388c2ecf20Sopenharmony_ci{ 15398c2ecf20Sopenharmony_ci struct rockchip_crtc_state *rockchip_state; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (WARN_ON(!crtc->state)) 15428c2ecf20Sopenharmony_ci return NULL; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci rockchip_state = kmemdup(to_rockchip_crtc_state(crtc->state), 15458c2ecf20Sopenharmony_ci sizeof(*rockchip_state), GFP_KERNEL); 15468c2ecf20Sopenharmony_ci if (!rockchip_state) 15478c2ecf20Sopenharmony_ci return NULL; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &rockchip_state->base); 15508c2ecf20Sopenharmony_ci return &rockchip_state->base; 15518c2ecf20Sopenharmony_ci} 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_cistatic void vop_crtc_destroy_state(struct drm_crtc *crtc, 15548c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 15558c2ecf20Sopenharmony_ci{ 15568c2ecf20Sopenharmony_ci struct rockchip_crtc_state *s = to_rockchip_crtc_state(state); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(&s->base); 15598c2ecf20Sopenharmony_ci kfree(s); 15608c2ecf20Sopenharmony_ci} 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic void vop_crtc_reset(struct drm_crtc *crtc) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci struct rockchip_crtc_state *crtc_state = 15658c2ecf20Sopenharmony_ci kzalloc(sizeof(*crtc_state), GFP_KERNEL); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci if (crtc->state) 15688c2ecf20Sopenharmony_ci vop_crtc_destroy_state(crtc, crtc->state); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci if (crtc_state) 15718c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base); 15728c2ecf20Sopenharmony_ci else 15738c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, NULL); 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_ANALOGIX_DP 15778c2ecf20Sopenharmony_cistatic struct drm_connector *vop_get_edp_connector(struct vop *vop) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci struct drm_connector *connector; 15808c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(vop->drm_dev, &conn_iter); 15838c2ecf20Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 15848c2ecf20Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { 15858c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 15868c2ecf20Sopenharmony_ci return connector; 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci return NULL; 15928c2ecf20Sopenharmony_ci} 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic int vop_crtc_set_crc_source(struct drm_crtc *crtc, 15958c2ecf20Sopenharmony_ci const char *source_name) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 15988c2ecf20Sopenharmony_ci struct drm_connector *connector; 15998c2ecf20Sopenharmony_ci int ret; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci connector = vop_get_edp_connector(vop); 16028c2ecf20Sopenharmony_ci if (!connector) 16038c2ecf20Sopenharmony_ci return -EINVAL; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci if (source_name && strcmp(source_name, "auto") == 0) 16068c2ecf20Sopenharmony_ci ret = analogix_dp_start_crc(connector); 16078c2ecf20Sopenharmony_ci else if (!source_name) 16088c2ecf20Sopenharmony_ci ret = analogix_dp_stop_crc(connector); 16098c2ecf20Sopenharmony_ci else 16108c2ecf20Sopenharmony_ci ret = -EINVAL; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci return ret; 16138c2ecf20Sopenharmony_ci} 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_cistatic int 16168c2ecf20Sopenharmony_civop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, 16178c2ecf20Sopenharmony_ci size_t *values_cnt) 16188c2ecf20Sopenharmony_ci{ 16198c2ecf20Sopenharmony_ci if (source_name && strcmp(source_name, "auto") != 0) 16208c2ecf20Sopenharmony_ci return -EINVAL; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci *values_cnt = 3; 16238c2ecf20Sopenharmony_ci return 0; 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci#else 16278c2ecf20Sopenharmony_cistatic int vop_crtc_set_crc_source(struct drm_crtc *crtc, 16288c2ecf20Sopenharmony_ci const char *source_name) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci return -ENODEV; 16318c2ecf20Sopenharmony_ci} 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_cistatic int 16348c2ecf20Sopenharmony_civop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, 16358c2ecf20Sopenharmony_ci size_t *values_cnt) 16368c2ecf20Sopenharmony_ci{ 16378c2ecf20Sopenharmony_ci return -ENODEV; 16388c2ecf20Sopenharmony_ci} 16398c2ecf20Sopenharmony_ci#endif 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs vop_crtc_funcs = { 16428c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 16438c2ecf20Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 16448c2ecf20Sopenharmony_ci .destroy = vop_crtc_destroy, 16458c2ecf20Sopenharmony_ci .reset = vop_crtc_reset, 16468c2ecf20Sopenharmony_ci .atomic_duplicate_state = vop_crtc_duplicate_state, 16478c2ecf20Sopenharmony_ci .atomic_destroy_state = vop_crtc_destroy_state, 16488c2ecf20Sopenharmony_ci .enable_vblank = vop_crtc_enable_vblank, 16498c2ecf20Sopenharmony_ci .disable_vblank = vop_crtc_disable_vblank, 16508c2ecf20Sopenharmony_ci .set_crc_source = vop_crtc_set_crc_source, 16518c2ecf20Sopenharmony_ci .verify_crc_source = vop_crtc_verify_crc_source, 16528c2ecf20Sopenharmony_ci .gamma_set = drm_atomic_helper_legacy_gamma_set, 16538c2ecf20Sopenharmony_ci}; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_cistatic void vop_fb_unref_worker(struct drm_flip_work *work, void *val) 16568c2ecf20Sopenharmony_ci{ 16578c2ecf20Sopenharmony_ci struct vop *vop = container_of(work, struct vop, fb_unref_work); 16588c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = val; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci drm_crtc_vblank_put(&vop->crtc); 16618c2ecf20Sopenharmony_ci drm_framebuffer_put(fb); 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_cistatic void vop_handle_vblank(struct vop *vop) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct drm_device *drm = vop->drm_dev; 16678c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci spin_lock(&drm->event_lock); 16708c2ecf20Sopenharmony_ci if (vop->event) { 16718c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, vop->event); 16728c2ecf20Sopenharmony_ci drm_crtc_vblank_put(crtc); 16738c2ecf20Sopenharmony_ci vop->event = NULL; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci spin_unlock(&drm->event_lock); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) 16788c2ecf20Sopenharmony_ci drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_cistatic irqreturn_t vop_isr(int irq, void *data) 16828c2ecf20Sopenharmony_ci{ 16838c2ecf20Sopenharmony_ci struct vop *vop = data; 16848c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 16858c2ecf20Sopenharmony_ci uint32_t active_irqs; 16868c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* 16898c2ecf20Sopenharmony_ci * The irq is shared with the iommu. If the runtime-pm state of the 16908c2ecf20Sopenharmony_ci * vop-device is disabled the irq has to be targeted at the iommu. 16918c2ecf20Sopenharmony_ci */ 16928c2ecf20Sopenharmony_ci if (!pm_runtime_get_if_in_use(vop->dev)) 16938c2ecf20Sopenharmony_ci return IRQ_NONE; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci if (vop_core_clks_enable(vop)) { 16968c2ecf20Sopenharmony_ci DRM_DEV_ERROR_RATELIMITED(vop->dev, "couldn't enable clocks\n"); 16978c2ecf20Sopenharmony_ci goto out; 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci /* 17018c2ecf20Sopenharmony_ci * interrupt register has interrupt status, enable and clear bits, we 17028c2ecf20Sopenharmony_ci * must hold irq_lock to avoid a race with enable/disable_vblank(). 17038c2ecf20Sopenharmony_ci */ 17048c2ecf20Sopenharmony_ci spin_lock(&vop->irq_lock); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci active_irqs = VOP_INTR_GET_TYPE(vop, status, INTR_MASK); 17078c2ecf20Sopenharmony_ci /* Clear all active interrupt sources */ 17088c2ecf20Sopenharmony_ci if (active_irqs) 17098c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci spin_unlock(&vop->irq_lock); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci /* This is expected for vop iommu irqs, since the irq is shared */ 17148c2ecf20Sopenharmony_ci if (!active_irqs) 17158c2ecf20Sopenharmony_ci goto out_disable; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci if (active_irqs & DSP_HOLD_VALID_INTR) { 17188c2ecf20Sopenharmony_ci complete(&vop->dsp_hold_completion); 17198c2ecf20Sopenharmony_ci active_irqs &= ~DSP_HOLD_VALID_INTR; 17208c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (active_irqs & LINE_FLAG_INTR) { 17248c2ecf20Sopenharmony_ci complete(&vop->line_flag_completion); 17258c2ecf20Sopenharmony_ci active_irqs &= ~LINE_FLAG_INTR; 17268c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 17278c2ecf20Sopenharmony_ci } 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci if (active_irqs & FS_INTR) { 17308c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(crtc); 17318c2ecf20Sopenharmony_ci vop_handle_vblank(vop); 17328c2ecf20Sopenharmony_ci active_irqs &= ~FS_INTR; 17338c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 17348c2ecf20Sopenharmony_ci } 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci /* Unhandled irqs are spurious. */ 17378c2ecf20Sopenharmony_ci if (active_irqs) 17388c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n", 17398c2ecf20Sopenharmony_ci active_irqs); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ciout_disable: 17428c2ecf20Sopenharmony_ci vop_core_clks_disable(vop); 17438c2ecf20Sopenharmony_ciout: 17448c2ecf20Sopenharmony_ci pm_runtime_put(vop->dev); 17458c2ecf20Sopenharmony_ci return ret; 17468c2ecf20Sopenharmony_ci} 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_cistatic void vop_plane_add_properties(struct drm_plane *plane, 17498c2ecf20Sopenharmony_ci const struct vop_win_data *win_data) 17508c2ecf20Sopenharmony_ci{ 17518c2ecf20Sopenharmony_ci unsigned int flags = 0; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0; 17548c2ecf20Sopenharmony_ci flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0; 17558c2ecf20Sopenharmony_ci if (flags) 17568c2ecf20Sopenharmony_ci drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, 17578c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0 | flags); 17588c2ecf20Sopenharmony_ci} 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic int vop_create_crtc(struct vop *vop) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci const struct vop_data *vop_data = vop->data; 17638c2ecf20Sopenharmony_ci struct device *dev = vop->dev; 17648c2ecf20Sopenharmony_ci struct drm_device *drm_dev = vop->drm_dev; 17658c2ecf20Sopenharmony_ci struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp; 17668c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 17678c2ecf20Sopenharmony_ci struct device_node *port; 17688c2ecf20Sopenharmony_ci int ret; 17698c2ecf20Sopenharmony_ci int i; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci /* 17728c2ecf20Sopenharmony_ci * Create drm_plane for primary and cursor planes first, since we need 17738c2ecf20Sopenharmony_ci * to pass them to drm_crtc_init_with_planes, which sets the 17748c2ecf20Sopenharmony_ci * "possible_crtcs" to the newly initialized crtc. 17758c2ecf20Sopenharmony_ci */ 17768c2ecf20Sopenharmony_ci for (i = 0; i < vop_data->win_size; i++) { 17778c2ecf20Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 17788c2ecf20Sopenharmony_ci const struct vop_win_data *win_data = vop_win->data; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci if (win_data->type != DRM_PLANE_TYPE_PRIMARY && 17818c2ecf20Sopenharmony_ci win_data->type != DRM_PLANE_TYPE_CURSOR) 17828c2ecf20Sopenharmony_ci continue; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base, 17858c2ecf20Sopenharmony_ci 0, &vop_plane_funcs, 17868c2ecf20Sopenharmony_ci win_data->phy->data_formats, 17878c2ecf20Sopenharmony_ci win_data->phy->nformats, 17888c2ecf20Sopenharmony_ci win_data->phy->format_modifiers, 17898c2ecf20Sopenharmony_ci win_data->type, NULL); 17908c2ecf20Sopenharmony_ci if (ret) { 17918c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n", 17928c2ecf20Sopenharmony_ci ret); 17938c2ecf20Sopenharmony_ci goto err_cleanup_planes; 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci plane = &vop_win->base; 17978c2ecf20Sopenharmony_ci drm_plane_helper_add(plane, &plane_helper_funcs); 17988c2ecf20Sopenharmony_ci vop_plane_add_properties(plane, win_data); 17998c2ecf20Sopenharmony_ci if (plane->type == DRM_PLANE_TYPE_PRIMARY) 18008c2ecf20Sopenharmony_ci primary = plane; 18018c2ecf20Sopenharmony_ci else if (plane->type == DRM_PLANE_TYPE_CURSOR) 18028c2ecf20Sopenharmony_ci cursor = plane; 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, 18068c2ecf20Sopenharmony_ci &vop_crtc_funcs, NULL); 18078c2ecf20Sopenharmony_ci if (ret) 18088c2ecf20Sopenharmony_ci goto err_cleanup_planes; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs); 18118c2ecf20Sopenharmony_ci if (vop->lut_regs) { 18128c2ecf20Sopenharmony_ci drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size); 18138c2ecf20Sopenharmony_ci drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size); 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci /* 18178c2ecf20Sopenharmony_ci * Create drm_planes for overlay windows with possible_crtcs restricted 18188c2ecf20Sopenharmony_ci * to the newly created crtc. 18198c2ecf20Sopenharmony_ci */ 18208c2ecf20Sopenharmony_ci for (i = 0; i < vop_data->win_size; i++) { 18218c2ecf20Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 18228c2ecf20Sopenharmony_ci const struct vop_win_data *win_data = vop_win->data; 18238c2ecf20Sopenharmony_ci unsigned long possible_crtcs = drm_crtc_mask(crtc); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci if (win_data->type != DRM_PLANE_TYPE_OVERLAY) 18268c2ecf20Sopenharmony_ci continue; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base, 18298c2ecf20Sopenharmony_ci possible_crtcs, 18308c2ecf20Sopenharmony_ci &vop_plane_funcs, 18318c2ecf20Sopenharmony_ci win_data->phy->data_formats, 18328c2ecf20Sopenharmony_ci win_data->phy->nformats, 18338c2ecf20Sopenharmony_ci win_data->phy->format_modifiers, 18348c2ecf20Sopenharmony_ci win_data->type, NULL); 18358c2ecf20Sopenharmony_ci if (ret) { 18368c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n", 18378c2ecf20Sopenharmony_ci ret); 18388c2ecf20Sopenharmony_ci goto err_cleanup_crtc; 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci drm_plane_helper_add(&vop_win->base, &plane_helper_funcs); 18418c2ecf20Sopenharmony_ci vop_plane_add_properties(&vop_win->base, win_data); 18428c2ecf20Sopenharmony_ci } 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci port = of_get_child_by_name(dev->of_node, "port"); 18458c2ecf20Sopenharmony_ci if (!port) { 18468c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "no port node found in %pOF\n", 18478c2ecf20Sopenharmony_ci dev->of_node); 18488c2ecf20Sopenharmony_ci ret = -ENOENT; 18498c2ecf20Sopenharmony_ci goto err_cleanup_crtc; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci drm_flip_work_init(&vop->fb_unref_work, "fb_unref", 18538c2ecf20Sopenharmony_ci vop_fb_unref_worker); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci init_completion(&vop->dsp_hold_completion); 18568c2ecf20Sopenharmony_ci init_completion(&vop->line_flag_completion); 18578c2ecf20Sopenharmony_ci crtc->port = port; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci ret = drm_self_refresh_helper_init(crtc); 18608c2ecf20Sopenharmony_ci if (ret) 18618c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(vop->dev, 18628c2ecf20Sopenharmony_ci "Failed to init %s with SR helpers %d, ignoring\n", 18638c2ecf20Sopenharmony_ci crtc->name, ret); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci return 0; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_cierr_cleanup_crtc: 18688c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 18698c2ecf20Sopenharmony_cierr_cleanup_planes: 18708c2ecf20Sopenharmony_ci list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list, 18718c2ecf20Sopenharmony_ci head) 18728c2ecf20Sopenharmony_ci drm_plane_cleanup(plane); 18738c2ecf20Sopenharmony_ci return ret; 18748c2ecf20Sopenharmony_ci} 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_cistatic void vop_destroy_crtc(struct vop *vop) 18778c2ecf20Sopenharmony_ci{ 18788c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &vop->crtc; 18798c2ecf20Sopenharmony_ci struct drm_device *drm_dev = vop->drm_dev; 18808c2ecf20Sopenharmony_ci struct drm_plane *plane, *tmp; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci drm_self_refresh_helper_cleanup(crtc); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci of_node_put(crtc->port); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci /* 18878c2ecf20Sopenharmony_ci * We need to cleanup the planes now. Why? 18888c2ecf20Sopenharmony_ci * 18898c2ecf20Sopenharmony_ci * The planes are "&vop->win[i].base". That means the memory is 18908c2ecf20Sopenharmony_ci * all part of the big "struct vop" chunk of memory. That memory 18918c2ecf20Sopenharmony_ci * was devm allocated and associated with this component. We need to 18928c2ecf20Sopenharmony_ci * free it ourselves before vop_unbind() finishes. 18938c2ecf20Sopenharmony_ci */ 18948c2ecf20Sopenharmony_ci list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list, 18958c2ecf20Sopenharmony_ci head) 18968c2ecf20Sopenharmony_ci vop_plane_destroy(plane); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci /* 18998c2ecf20Sopenharmony_ci * Destroy CRTC after vop_plane_destroy() since vop_disable_plane() 19008c2ecf20Sopenharmony_ci * references the CRTC. 19018c2ecf20Sopenharmony_ci */ 19028c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 19038c2ecf20Sopenharmony_ci drm_flip_work_cleanup(&vop->fb_unref_work); 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic int vop_initial(struct vop *vop) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci struct reset_control *ahb_rst; 19098c2ecf20Sopenharmony_ci int i, ret; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci vop->hclk = devm_clk_get(vop->dev, "hclk_vop"); 19128c2ecf20Sopenharmony_ci if (IS_ERR(vop->hclk)) { 19138c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n"); 19148c2ecf20Sopenharmony_ci return PTR_ERR(vop->hclk); 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci vop->aclk = devm_clk_get(vop->dev, "aclk_vop"); 19178c2ecf20Sopenharmony_ci if (IS_ERR(vop->aclk)) { 19188c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n"); 19198c2ecf20Sopenharmony_ci return PTR_ERR(vop->aclk); 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci vop->dclk = devm_clk_get(vop->dev, "dclk_vop"); 19228c2ecf20Sopenharmony_ci if (IS_ERR(vop->dclk)) { 19238c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n"); 19248c2ecf20Sopenharmony_ci return PTR_ERR(vop->dclk); 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(vop->dev); 19288c2ecf20Sopenharmony_ci if (ret < 0) { 19298c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); 19308c2ecf20Sopenharmony_ci return ret; 19318c2ecf20Sopenharmony_ci } 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci ret = clk_prepare(vop->dclk); 19348c2ecf20Sopenharmony_ci if (ret < 0) { 19358c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n"); 19368c2ecf20Sopenharmony_ci goto err_put_pm_runtime; 19378c2ecf20Sopenharmony_ci } 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci /* Enable both the hclk and aclk to setup the vop */ 19408c2ecf20Sopenharmony_ci ret = clk_prepare_enable(vop->hclk); 19418c2ecf20Sopenharmony_ci if (ret < 0) { 19428c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n"); 19438c2ecf20Sopenharmony_ci goto err_unprepare_dclk; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci ret = clk_prepare_enable(vop->aclk); 19478c2ecf20Sopenharmony_ci if (ret < 0) { 19488c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n"); 19498c2ecf20Sopenharmony_ci goto err_disable_hclk; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci /* 19538c2ecf20Sopenharmony_ci * do hclk_reset, reset all vop registers. 19548c2ecf20Sopenharmony_ci */ 19558c2ecf20Sopenharmony_ci ahb_rst = devm_reset_control_get(vop->dev, "ahb"); 19568c2ecf20Sopenharmony_ci if (IS_ERR(ahb_rst)) { 19578c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n"); 19588c2ecf20Sopenharmony_ci ret = PTR_ERR(ahb_rst); 19598c2ecf20Sopenharmony_ci goto err_disable_aclk; 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci reset_control_assert(ahb_rst); 19628c2ecf20Sopenharmony_ci usleep_range(10, 20); 19638c2ecf20Sopenharmony_ci reset_control_deassert(ahb_rst); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1); 19668c2ecf20Sopenharmony_ci VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0); 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci for (i = 0; i < vop->len; i += sizeof(u32)) 19698c2ecf20Sopenharmony_ci vop->regsbak[i / 4] = readl_relaxed(vop->regs + i); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci VOP_REG_SET(vop, misc, global_regdone_en, 1); 19728c2ecf20Sopenharmony_ci VOP_REG_SET(vop, common, dsp_blank, 0); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci for (i = 0; i < vop->data->win_size; i++) { 19758c2ecf20Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 19768c2ecf20Sopenharmony_ci const struct vop_win_data *win = vop_win->data; 19778c2ecf20Sopenharmony_ci int channel = i * 2 + 1; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel); 19808c2ecf20Sopenharmony_ci vop_win_disable(vop, vop_win); 19818c2ecf20Sopenharmony_ci VOP_WIN_SET(vop, win, gate, 1); 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci vop_cfg_done(vop); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci /* 19878c2ecf20Sopenharmony_ci * do dclk_reset, let all config take affect. 19888c2ecf20Sopenharmony_ci */ 19898c2ecf20Sopenharmony_ci vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk"); 19908c2ecf20Sopenharmony_ci if (IS_ERR(vop->dclk_rst)) { 19918c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n"); 19928c2ecf20Sopenharmony_ci ret = PTR_ERR(vop->dclk_rst); 19938c2ecf20Sopenharmony_ci goto err_disable_aclk; 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci reset_control_assert(vop->dclk_rst); 19968c2ecf20Sopenharmony_ci usleep_range(10, 20); 19978c2ecf20Sopenharmony_ci reset_control_deassert(vop->dclk_rst); 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci clk_disable(vop->hclk); 20008c2ecf20Sopenharmony_ci clk_disable(vop->aclk); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci vop->is_enabled = false; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci pm_runtime_put_sync(vop->dev); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci return 0; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_cierr_disable_aclk: 20098c2ecf20Sopenharmony_ci clk_disable_unprepare(vop->aclk); 20108c2ecf20Sopenharmony_cierr_disable_hclk: 20118c2ecf20Sopenharmony_ci clk_disable_unprepare(vop->hclk); 20128c2ecf20Sopenharmony_cierr_unprepare_dclk: 20138c2ecf20Sopenharmony_ci clk_unprepare(vop->dclk); 20148c2ecf20Sopenharmony_cierr_put_pm_runtime: 20158c2ecf20Sopenharmony_ci pm_runtime_put_sync(vop->dev); 20168c2ecf20Sopenharmony_ci return ret; 20178c2ecf20Sopenharmony_ci} 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci/* 20208c2ecf20Sopenharmony_ci * Initialize the vop->win array elements. 20218c2ecf20Sopenharmony_ci */ 20228c2ecf20Sopenharmony_cistatic void vop_win_init(struct vop *vop) 20238c2ecf20Sopenharmony_ci{ 20248c2ecf20Sopenharmony_ci const struct vop_data *vop_data = vop->data; 20258c2ecf20Sopenharmony_ci unsigned int i; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci for (i = 0; i < vop_data->win_size; i++) { 20288c2ecf20Sopenharmony_ci struct vop_win *vop_win = &vop->win[i]; 20298c2ecf20Sopenharmony_ci const struct vop_win_data *win_data = &vop_data->win[i]; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci vop_win->data = win_data; 20328c2ecf20Sopenharmony_ci vop_win->vop = vop; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci if (vop_data->win_yuv2yuv) 20358c2ecf20Sopenharmony_ci vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i]; 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci} 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci/** 20408c2ecf20Sopenharmony_ci * rockchip_drm_wait_vact_end 20418c2ecf20Sopenharmony_ci * @crtc: CRTC to enable line flag 20428c2ecf20Sopenharmony_ci * @mstimeout: millisecond for timeout 20438c2ecf20Sopenharmony_ci * 20448c2ecf20Sopenharmony_ci * Wait for vact_end line flag irq or timeout. 20458c2ecf20Sopenharmony_ci * 20468c2ecf20Sopenharmony_ci * Returns: 20478c2ecf20Sopenharmony_ci * Zero on success, negative errno on failure. 20488c2ecf20Sopenharmony_ci */ 20498c2ecf20Sopenharmony_ciint rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout) 20508c2ecf20Sopenharmony_ci{ 20518c2ecf20Sopenharmony_ci struct vop *vop = to_vop(crtc); 20528c2ecf20Sopenharmony_ci unsigned long jiffies_left; 20538c2ecf20Sopenharmony_ci int ret = 0; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci if (!crtc || !vop->is_enabled) 20568c2ecf20Sopenharmony_ci return -ENODEV; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci mutex_lock(&vop->vop_lock); 20598c2ecf20Sopenharmony_ci if (mstimeout <= 0) { 20608c2ecf20Sopenharmony_ci ret = -EINVAL; 20618c2ecf20Sopenharmony_ci goto out; 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci if (vop_line_flag_irq_is_enabled(vop)) { 20658c2ecf20Sopenharmony_ci ret = -EBUSY; 20668c2ecf20Sopenharmony_ci goto out; 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci reinit_completion(&vop->line_flag_completion); 20708c2ecf20Sopenharmony_ci vop_line_flag_irq_enable(vop); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion, 20738c2ecf20Sopenharmony_ci msecs_to_jiffies(mstimeout)); 20748c2ecf20Sopenharmony_ci vop_line_flag_irq_disable(vop); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci if (jiffies_left == 0) { 20778c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n"); 20788c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 20798c2ecf20Sopenharmony_ci goto out; 20808c2ecf20Sopenharmony_ci } 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ciout: 20838c2ecf20Sopenharmony_ci mutex_unlock(&vop->vop_lock); 20848c2ecf20Sopenharmony_ci return ret; 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rockchip_drm_wait_vact_end); 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_cistatic int vop_bind(struct device *dev, struct device *master, void *data) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 20918c2ecf20Sopenharmony_ci const struct vop_data *vop_data; 20928c2ecf20Sopenharmony_ci struct drm_device *drm_dev = data; 20938c2ecf20Sopenharmony_ci struct vop *vop; 20948c2ecf20Sopenharmony_ci struct resource *res; 20958c2ecf20Sopenharmony_ci int ret, irq; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci vop_data = of_device_get_match_data(dev); 20988c2ecf20Sopenharmony_ci if (!vop_data) 20998c2ecf20Sopenharmony_ci return -ENODEV; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci /* Allocate vop struct and its vop_win array */ 21028c2ecf20Sopenharmony_ci vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size), 21038c2ecf20Sopenharmony_ci GFP_KERNEL); 21048c2ecf20Sopenharmony_ci if (!vop) 21058c2ecf20Sopenharmony_ci return -ENOMEM; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci vop->dev = dev; 21088c2ecf20Sopenharmony_ci vop->data = vop_data; 21098c2ecf20Sopenharmony_ci vop->drm_dev = drm_dev; 21108c2ecf20Sopenharmony_ci dev_set_drvdata(dev, vop); 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci vop_win_init(vop); 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 21158c2ecf20Sopenharmony_ci vop->regs = devm_ioremap_resource(dev, res); 21168c2ecf20Sopenharmony_ci if (IS_ERR(vop->regs)) 21178c2ecf20Sopenharmony_ci return PTR_ERR(vop->regs); 21188c2ecf20Sopenharmony_ci vop->len = resource_size(res); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 21218c2ecf20Sopenharmony_ci if (res) { 21228c2ecf20Sopenharmony_ci if (!vop_data->lut_size) { 21238c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "no gamma LUT size defined\n"); 21248c2ecf20Sopenharmony_ci return -EINVAL; 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci vop->lut_regs = devm_ioremap_resource(dev, res); 21278c2ecf20Sopenharmony_ci if (IS_ERR(vop->lut_regs)) 21288c2ecf20Sopenharmony_ci return PTR_ERR(vop->lut_regs); 21298c2ecf20Sopenharmony_ci } 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL); 21328c2ecf20Sopenharmony_ci if (!vop->regsbak) 21338c2ecf20Sopenharmony_ci return -ENOMEM; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 21368c2ecf20Sopenharmony_ci if (irq < 0) { 21378c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "cannot find irq for vop\n"); 21388c2ecf20Sopenharmony_ci return irq; 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci vop->irq = (unsigned int)irq; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci spin_lock_init(&vop->reg_lock); 21438c2ecf20Sopenharmony_ci spin_lock_init(&vop->irq_lock); 21448c2ecf20Sopenharmony_ci mutex_init(&vop->vop_lock); 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci ret = vop_create_crtc(vop); 21478c2ecf20Sopenharmony_ci if (ret) 21488c2ecf20Sopenharmony_ci return ret; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci ret = vop_initial(vop); 21538c2ecf20Sopenharmony_ci if (ret < 0) { 21548c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, 21558c2ecf20Sopenharmony_ci "cannot initial vop dev - err %d\n", ret); 21568c2ecf20Sopenharmony_ci goto err_disable_pm_runtime; 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, vop->irq, vop_isr, 21608c2ecf20Sopenharmony_ci IRQF_SHARED, dev_name(dev), vop); 21618c2ecf20Sopenharmony_ci if (ret) 21628c2ecf20Sopenharmony_ci goto err_disable_pm_runtime; 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) { 21658c2ecf20Sopenharmony_ci vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev); 21668c2ecf20Sopenharmony_ci if (IS_ERR(vop->rgb)) { 21678c2ecf20Sopenharmony_ci ret = PTR_ERR(vop->rgb); 21688c2ecf20Sopenharmony_ci goto err_disable_pm_runtime; 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci return 0; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_cierr_disable_pm_runtime: 21758c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 21768c2ecf20Sopenharmony_ci vop_destroy_crtc(vop); 21778c2ecf20Sopenharmony_ci return ret; 21788c2ecf20Sopenharmony_ci} 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_cistatic void vop_unbind(struct device *dev, struct device *master, void *data) 21818c2ecf20Sopenharmony_ci{ 21828c2ecf20Sopenharmony_ci struct vop *vop = dev_get_drvdata(dev); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci if (vop->rgb) 21858c2ecf20Sopenharmony_ci rockchip_rgb_fini(vop->rgb); 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 21888c2ecf20Sopenharmony_ci vop_destroy_crtc(vop); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci clk_unprepare(vop->aclk); 21918c2ecf20Sopenharmony_ci clk_unprepare(vop->hclk); 21928c2ecf20Sopenharmony_ci clk_unprepare(vop->dclk); 21938c2ecf20Sopenharmony_ci} 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ciconst struct component_ops vop_component_ops = { 21968c2ecf20Sopenharmony_ci .bind = vop_bind, 21978c2ecf20Sopenharmony_ci .unbind = vop_unbind, 21988c2ecf20Sopenharmony_ci}; 21998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vop_component_ops); 2200