18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Broadcom 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/** 78c2ecf20Sopenharmony_ci * DOC: VC4 plane module 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Each DRM plane is a layer of pixels being scanned out by the HVS. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * At atomic modeset check time, we compute the HVS display element 128c2ecf20Sopenharmony_ci * state that would be necessary for displaying the plane (giving us a 138c2ecf20Sopenharmony_ci * chance to figure out if a plane configuration is invalid), then at 148c2ecf20Sopenharmony_ci * atomic flush time the CRTC will ask us to write our element state 158c2ecf20Sopenharmony_ci * into the region of the HVS that it has allocated for us. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_atomic_uapi.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "uapi/drm/vc4_drm.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "vc4_drv.h" 298c2ecf20Sopenharmony_ci#include "vc4_regs.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic const struct hvs_format { 328c2ecf20Sopenharmony_ci u32 drm; /* DRM_FORMAT_* */ 338c2ecf20Sopenharmony_ci u32 hvs; /* HVS_FORMAT_* */ 348c2ecf20Sopenharmony_ci u32 pixel_order; 358c2ecf20Sopenharmony_ci u32 pixel_order_hvs5; 368c2ecf20Sopenharmony_ci} hvs_formats[] = { 378c2ecf20Sopenharmony_ci { 388c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_XRGB8888, 398c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGBA8888, 408c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_ABGR, 418c2ecf20Sopenharmony_ci .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, 428c2ecf20Sopenharmony_ci }, 438c2ecf20Sopenharmony_ci { 448c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_ARGB8888, 458c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGBA8888, 468c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_ABGR, 478c2ecf20Sopenharmony_ci .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, 488c2ecf20Sopenharmony_ci }, 498c2ecf20Sopenharmony_ci { 508c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_ABGR8888, 518c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGBA8888, 528c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_ARGB, 538c2ecf20Sopenharmony_ci .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR, 548c2ecf20Sopenharmony_ci }, 558c2ecf20Sopenharmony_ci { 568c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_XBGR8888, 578c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGBA8888, 588c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_ARGB, 598c2ecf20Sopenharmony_ci .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR, 608c2ecf20Sopenharmony_ci }, 618c2ecf20Sopenharmony_ci { 628c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_RGB565, 638c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGB565, 648c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XRGB, 658c2ecf20Sopenharmony_ci }, 668c2ecf20Sopenharmony_ci { 678c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_BGR565, 688c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGB565, 698c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XBGR, 708c2ecf20Sopenharmony_ci }, 718c2ecf20Sopenharmony_ci { 728c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_ARGB1555, 738c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGBA5551, 748c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_ABGR, 758c2ecf20Sopenharmony_ci .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, 768c2ecf20Sopenharmony_ci }, 778c2ecf20Sopenharmony_ci { 788c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_XRGB1555, 798c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGBA5551, 808c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_ABGR, 818c2ecf20Sopenharmony_ci .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, 828c2ecf20Sopenharmony_ci }, 838c2ecf20Sopenharmony_ci { 848c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_RGB888, 858c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGB888, 868c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XRGB, 878c2ecf20Sopenharmony_ci }, 888c2ecf20Sopenharmony_ci { 898c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_BGR888, 908c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_RGB888, 918c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XBGR, 928c2ecf20Sopenharmony_ci }, 938c2ecf20Sopenharmony_ci { 948c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_YUV422, 958c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, 968c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCBCR, 978c2ecf20Sopenharmony_ci }, 988c2ecf20Sopenharmony_ci { 998c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_YVU422, 1008c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, 1018c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCRCB, 1028c2ecf20Sopenharmony_ci }, 1038c2ecf20Sopenharmony_ci { 1048c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_YUV420, 1058c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, 1068c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCBCR, 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci { 1098c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_YVU420, 1108c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, 1118c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCRCB, 1128c2ecf20Sopenharmony_ci }, 1138c2ecf20Sopenharmony_ci { 1148c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_NV12, 1158c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, 1168c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCBCR, 1178c2ecf20Sopenharmony_ci }, 1188c2ecf20Sopenharmony_ci { 1198c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_NV21, 1208c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, 1218c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCRCB, 1228c2ecf20Sopenharmony_ci }, 1238c2ecf20Sopenharmony_ci { 1248c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_NV16, 1258c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, 1268c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCBCR, 1278c2ecf20Sopenharmony_ci }, 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci .drm = DRM_FORMAT_NV61, 1308c2ecf20Sopenharmony_ci .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, 1318c2ecf20Sopenharmony_ci .pixel_order = HVS_PIXEL_ORDER_XYCRCB, 1328c2ecf20Sopenharmony_ci }, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic const struct hvs_format *vc4_get_hvs_format(u32 drm_format) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci unsigned i; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { 1408c2ecf20Sopenharmony_ci if (hvs_formats[i].drm == drm_format) 1418c2ecf20Sopenharmony_ci return &hvs_formats[i]; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return NULL; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci if (dst == src) 1508c2ecf20Sopenharmony_ci return VC4_SCALING_NONE; 1518c2ecf20Sopenharmony_ci if (3 * dst >= 2 * src) 1528c2ecf20Sopenharmony_ci return VC4_SCALING_PPF; 1538c2ecf20Sopenharmony_ci else 1548c2ecf20Sopenharmony_ci return VC4_SCALING_TPZ; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic bool plane_enabled(struct drm_plane_state *state) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return state->fb && !WARN_ON(!state->crtc); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (WARN_ON(!plane->state)) 1678c2ecf20Sopenharmony_ci return NULL; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL); 1708c2ecf20Sopenharmony_ci if (!vc4_state) 1718c2ecf20Sopenharmony_ci return NULL; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm)); 1748c2ecf20Sopenharmony_ci vc4_state->dlist_initialized = 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (vc4_state->dlist) { 1798c2ecf20Sopenharmony_ci vc4_state->dlist = kmemdup(vc4_state->dlist, 1808c2ecf20Sopenharmony_ci vc4_state->dlist_count * 4, 1818c2ecf20Sopenharmony_ci GFP_KERNEL); 1828c2ecf20Sopenharmony_ci if (!vc4_state->dlist) { 1838c2ecf20Sopenharmony_ci kfree(vc4_state); 1848c2ecf20Sopenharmony_ci return NULL; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci vc4_state->dlist_size = vc4_state->dlist_count; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return &vc4_state->base; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void vc4_plane_destroy_state(struct drm_plane *plane, 1938c2ecf20Sopenharmony_ci struct drm_plane_state *state) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(plane->dev); 1968c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (drm_mm_node_allocated(&vc4_state->lbm)) { 1998c2ecf20Sopenharmony_ci unsigned long irqflags; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); 2028c2ecf20Sopenharmony_ci drm_mm_remove_node(&vc4_state->lbm); 2038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci kfree(vc4_state->dlist); 2078c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_destroy_state(&vc4_state->base); 2088c2ecf20Sopenharmony_ci kfree(state); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* Called during init to allocate the plane's atomic state. */ 2128c2ecf20Sopenharmony_cistatic void vc4_plane_reset(struct drm_plane *plane) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci WARN_ON(plane->state); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!vc4_state) 2208c2ecf20Sopenharmony_ci return; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_reset(plane, &vc4_state->base); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void vc4_dlist_counter_increment(struct vc4_plane_state *vc4_state) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci if (vc4_state->dlist_count == vc4_state->dlist_size) { 2288c2ecf20Sopenharmony_ci u32 new_size = max(4u, vc4_state->dlist_count * 2); 2298c2ecf20Sopenharmony_ci u32 *new_dlist = kmalloc_array(new_size, 4, GFP_KERNEL); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!new_dlist) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci kfree(vc4_state->dlist); 2368c2ecf20Sopenharmony_ci vc4_state->dlist = new_dlist; 2378c2ecf20Sopenharmony_ci vc4_state->dlist_size = new_size; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci vc4_state->dlist_count++; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci unsigned int idx = vc4_state->dlist_count; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci vc4_dlist_counter_increment(vc4_state); 2488c2ecf20Sopenharmony_ci vc4_state->dlist[idx] = val; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* Returns the scl0/scl1 field based on whether the dimensions need to 2528c2ecf20Sopenharmony_ci * be up/down/non-scaled. 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * This is a replication of a table from the spec. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic u32 vc4_get_scl_field(struct drm_plane_state *state, int plane) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) { 2618c2ecf20Sopenharmony_ci case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: 2628c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_PPF_V_PPF; 2638c2ecf20Sopenharmony_ci case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: 2648c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_TPZ_V_PPF; 2658c2ecf20Sopenharmony_ci case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ: 2668c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_PPF_V_TPZ; 2678c2ecf20Sopenharmony_ci case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ: 2688c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_TPZ_V_TPZ; 2698c2ecf20Sopenharmony_ci case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE: 2708c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_PPF_V_NONE; 2718c2ecf20Sopenharmony_ci case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF: 2728c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_NONE_V_PPF; 2738c2ecf20Sopenharmony_ci case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ: 2748c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_NONE_V_TPZ; 2758c2ecf20Sopenharmony_ci case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE: 2768c2ecf20Sopenharmony_ci return SCALER_CTL0_SCL_H_TPZ_V_NONE; 2778c2ecf20Sopenharmony_ci default: 2788c2ecf20Sopenharmony_ci case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE: 2798c2ecf20Sopenharmony_ci /* The unity case is independently handled by 2808c2ecf20Sopenharmony_ci * SCALER_CTL0_UNITY. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int vc4_plane_margins_adj(struct drm_plane_state *pstate) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_pstate = to_vc4_plane_state(pstate); 2898c2ecf20Sopenharmony_ci unsigned int left, right, top, bottom, adjhdisplay, adjvdisplay; 2908c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(pstate->state, 2938c2ecf20Sopenharmony_ci pstate->crtc); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci vc4_crtc_get_margins(crtc_state, &left, &right, &top, &bottom); 2968c2ecf20Sopenharmony_ci if (!left && !right && !top && !bottom) 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (left + right >= crtc_state->mode.hdisplay || 3008c2ecf20Sopenharmony_ci top + bottom >= crtc_state->mode.vdisplay) 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci adjhdisplay = crtc_state->mode.hdisplay - (left + right); 3048c2ecf20Sopenharmony_ci vc4_pstate->crtc_x = DIV_ROUND_CLOSEST(vc4_pstate->crtc_x * 3058c2ecf20Sopenharmony_ci adjhdisplay, 3068c2ecf20Sopenharmony_ci crtc_state->mode.hdisplay); 3078c2ecf20Sopenharmony_ci vc4_pstate->crtc_x += left; 3088c2ecf20Sopenharmony_ci if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - right) 3098c2ecf20Sopenharmony_ci vc4_pstate->crtc_x = crtc_state->mode.hdisplay - right; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci adjvdisplay = crtc_state->mode.vdisplay - (top + bottom); 3128c2ecf20Sopenharmony_ci vc4_pstate->crtc_y = DIV_ROUND_CLOSEST(vc4_pstate->crtc_y * 3138c2ecf20Sopenharmony_ci adjvdisplay, 3148c2ecf20Sopenharmony_ci crtc_state->mode.vdisplay); 3158c2ecf20Sopenharmony_ci vc4_pstate->crtc_y += top; 3168c2ecf20Sopenharmony_ci if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - bottom) 3178c2ecf20Sopenharmony_ci vc4_pstate->crtc_y = crtc_state->mode.vdisplay - bottom; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci vc4_pstate->crtc_w = DIV_ROUND_CLOSEST(vc4_pstate->crtc_w * 3208c2ecf20Sopenharmony_ci adjhdisplay, 3218c2ecf20Sopenharmony_ci crtc_state->mode.hdisplay); 3228c2ecf20Sopenharmony_ci vc4_pstate->crtc_h = DIV_ROUND_CLOSEST(vc4_pstate->crtc_h * 3238c2ecf20Sopenharmony_ci adjvdisplay, 3248c2ecf20Sopenharmony_ci crtc_state->mode.vdisplay); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!vc4_pstate->crtc_w || !vc4_pstate->crtc_h) 3278c2ecf20Sopenharmony_ci return -EINVAL; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 3358c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 3368c2ecf20Sopenharmony_ci struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); 3378c2ecf20Sopenharmony_ci int num_planes = fb->format->num_planes; 3388c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 3398c2ecf20Sopenharmony_ci u32 h_subsample = fb->format->hsub; 3408c2ecf20Sopenharmony_ci u32 v_subsample = fb->format->vsub; 3418c2ecf20Sopenharmony_ci int i, ret; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state->state, 3448c2ecf20Sopenharmony_ci state->crtc); 3458c2ecf20Sopenharmony_ci if (!crtc_state) { 3468c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid crtc state\n"); 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(state, crtc_state, 1, 3518c2ecf20Sopenharmony_ci INT_MAX, true, true); 3528c2ecf20Sopenharmony_ci if (ret) 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci for (i = 0; i < num_planes; i++) 3568c2ecf20Sopenharmony_ci vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * We don't support subpixel source positioning for scaling, 3608c2ecf20Sopenharmony_ci * but fractional coordinates can be generated by clipping 3618c2ecf20Sopenharmony_ci * so just round for now 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci vc4_state->src_x = DIV_ROUND_CLOSEST(state->src.x1, 1 << 16); 3648c2ecf20Sopenharmony_ci vc4_state->src_y = DIV_ROUND_CLOSEST(state->src.y1, 1 << 16); 3658c2ecf20Sopenharmony_ci vc4_state->src_w[0] = DIV_ROUND_CLOSEST(state->src.x2, 1 << 16) - vc4_state->src_x; 3668c2ecf20Sopenharmony_ci vc4_state->src_h[0] = DIV_ROUND_CLOSEST(state->src.y2, 1 << 16) - vc4_state->src_y; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci vc4_state->crtc_x = state->dst.x1; 3698c2ecf20Sopenharmony_ci vc4_state->crtc_y = state->dst.y1; 3708c2ecf20Sopenharmony_ci vc4_state->crtc_w = state->dst.x2 - state->dst.x1; 3718c2ecf20Sopenharmony_ci vc4_state->crtc_h = state->dst.y2 - state->dst.y1; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ret = vc4_plane_margins_adj(state); 3748c2ecf20Sopenharmony_ci if (ret) 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], 3788c2ecf20Sopenharmony_ci vc4_state->crtc_w); 3798c2ecf20Sopenharmony_ci vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], 3808c2ecf20Sopenharmony_ci vc4_state->crtc_h); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE && 3838c2ecf20Sopenharmony_ci vc4_state->y_scaling[0] == VC4_SCALING_NONE); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (num_planes > 1) { 3868c2ecf20Sopenharmony_ci vc4_state->is_yuv = true; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample; 3898c2ecf20Sopenharmony_ci vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci vc4_state->x_scaling[1] = 3928c2ecf20Sopenharmony_ci vc4_get_scaling_mode(vc4_state->src_w[1], 3938c2ecf20Sopenharmony_ci vc4_state->crtc_w); 3948c2ecf20Sopenharmony_ci vc4_state->y_scaling[1] = 3958c2ecf20Sopenharmony_ci vc4_get_scaling_mode(vc4_state->src_h[1], 3968c2ecf20Sopenharmony_ci vc4_state->crtc_h); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* YUV conversion requires that horizontal scaling be enabled 3998c2ecf20Sopenharmony_ci * on the UV plane even if vc4_get_scaling_mode() returned 4008c2ecf20Sopenharmony_ci * VC4_SCALING_NONE (which can happen when the down-scaling 4018c2ecf20Sopenharmony_ci * ratio is 0.5). Let's force it to VC4_SCALING_PPF in this 4028c2ecf20Sopenharmony_ci * case. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci if (vc4_state->x_scaling[1] == VC4_SCALING_NONE) 4058c2ecf20Sopenharmony_ci vc4_state->x_scaling[1] = VC4_SCALING_PPF; 4068c2ecf20Sopenharmony_ci } else { 4078c2ecf20Sopenharmony_ci vc4_state->is_yuv = false; 4088c2ecf20Sopenharmony_ci vc4_state->x_scaling[1] = VC4_SCALING_NONE; 4098c2ecf20Sopenharmony_ci vc4_state->y_scaling[1] = VC4_SCALING_NONE; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci u32 scale, recip; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci scale = (1 << 16) * src / dst; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* The specs note that while the reciprocal would be defined 4228c2ecf20Sopenharmony_ci * as (1<<32)/scale, ~0 is close enough. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci recip = ~0 / scale; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 4278c2ecf20Sopenharmony_ci VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) | 4288c2ecf20Sopenharmony_ci VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE)); 4298c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 4308c2ecf20Sopenharmony_ci VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP)); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci u32 scale = (1 << 16) * src / dst; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 4388c2ecf20Sopenharmony_ci SCALER_PPF_AGC | 4398c2ecf20Sopenharmony_ci VC4_SET_FIELD(scale, SCALER_PPF_SCALE) | 4408c2ecf20Sopenharmony_ci VC4_SET_FIELD(0, SCALER_PPF_IPHASE)); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic u32 vc4_lbm_size(struct drm_plane_state *state) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 4468c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev); 4478c2ecf20Sopenharmony_ci u32 pix_per_line; 4488c2ecf20Sopenharmony_ci u32 lbm; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* LBM is not needed when there's no vertical scaling. */ 4518c2ecf20Sopenharmony_ci if (vc4_state->y_scaling[0] == VC4_SCALING_NONE && 4528c2ecf20Sopenharmony_ci vc4_state->y_scaling[1] == VC4_SCALING_NONE) 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* 4568c2ecf20Sopenharmony_ci * This can be further optimized in the RGB/YUV444 case if the PPF 4578c2ecf20Sopenharmony_ci * decimation factor is between 0.5 and 1.0 by using crtc_w. 4588c2ecf20Sopenharmony_ci * 4598c2ecf20Sopenharmony_ci * It's not an issue though, since in that case since src_w[0] is going 4608c2ecf20Sopenharmony_ci * to be greater than or equal to crtc_w. 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ) 4638c2ecf20Sopenharmony_ci pix_per_line = vc4_state->crtc_w; 4648c2ecf20Sopenharmony_ci else 4658c2ecf20Sopenharmony_ci pix_per_line = vc4_state->src_w[0]; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!vc4_state->is_yuv) { 4688c2ecf20Sopenharmony_ci if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) 4698c2ecf20Sopenharmony_ci lbm = pix_per_line * 8; 4708c2ecf20Sopenharmony_ci else { 4718c2ecf20Sopenharmony_ci /* In special cases, this multiplier might be 12. */ 4728c2ecf20Sopenharmony_ci lbm = pix_per_line * 16; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci } else { 4758c2ecf20Sopenharmony_ci /* There are cases for this going down to a multiplier 4768c2ecf20Sopenharmony_ci * of 2, but according to the firmware source, the 4778c2ecf20Sopenharmony_ci * table in the docs is somewhat wrong. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci lbm = pix_per_line * 16; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* Align it to 64 or 128 (hvs5) bytes */ 4838c2ecf20Sopenharmony_ci lbm = roundup(lbm, vc4->hvs->hvs5 ? 128 : 64); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Each "word" of the LBM memory contains 2 or 4 (hvs5) pixels */ 4868c2ecf20Sopenharmony_ci lbm /= vc4->hvs->hvs5 ? 4 : 2; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return lbm; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void vc4_write_scaling_parameters(struct drm_plane_state *state, 4928c2ecf20Sopenharmony_ci int channel) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Ch0 H-PPF Word 0: Scaling Parameters */ 4978c2ecf20Sopenharmony_ci if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) { 4988c2ecf20Sopenharmony_ci vc4_write_ppf(vc4_state, 4998c2ecf20Sopenharmony_ci vc4_state->src_w[channel], vc4_state->crtc_w); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ 5038c2ecf20Sopenharmony_ci if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) { 5048c2ecf20Sopenharmony_ci vc4_write_ppf(vc4_state, 5058c2ecf20Sopenharmony_ci vc4_state->src_h[channel], vc4_state->crtc_h); 5068c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 0xc0c0c0c0); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */ 5108c2ecf20Sopenharmony_ci if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) { 5118c2ecf20Sopenharmony_ci vc4_write_tpz(vc4_state, 5128c2ecf20Sopenharmony_ci vc4_state->src_w[channel], vc4_state->crtc_w); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */ 5168c2ecf20Sopenharmony_ci if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) { 5178c2ecf20Sopenharmony_ci vc4_write_tpz(vc4_state, 5188c2ecf20Sopenharmony_ci vc4_state->src_h[channel], vc4_state->crtc_h); 5198c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 0xc0c0c0c0); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic void vc4_plane_calc_load(struct drm_plane_state *state) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci unsigned int hvs_load_shift, vrefresh, i; 5268c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 5278c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state; 5288c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 5298c2ecf20Sopenharmony_ci unsigned int vscale_factor; 5308c2ecf20Sopenharmony_ci struct vc4_dev *vc4; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci vc4 = to_vc4_dev(state->plane->dev); 5338c2ecf20Sopenharmony_ci if (!vc4->load_tracker_available) 5348c2ecf20Sopenharmony_ci return; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci vc4_state = to_vc4_plane_state(state); 5378c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state->state, 5388c2ecf20Sopenharmony_ci state->crtc); 5398c2ecf20Sopenharmony_ci vrefresh = drm_mode_vrefresh(&crtc_state->adjusted_mode); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* The HVS is able to process 2 pixels/cycle when scaling the source, 5428c2ecf20Sopenharmony_ci * 4 pixels/cycle otherwise. 5438c2ecf20Sopenharmony_ci * Alpha blending step seems to be pipelined and it's always operating 5448c2ecf20Sopenharmony_ci * at 4 pixels/cycle, so the limiting aspect here seems to be the 5458c2ecf20Sopenharmony_ci * scaler block. 5468c2ecf20Sopenharmony_ci * HVS load is expressed in clk-cycles/sec (AKA Hz). 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci if (vc4_state->x_scaling[0] != VC4_SCALING_NONE || 5498c2ecf20Sopenharmony_ci vc4_state->x_scaling[1] != VC4_SCALING_NONE || 5508c2ecf20Sopenharmony_ci vc4_state->y_scaling[0] != VC4_SCALING_NONE || 5518c2ecf20Sopenharmony_ci vc4_state->y_scaling[1] != VC4_SCALING_NONE) 5528c2ecf20Sopenharmony_ci hvs_load_shift = 1; 5538c2ecf20Sopenharmony_ci else 5548c2ecf20Sopenharmony_ci hvs_load_shift = 2; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci vc4_state->membus_load = 0; 5578c2ecf20Sopenharmony_ci vc4_state->hvs_load = 0; 5588c2ecf20Sopenharmony_ci for (i = 0; i < fb->format->num_planes; i++) { 5598c2ecf20Sopenharmony_ci /* Even if the bandwidth/plane required for a single frame is 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * vc4_state->src_w[i] * vc4_state->src_h[i] * cpp * vrefresh 5628c2ecf20Sopenharmony_ci * 5638c2ecf20Sopenharmony_ci * when downscaling, we have to read more pixels per line in 5648c2ecf20Sopenharmony_ci * the time frame reserved for a single line, so the bandwidth 5658c2ecf20Sopenharmony_ci * demand can be punctually higher. To account for that, we 5668c2ecf20Sopenharmony_ci * calculate the down-scaling factor and multiply the plane 5678c2ecf20Sopenharmony_ci * load by this number. We're likely over-estimating the read 5688c2ecf20Sopenharmony_ci * demand, but that's better than under-estimating it. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_ci vscale_factor = DIV_ROUND_UP(vc4_state->src_h[i], 5718c2ecf20Sopenharmony_ci vc4_state->crtc_h); 5728c2ecf20Sopenharmony_ci vc4_state->membus_load += vc4_state->src_w[i] * 5738c2ecf20Sopenharmony_ci vc4_state->src_h[i] * vscale_factor * 5748c2ecf20Sopenharmony_ci fb->format->cpp[i]; 5758c2ecf20Sopenharmony_ci vc4_state->hvs_load += vc4_state->crtc_h * vc4_state->crtc_w; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci vc4_state->hvs_load *= vrefresh; 5798c2ecf20Sopenharmony_ci vc4_state->hvs_load >>= hvs_load_shift; 5808c2ecf20Sopenharmony_ci vc4_state->membus_load *= vrefresh; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int vc4_plane_allocate_lbm(struct drm_plane_state *state) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev); 5868c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 5878c2ecf20Sopenharmony_ci unsigned long irqflags; 5888c2ecf20Sopenharmony_ci u32 lbm_size; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci lbm_size = vc4_lbm_size(state); 5918c2ecf20Sopenharmony_ci if (!lbm_size) 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (WARN_ON(!vc4_state->lbm_offset)) 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Allocate the LBM memory that the HVS will use for temporary 5988c2ecf20Sopenharmony_ci * storage due to our scaling/format conversion. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci if (!drm_mm_node_allocated(&vc4_state->lbm)) { 6018c2ecf20Sopenharmony_ci int ret; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); 6048c2ecf20Sopenharmony_ci ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, 6058c2ecf20Sopenharmony_ci &vc4_state->lbm, 6068c2ecf20Sopenharmony_ci lbm_size, 6078c2ecf20Sopenharmony_ci vc4->hvs->hvs5 ? 64 : 32, 6088c2ecf20Sopenharmony_ci 0, 0); 6098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (ret) 6128c2ecf20Sopenharmony_ci return ret; 6138c2ecf20Sopenharmony_ci } else { 6148c2ecf20Sopenharmony_ci WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci vc4_state->dlist[vc4_state->lbm_offset] = vc4_state->lbm.start; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci/* Writes out a full display list for an active plane to the plane's 6238c2ecf20Sopenharmony_ci * private dlist state. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_cistatic int vc4_plane_mode_set(struct drm_plane *plane, 6268c2ecf20Sopenharmony_ci struct drm_plane_state *state) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(plane->dev); 6298c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 6308c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 6318c2ecf20Sopenharmony_ci u32 ctl0_offset = vc4_state->dlist_count; 6328c2ecf20Sopenharmony_ci const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); 6338c2ecf20Sopenharmony_ci u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); 6348c2ecf20Sopenharmony_ci int num_planes = fb->format->num_planes; 6358c2ecf20Sopenharmony_ci u32 h_subsample = fb->format->hsub; 6368c2ecf20Sopenharmony_ci u32 v_subsample = fb->format->vsub; 6378c2ecf20Sopenharmony_ci bool mix_plane_alpha; 6388c2ecf20Sopenharmony_ci bool covers_screen; 6398c2ecf20Sopenharmony_ci u32 scl0, scl1, pitch0; 6408c2ecf20Sopenharmony_ci u32 tiling, src_y; 6418c2ecf20Sopenharmony_ci u32 hvs_format = format->hvs; 6428c2ecf20Sopenharmony_ci unsigned int rotation; 6438c2ecf20Sopenharmony_ci int ret, i; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (vc4_state->dlist_initialized) 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci ret = vc4_plane_setup_clipping_and_scaling(state); 6498c2ecf20Sopenharmony_ci if (ret) 6508c2ecf20Sopenharmony_ci return ret; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB 6538c2ecf20Sopenharmony_ci * and 4:4:4, scl1 should be set to scl0 so both channels of 6548c2ecf20Sopenharmony_ci * the scaler do the same thing. For YUV, the Y plane needs 6558c2ecf20Sopenharmony_ci * to be put in channel 1 and Cb/Cr in channel 0, so we swap 6568c2ecf20Sopenharmony_ci * the scl fields here. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci if (num_planes == 1) { 6598c2ecf20Sopenharmony_ci scl0 = vc4_get_scl_field(state, 0); 6608c2ecf20Sopenharmony_ci scl1 = scl0; 6618c2ecf20Sopenharmony_ci } else { 6628c2ecf20Sopenharmony_ci scl0 = vc4_get_scl_field(state, 1); 6638c2ecf20Sopenharmony_ci scl1 = vc4_get_scl_field(state, 0); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci rotation = drm_rotation_simplify(state->rotation, 6678c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0 | 6688c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_X | 6698c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_Y); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* We must point to the last line when Y reflection is enabled. */ 6728c2ecf20Sopenharmony_ci src_y = vc4_state->src_y; 6738c2ecf20Sopenharmony_ci if (rotation & DRM_MODE_REFLECT_Y) 6748c2ecf20Sopenharmony_ci src_y += vc4_state->src_h[0] - 1; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci switch (base_format_mod) { 6778c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_LINEAR: 6788c2ecf20Sopenharmony_ci tiling = SCALER_CTL0_TILING_LINEAR; 6798c2ecf20Sopenharmony_ci pitch0 = VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* Adjust the base pointer to the first pixel to be scanned 6828c2ecf20Sopenharmony_ci * out. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_ci for (i = 0; i < num_planes; i++) { 6858c2ecf20Sopenharmony_ci vc4_state->offsets[i] += src_y / 6868c2ecf20Sopenharmony_ci (i ? v_subsample : 1) * 6878c2ecf20Sopenharmony_ci fb->pitches[i]; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci vc4_state->offsets[i] += vc4_state->src_x / 6908c2ecf20Sopenharmony_ci (i ? h_subsample : 1) * 6918c2ecf20Sopenharmony_ci fb->format->cpp[i]; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: { 6978c2ecf20Sopenharmony_ci u32 tile_size_shift = 12; /* T tiles are 4kb */ 6988c2ecf20Sopenharmony_ci /* Whole-tile offsets, mostly for setting the pitch. */ 6998c2ecf20Sopenharmony_ci u32 tile_w_shift = fb->format->cpp[0] == 2 ? 6 : 5; 7008c2ecf20Sopenharmony_ci u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */ 7018c2ecf20Sopenharmony_ci u32 tile_w_mask = (1 << tile_w_shift) - 1; 7028c2ecf20Sopenharmony_ci /* The height mask on 32-bit-per-pixel tiles is 63, i.e. twice 7038c2ecf20Sopenharmony_ci * the height (in pixels) of a 4k tile. 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ci u32 tile_h_mask = (2 << tile_h_shift) - 1; 7068c2ecf20Sopenharmony_ci /* For T-tiled, the FB pitch is "how many bytes from one row to 7078c2ecf20Sopenharmony_ci * the next, such that 7088c2ecf20Sopenharmony_ci * 7098c2ecf20Sopenharmony_ci * pitch * tile_h == tile_size * tiles_per_row 7108c2ecf20Sopenharmony_ci */ 7118c2ecf20Sopenharmony_ci u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); 7128c2ecf20Sopenharmony_ci u32 tiles_l = vc4_state->src_x >> tile_w_shift; 7138c2ecf20Sopenharmony_ci u32 tiles_r = tiles_w - tiles_l; 7148c2ecf20Sopenharmony_ci u32 tiles_t = src_y >> tile_h_shift; 7158c2ecf20Sopenharmony_ci /* Intra-tile offsets, which modify the base address (the 7168c2ecf20Sopenharmony_ci * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that 7178c2ecf20Sopenharmony_ci * base address). 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ci u32 tile_y = (src_y >> 4) & 1; 7208c2ecf20Sopenharmony_ci u32 subtile_y = (src_y >> 2) & 3; 7218c2ecf20Sopenharmony_ci u32 utile_y = src_y & 3; 7228c2ecf20Sopenharmony_ci u32 x_off = vc4_state->src_x & tile_w_mask; 7238c2ecf20Sopenharmony_ci u32 y_off = src_y & tile_h_mask; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* When Y reflection is requested we must set the 7268c2ecf20Sopenharmony_ci * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines 7278c2ecf20Sopenharmony_ci * after the initial one should be fetched in descending order, 7288c2ecf20Sopenharmony_ci * which makes sense since we start from the last line and go 7298c2ecf20Sopenharmony_ci * backward. 7308c2ecf20Sopenharmony_ci * Don't know why we need y_off = max_y_off - y_off, but it's 7318c2ecf20Sopenharmony_ci * definitely required (I guess it's also related to the "going 7328c2ecf20Sopenharmony_ci * backward" situation). 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_ci if (rotation & DRM_MODE_REFLECT_Y) { 7358c2ecf20Sopenharmony_ci y_off = tile_h_mask - y_off; 7368c2ecf20Sopenharmony_ci pitch0 = SCALER_PITCH0_TILE_LINE_DIR; 7378c2ecf20Sopenharmony_ci } else { 7388c2ecf20Sopenharmony_ci pitch0 = 0; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci tiling = SCALER_CTL0_TILING_256B_OR_T; 7428c2ecf20Sopenharmony_ci pitch0 |= (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) | 7438c2ecf20Sopenharmony_ci VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | 7448c2ecf20Sopenharmony_ci VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | 7458c2ecf20Sopenharmony_ci VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R)); 7468c2ecf20Sopenharmony_ci vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift); 7478c2ecf20Sopenharmony_ci vc4_state->offsets[0] += subtile_y << 8; 7488c2ecf20Sopenharmony_ci vc4_state->offsets[0] += utile_y << 4; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* Rows of tiles alternate left-to-right and right-to-left. */ 7518c2ecf20Sopenharmony_ci if (tiles_t & 1) { 7528c2ecf20Sopenharmony_ci pitch0 |= SCALER_PITCH0_TILE_INITIAL_LINE_DIR; 7538c2ecf20Sopenharmony_ci vc4_state->offsets[0] += (tiles_w - tiles_l) << 7548c2ecf20Sopenharmony_ci tile_size_shift; 7558c2ecf20Sopenharmony_ci vc4_state->offsets[0] -= (1 + !tile_y) << 10; 7568c2ecf20Sopenharmony_ci } else { 7578c2ecf20Sopenharmony_ci vc4_state->offsets[0] += tiles_l << tile_size_shift; 7588c2ecf20Sopenharmony_ci vc4_state->offsets[0] += tile_y << 10; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci break; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND64: 7658c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND128: 7668c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND256: { 7678c2ecf20Sopenharmony_ci uint32_t param = fourcc_mod_broadcom_param(fb->modifier); 7688c2ecf20Sopenharmony_ci u32 tile_w, tile, x_off, pix_per_tile; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci hvs_format = HVS_PIXEL_FORMAT_H264; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci switch (base_format_mod) { 7738c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND64: 7748c2ecf20Sopenharmony_ci tiling = SCALER_CTL0_TILING_64B; 7758c2ecf20Sopenharmony_ci tile_w = 64; 7768c2ecf20Sopenharmony_ci break; 7778c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND128: 7788c2ecf20Sopenharmony_ci tiling = SCALER_CTL0_TILING_128B; 7798c2ecf20Sopenharmony_ci tile_w = 128; 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND256: 7828c2ecf20Sopenharmony_ci tiling = SCALER_CTL0_TILING_256B_OR_T; 7838c2ecf20Sopenharmony_ci tile_w = 256; 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci default: 7868c2ecf20Sopenharmony_ci break; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (param > SCALER_TILE_HEIGHT_MASK) { 7908c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SAND height too large (%d)\n", param); 7918c2ecf20Sopenharmony_ci return -EINVAL; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci pix_per_tile = tile_w / fb->format->cpp[0]; 7958c2ecf20Sopenharmony_ci tile = vc4_state->src_x / pix_per_tile; 7968c2ecf20Sopenharmony_ci x_off = vc4_state->src_x % pix_per_tile; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* Adjust the base pointer to the first pixel to be scanned 7998c2ecf20Sopenharmony_ci * out. 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_ci for (i = 0; i < num_planes; i++) { 8028c2ecf20Sopenharmony_ci vc4_state->offsets[i] += param * tile_w * tile; 8038c2ecf20Sopenharmony_ci vc4_state->offsets[i] += src_y / 8048c2ecf20Sopenharmony_ci (i ? v_subsample : 1) * 8058c2ecf20Sopenharmony_ci tile_w; 8068c2ecf20Sopenharmony_ci vc4_state->offsets[i] += x_off / 8078c2ecf20Sopenharmony_ci (i ? h_subsample : 1) * 8088c2ecf20Sopenharmony_ci fb->format->cpp[i]; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT); 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci default: 8168c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx", 8178c2ecf20Sopenharmony_ci (long long)fb->modifier); 8188c2ecf20Sopenharmony_ci return -EINVAL; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* Don't waste cycles mixing with plane alpha if the set alpha 8228c2ecf20Sopenharmony_ci * is opaque or there is no per-pixel alpha information. 8238c2ecf20Sopenharmony_ci * In any case we use the alpha property value as the fixed alpha. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE && 8268c2ecf20Sopenharmony_ci fb->format->has_alpha; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (!vc4->hvs->hvs5) { 8298c2ecf20Sopenharmony_ci /* Control word */ 8308c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 8318c2ecf20Sopenharmony_ci SCALER_CTL0_VALID | 8328c2ecf20Sopenharmony_ci (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) | 8338c2ecf20Sopenharmony_ci (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) | 8348c2ecf20Sopenharmony_ci VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) | 8358c2ecf20Sopenharmony_ci (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | 8368c2ecf20Sopenharmony_ci (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | 8378c2ecf20Sopenharmony_ci VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | 8388c2ecf20Sopenharmony_ci (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | 8398c2ecf20Sopenharmony_ci VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | 8408c2ecf20Sopenharmony_ci VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1)); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* Position Word 0: Image Positions and Alpha Value */ 8438c2ecf20Sopenharmony_ci vc4_state->pos0_offset = vc4_state->dlist_count; 8448c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 8458c2ecf20Sopenharmony_ci VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) | 8468c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | 8478c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* Position Word 1: Scaled Image Dimensions. */ 8508c2ecf20Sopenharmony_ci if (!vc4_state->is_unity) { 8518c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 8528c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_w, 8538c2ecf20Sopenharmony_ci SCALER_POS1_SCL_WIDTH) | 8548c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_h, 8558c2ecf20Sopenharmony_ci SCALER_POS1_SCL_HEIGHT)); 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* Position Word 2: Source Image Size, Alpha */ 8598c2ecf20Sopenharmony_ci vc4_state->pos2_offset = vc4_state->dlist_count; 8608c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 8618c2ecf20Sopenharmony_ci VC4_SET_FIELD(fb->format->has_alpha ? 8628c2ecf20Sopenharmony_ci SCALER_POS2_ALPHA_MODE_PIPELINE : 8638c2ecf20Sopenharmony_ci SCALER_POS2_ALPHA_MODE_FIXED, 8648c2ecf20Sopenharmony_ci SCALER_POS2_ALPHA_MODE) | 8658c2ecf20Sopenharmony_ci (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | 8668c2ecf20Sopenharmony_ci (fb->format->has_alpha ? 8678c2ecf20Sopenharmony_ci SCALER_POS2_ALPHA_PREMULT : 0) | 8688c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->src_w[0], 8698c2ecf20Sopenharmony_ci SCALER_POS2_WIDTH) | 8708c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->src_h[0], 8718c2ecf20Sopenharmony_ci SCALER_POS2_HEIGHT)); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* Position Word 3: Context. Written by the HVS. */ 8748c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 0xc0c0c0c0); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci } else { 8778c2ecf20Sopenharmony_ci u32 hvs_pixel_order = format->pixel_order; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (format->pixel_order_hvs5) 8808c2ecf20Sopenharmony_ci hvs_pixel_order = format->pixel_order_hvs5; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* Control word */ 8838c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 8848c2ecf20Sopenharmony_ci SCALER_CTL0_VALID | 8858c2ecf20Sopenharmony_ci (hvs_pixel_order << SCALER_CTL0_ORDER_SHIFT) | 8868c2ecf20Sopenharmony_ci (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | 8878c2ecf20Sopenharmony_ci VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | 8888c2ecf20Sopenharmony_ci (vc4_state->is_unity ? 8898c2ecf20Sopenharmony_ci SCALER5_CTL0_UNITY : 0) | 8908c2ecf20Sopenharmony_ci VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | 8918c2ecf20Sopenharmony_ci VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1) | 8928c2ecf20Sopenharmony_ci SCALER5_CTL0_ALPHA_EXPAND | 8938c2ecf20Sopenharmony_ci SCALER5_CTL0_RGB_EXPAND); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* Position Word 0: Image Positions and Alpha Value */ 8968c2ecf20Sopenharmony_ci vc4_state->pos0_offset = vc4_state->dlist_count; 8978c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 8988c2ecf20Sopenharmony_ci (rotation & DRM_MODE_REFLECT_Y ? 8998c2ecf20Sopenharmony_ci SCALER5_POS0_VFLIP : 0) | 9008c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_x, 9018c2ecf20Sopenharmony_ci SCALER_POS0_START_X) | 9028c2ecf20Sopenharmony_ci (rotation & DRM_MODE_REFLECT_X ? 9038c2ecf20Sopenharmony_ci SCALER5_POS0_HFLIP : 0) | 9048c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_y, 9058c2ecf20Sopenharmony_ci SCALER5_POS0_START_Y) 9068c2ecf20Sopenharmony_ci ); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* Control Word 2 */ 9098c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 9108c2ecf20Sopenharmony_ci VC4_SET_FIELD(state->alpha >> 4, 9118c2ecf20Sopenharmony_ci SCALER5_CTL2_ALPHA) | 9128c2ecf20Sopenharmony_ci (fb->format->has_alpha ? 9138c2ecf20Sopenharmony_ci SCALER5_CTL2_ALPHA_PREMULT : 0) | 9148c2ecf20Sopenharmony_ci (mix_plane_alpha ? 9158c2ecf20Sopenharmony_ci SCALER5_CTL2_ALPHA_MIX : 0) | 9168c2ecf20Sopenharmony_ci VC4_SET_FIELD(fb->format->has_alpha ? 9178c2ecf20Sopenharmony_ci SCALER5_CTL2_ALPHA_MODE_PIPELINE : 9188c2ecf20Sopenharmony_ci SCALER5_CTL2_ALPHA_MODE_FIXED, 9198c2ecf20Sopenharmony_ci SCALER5_CTL2_ALPHA_MODE) 9208c2ecf20Sopenharmony_ci ); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* Position Word 1: Scaled Image Dimensions. */ 9238c2ecf20Sopenharmony_ci if (!vc4_state->is_unity) { 9248c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 9258c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_w, 9268c2ecf20Sopenharmony_ci SCALER5_POS1_SCL_WIDTH) | 9278c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->crtc_h, 9288c2ecf20Sopenharmony_ci SCALER5_POS1_SCL_HEIGHT)); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Position Word 2: Source Image Size */ 9328c2ecf20Sopenharmony_ci vc4_state->pos2_offset = vc4_state->dlist_count; 9338c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 9348c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->src_w[0], 9358c2ecf20Sopenharmony_ci SCALER5_POS2_WIDTH) | 9368c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->src_h[0], 9378c2ecf20Sopenharmony_ci SCALER5_POS2_HEIGHT)); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Position Word 3: Context. Written by the HVS. */ 9408c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 0xc0c0c0c0); 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers 9458c2ecf20Sopenharmony_ci * 9468c2ecf20Sopenharmony_ci * The pointers may be any byte address. 9478c2ecf20Sopenharmony_ci */ 9488c2ecf20Sopenharmony_ci vc4_state->ptr0_offset = vc4_state->dlist_count; 9498c2ecf20Sopenharmony_ci for (i = 0; i < num_planes; i++) 9508c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, vc4_state->offsets[i]); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* Pointer Context Word 0/1/2: Written by the HVS */ 9538c2ecf20Sopenharmony_ci for (i = 0; i < num_planes; i++) 9548c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 0xc0c0c0c0); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* Pitch word 0 */ 9578c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, pitch0); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* Pitch word 1/2 */ 9608c2ecf20Sopenharmony_ci for (i = 1; i < num_planes; i++) { 9618c2ecf20Sopenharmony_ci if (hvs_format != HVS_PIXEL_FORMAT_H264) { 9628c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, 9638c2ecf20Sopenharmony_ci VC4_SET_FIELD(fb->pitches[i], 9648c2ecf20Sopenharmony_ci SCALER_SRC_PITCH)); 9658c2ecf20Sopenharmony_ci } else { 9668c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, pitch0); 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci /* Colorspace conversion words */ 9718c2ecf20Sopenharmony_ci if (vc4_state->is_yuv) { 9728c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5); 9738c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5); 9748c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5); 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci vc4_state->lbm_offset = 0; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (vc4_state->x_scaling[0] != VC4_SCALING_NONE || 9808c2ecf20Sopenharmony_ci vc4_state->x_scaling[1] != VC4_SCALING_NONE || 9818c2ecf20Sopenharmony_ci vc4_state->y_scaling[0] != VC4_SCALING_NONE || 9828c2ecf20Sopenharmony_ci vc4_state->y_scaling[1] != VC4_SCALING_NONE) { 9838c2ecf20Sopenharmony_ci /* Reserve a slot for the LBM Base Address. The real value will 9848c2ecf20Sopenharmony_ci * be set when calling vc4_plane_allocate_lbm(). 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ci if (vc4_state->y_scaling[0] != VC4_SCALING_NONE || 9878c2ecf20Sopenharmony_ci vc4_state->y_scaling[1] != VC4_SCALING_NONE) { 9888c2ecf20Sopenharmony_ci vc4_state->lbm_offset = vc4_state->dlist_count; 9898c2ecf20Sopenharmony_ci vc4_dlist_counter_increment(vc4_state); 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (num_planes > 1) { 9938c2ecf20Sopenharmony_ci /* Emit Cb/Cr as channel 0 and Y as channel 9948c2ecf20Sopenharmony_ci * 1. This matches how we set up scl0/scl1 9958c2ecf20Sopenharmony_ci * above. 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_ci vc4_write_scaling_parameters(state, 1); 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci vc4_write_scaling_parameters(state, 0); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* If any PPF setup was done, then all the kernel 10028c2ecf20Sopenharmony_ci * pointers get uploaded. 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_ci if (vc4_state->x_scaling[0] == VC4_SCALING_PPF || 10058c2ecf20Sopenharmony_ci vc4_state->y_scaling[0] == VC4_SCALING_PPF || 10068c2ecf20Sopenharmony_ci vc4_state->x_scaling[1] == VC4_SCALING_PPF || 10078c2ecf20Sopenharmony_ci vc4_state->y_scaling[1] == VC4_SCALING_PPF) { 10088c2ecf20Sopenharmony_ci u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start, 10098c2ecf20Sopenharmony_ci SCALER_PPF_KERNEL_OFFSET); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* HPPF plane 0 */ 10128c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, kernel); 10138c2ecf20Sopenharmony_ci /* VPPF plane 0 */ 10148c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, kernel); 10158c2ecf20Sopenharmony_ci /* HPPF plane 1 */ 10168c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, kernel); 10178c2ecf20Sopenharmony_ci /* VPPF plane 1 */ 10188c2ecf20Sopenharmony_ci vc4_dlist_write(vc4_state, kernel); 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci vc4_state->dlist[ctl0_offset] |= 10238c2ecf20Sopenharmony_ci VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* crtc_* are already clipped coordinates. */ 10268c2ecf20Sopenharmony_ci covers_screen = vc4_state->crtc_x == 0 && vc4_state->crtc_y == 0 && 10278c2ecf20Sopenharmony_ci vc4_state->crtc_w == state->crtc->mode.hdisplay && 10288c2ecf20Sopenharmony_ci vc4_state->crtc_h == state->crtc->mode.vdisplay; 10298c2ecf20Sopenharmony_ci /* Background fill might be necessary when the plane has per-pixel 10308c2ecf20Sopenharmony_ci * alpha content or a non-opaque plane alpha and could blend from the 10318c2ecf20Sopenharmony_ci * background or does not cover the entire screen. 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_ci vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen || 10348c2ecf20Sopenharmony_ci state->alpha != DRM_BLEND_ALPHA_OPAQUE; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* Flag the dlist as initialized to avoid checking it twice in case 10378c2ecf20Sopenharmony_ci * the async update check already called vc4_plane_mode_set() and 10388c2ecf20Sopenharmony_ci * decided to fallback to sync update because async update was not 10398c2ecf20Sopenharmony_ci * possible. 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_ci vc4_state->dlist_initialized = 1; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci vc4_plane_calc_load(state); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci return 0; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/* If a modeset involves changing the setup of a plane, the atomic 10498c2ecf20Sopenharmony_ci * infrastructure will call this to validate a proposed plane setup. 10508c2ecf20Sopenharmony_ci * However, if a plane isn't getting updated, this (and the 10518c2ecf20Sopenharmony_ci * corresponding vc4_plane_atomic_update) won't get called. Thus, we 10528c2ecf20Sopenharmony_ci * compute the dlist here and have all active plane dlists get updated 10538c2ecf20Sopenharmony_ci * in the CRTC's flush. 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_cistatic int vc4_plane_atomic_check(struct drm_plane *plane, 10568c2ecf20Sopenharmony_ci struct drm_plane_state *state) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); 10598c2ecf20Sopenharmony_ci int ret; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci vc4_state->dlist_count = 0; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (!plane_enabled(state)) 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci ret = vc4_plane_mode_set(plane, state); 10678c2ecf20Sopenharmony_ci if (ret) 10688c2ecf20Sopenharmony_ci return ret; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci return vc4_plane_allocate_lbm(state); 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void vc4_plane_atomic_update(struct drm_plane *plane, 10748c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci /* No contents here. Since we don't know where in the CRTC's 10778c2ecf20Sopenharmony_ci * dlist we should be stored, our dlist is uploaded to the 10788c2ecf20Sopenharmony_ci * hardware with vc4_plane_write_dlist() at CRTC atomic_flush 10798c2ecf20Sopenharmony_ci * time. 10808c2ecf20Sopenharmony_ci */ 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ciu32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); 10868c2ecf20Sopenharmony_ci int i; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci vc4_state->hw_dlist = dlist; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* Can't memcpy_toio() because it needs to be 32-bit writes. */ 10918c2ecf20Sopenharmony_ci for (i = 0; i < vc4_state->dlist_count; i++) 10928c2ecf20Sopenharmony_ci writel(vc4_state->dlist[i], &dlist[i]); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return vc4_state->dlist_count; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ciu32 vc4_plane_dlist_size(const struct drm_plane_state *state) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci const struct vc4_plane_state *vc4_state = 11008c2ecf20Sopenharmony_ci container_of(state, typeof(*vc4_state), base); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci return vc4_state->dlist_count; 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci/* Updates the plane to immediately (well, once the FIFO needs 11068c2ecf20Sopenharmony_ci * refilling) scan out from at a new framebuffer. 11078c2ecf20Sopenharmony_ci */ 11088c2ecf20Sopenharmony_civoid vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); 11118c2ecf20Sopenharmony_ci struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); 11128c2ecf20Sopenharmony_ci uint32_t addr; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci /* We're skipping the address adjustment for negative origin, 11158c2ecf20Sopenharmony_ci * because this is only called on the primary plane. 11168c2ecf20Sopenharmony_ci */ 11178c2ecf20Sopenharmony_ci WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); 11188c2ecf20Sopenharmony_ci addr = bo->paddr + fb->offsets[0]; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* Write the new address into the hardware immediately. The 11218c2ecf20Sopenharmony_ci * scanout will start from this address as soon as the FIFO 11228c2ecf20Sopenharmony_ci * needs to refill with pixels. 11238c2ecf20Sopenharmony_ci */ 11248c2ecf20Sopenharmony_ci writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* Also update the CPU-side dlist copy, so that any later 11278c2ecf20Sopenharmony_ci * atomic updates that don't do a new modeset on our plane 11288c2ecf20Sopenharmony_ci * also use our updated address. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci vc4_state->dlist[vc4_state->ptr0_offset] = addr; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic void vc4_plane_atomic_async_update(struct drm_plane *plane, 11348c2ecf20Sopenharmony_ci struct drm_plane_state *state) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_state, *new_vc4_state; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci swap(plane->state->fb, state->fb); 11398c2ecf20Sopenharmony_ci plane->state->crtc_x = state->crtc_x; 11408c2ecf20Sopenharmony_ci plane->state->crtc_y = state->crtc_y; 11418c2ecf20Sopenharmony_ci plane->state->crtc_w = state->crtc_w; 11428c2ecf20Sopenharmony_ci plane->state->crtc_h = state->crtc_h; 11438c2ecf20Sopenharmony_ci plane->state->src_x = state->src_x; 11448c2ecf20Sopenharmony_ci plane->state->src_y = state->src_y; 11458c2ecf20Sopenharmony_ci plane->state->src_w = state->src_w; 11468c2ecf20Sopenharmony_ci plane->state->src_h = state->src_h; 11478c2ecf20Sopenharmony_ci plane->state->src_h = state->src_h; 11488c2ecf20Sopenharmony_ci plane->state->alpha = state->alpha; 11498c2ecf20Sopenharmony_ci plane->state->pixel_blend_mode = state->pixel_blend_mode; 11508c2ecf20Sopenharmony_ci plane->state->rotation = state->rotation; 11518c2ecf20Sopenharmony_ci plane->state->zpos = state->zpos; 11528c2ecf20Sopenharmony_ci plane->state->normalized_zpos = state->normalized_zpos; 11538c2ecf20Sopenharmony_ci plane->state->color_encoding = state->color_encoding; 11548c2ecf20Sopenharmony_ci plane->state->color_range = state->color_range; 11558c2ecf20Sopenharmony_ci plane->state->src = state->src; 11568c2ecf20Sopenharmony_ci plane->state->dst = state->dst; 11578c2ecf20Sopenharmony_ci plane->state->visible = state->visible; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci new_vc4_state = to_vc4_plane_state(state); 11608c2ecf20Sopenharmony_ci vc4_state = to_vc4_plane_state(plane->state); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci vc4_state->crtc_x = new_vc4_state->crtc_x; 11638c2ecf20Sopenharmony_ci vc4_state->crtc_y = new_vc4_state->crtc_y; 11648c2ecf20Sopenharmony_ci vc4_state->crtc_h = new_vc4_state->crtc_h; 11658c2ecf20Sopenharmony_ci vc4_state->crtc_w = new_vc4_state->crtc_w; 11668c2ecf20Sopenharmony_ci vc4_state->src_x = new_vc4_state->src_x; 11678c2ecf20Sopenharmony_ci vc4_state->src_y = new_vc4_state->src_y; 11688c2ecf20Sopenharmony_ci memcpy(vc4_state->src_w, new_vc4_state->src_w, 11698c2ecf20Sopenharmony_ci sizeof(vc4_state->src_w)); 11708c2ecf20Sopenharmony_ci memcpy(vc4_state->src_h, new_vc4_state->src_h, 11718c2ecf20Sopenharmony_ci sizeof(vc4_state->src_h)); 11728c2ecf20Sopenharmony_ci memcpy(vc4_state->x_scaling, new_vc4_state->x_scaling, 11738c2ecf20Sopenharmony_ci sizeof(vc4_state->x_scaling)); 11748c2ecf20Sopenharmony_ci memcpy(vc4_state->y_scaling, new_vc4_state->y_scaling, 11758c2ecf20Sopenharmony_ci sizeof(vc4_state->y_scaling)); 11768c2ecf20Sopenharmony_ci vc4_state->is_unity = new_vc4_state->is_unity; 11778c2ecf20Sopenharmony_ci vc4_state->is_yuv = new_vc4_state->is_yuv; 11788c2ecf20Sopenharmony_ci memcpy(vc4_state->offsets, new_vc4_state->offsets, 11798c2ecf20Sopenharmony_ci sizeof(vc4_state->offsets)); 11808c2ecf20Sopenharmony_ci vc4_state->needs_bg_fill = new_vc4_state->needs_bg_fill; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */ 11838c2ecf20Sopenharmony_ci vc4_state->dlist[vc4_state->pos0_offset] = 11848c2ecf20Sopenharmony_ci new_vc4_state->dlist[vc4_state->pos0_offset]; 11858c2ecf20Sopenharmony_ci vc4_state->dlist[vc4_state->pos2_offset] = 11868c2ecf20Sopenharmony_ci new_vc4_state->dlist[vc4_state->pos2_offset]; 11878c2ecf20Sopenharmony_ci vc4_state->dlist[vc4_state->ptr0_offset] = 11888c2ecf20Sopenharmony_ci new_vc4_state->dlist[vc4_state->ptr0_offset]; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Note that we can't just call vc4_plane_write_dlist() 11918c2ecf20Sopenharmony_ci * because that would smash the context data that the HVS is 11928c2ecf20Sopenharmony_ci * currently using. 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_ci writel(vc4_state->dlist[vc4_state->pos0_offset], 11958c2ecf20Sopenharmony_ci &vc4_state->hw_dlist[vc4_state->pos0_offset]); 11968c2ecf20Sopenharmony_ci writel(vc4_state->dlist[vc4_state->pos2_offset], 11978c2ecf20Sopenharmony_ci &vc4_state->hw_dlist[vc4_state->pos2_offset]); 11988c2ecf20Sopenharmony_ci writel(vc4_state->dlist[vc4_state->ptr0_offset], 11998c2ecf20Sopenharmony_ci &vc4_state->hw_dlist[vc4_state->ptr0_offset]); 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic int vc4_plane_atomic_async_check(struct drm_plane *plane, 12038c2ecf20Sopenharmony_ci struct drm_plane_state *state) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct vc4_plane_state *old_vc4_state, *new_vc4_state; 12068c2ecf20Sopenharmony_ci int ret; 12078c2ecf20Sopenharmony_ci u32 i; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci ret = vc4_plane_mode_set(plane, state); 12108c2ecf20Sopenharmony_ci if (ret) 12118c2ecf20Sopenharmony_ci return ret; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci old_vc4_state = to_vc4_plane_state(plane->state); 12148c2ecf20Sopenharmony_ci new_vc4_state = to_vc4_plane_state(state); 12158c2ecf20Sopenharmony_ci if (old_vc4_state->dlist_count != new_vc4_state->dlist_count || 12168c2ecf20Sopenharmony_ci old_vc4_state->pos0_offset != new_vc4_state->pos0_offset || 12178c2ecf20Sopenharmony_ci old_vc4_state->pos2_offset != new_vc4_state->pos2_offset || 12188c2ecf20Sopenharmony_ci old_vc4_state->ptr0_offset != new_vc4_state->ptr0_offset || 12198c2ecf20Sopenharmony_ci vc4_lbm_size(plane->state) != vc4_lbm_size(state)) 12208c2ecf20Sopenharmony_ci return -EINVAL; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* Only pos0, pos2 and ptr0 DWORDS can be updated in an async update 12238c2ecf20Sopenharmony_ci * if anything else has changed, fallback to a sync update. 12248c2ecf20Sopenharmony_ci */ 12258c2ecf20Sopenharmony_ci for (i = 0; i < new_vc4_state->dlist_count; i++) { 12268c2ecf20Sopenharmony_ci if (i == new_vc4_state->pos0_offset || 12278c2ecf20Sopenharmony_ci i == new_vc4_state->pos2_offset || 12288c2ecf20Sopenharmony_ci i == new_vc4_state->ptr0_offset || 12298c2ecf20Sopenharmony_ci (new_vc4_state->lbm_offset && 12308c2ecf20Sopenharmony_ci i == new_vc4_state->lbm_offset)) 12318c2ecf20Sopenharmony_ci continue; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if (new_vc4_state->dlist[i] != old_vc4_state->dlist[i]) 12348c2ecf20Sopenharmony_ci return -EINVAL; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic int vc4_prepare_fb(struct drm_plane *plane, 12418c2ecf20Sopenharmony_ci struct drm_plane_state *state) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci struct vc4_bo *bo; 12448c2ecf20Sopenharmony_ci int ret; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (!state->fb) 12478c2ecf20Sopenharmony_ci return 0; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci drm_gem_fb_prepare_fb(plane, state); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (plane->state->fb == state->fb) 12548c2ecf20Sopenharmony_ci return 0; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci ret = vc4_bo_inc_usecnt(bo); 12578c2ecf20Sopenharmony_ci if (ret) 12588c2ecf20Sopenharmony_ci return ret; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return 0; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistatic void vc4_cleanup_fb(struct drm_plane *plane, 12648c2ecf20Sopenharmony_ci struct drm_plane_state *state) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct vc4_bo *bo; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci if (plane->state->fb == state->fb || !state->fb) 12698c2ecf20Sopenharmony_ci return; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); 12728c2ecf20Sopenharmony_ci vc4_bo_dec_usecnt(bo); 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { 12768c2ecf20Sopenharmony_ci .atomic_check = vc4_plane_atomic_check, 12778c2ecf20Sopenharmony_ci .atomic_update = vc4_plane_atomic_update, 12788c2ecf20Sopenharmony_ci .prepare_fb = vc4_prepare_fb, 12798c2ecf20Sopenharmony_ci .cleanup_fb = vc4_cleanup_fb, 12808c2ecf20Sopenharmony_ci .atomic_async_check = vc4_plane_atomic_async_check, 12818c2ecf20Sopenharmony_ci .atomic_async_update = vc4_plane_atomic_async_update, 12828c2ecf20Sopenharmony_ci}; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic void vc4_plane_destroy(struct drm_plane *plane) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci drm_plane_cleanup(plane); 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic bool vc4_format_mod_supported(struct drm_plane *plane, 12908c2ecf20Sopenharmony_ci uint32_t format, 12918c2ecf20Sopenharmony_ci uint64_t modifier) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci /* Support T_TILING for RGB formats only. */ 12948c2ecf20Sopenharmony_ci switch (format) { 12958c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 12968c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 12978c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR8888: 12988c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR8888: 12998c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 13008c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR565: 13018c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB1555: 13028c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 13038c2ecf20Sopenharmony_ci switch (fourcc_mod_broadcom_mod(modifier)) { 13048c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_LINEAR: 13058c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: 13068c2ecf20Sopenharmony_ci return true; 13078c2ecf20Sopenharmony_ci default: 13088c2ecf20Sopenharmony_ci return false; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci case DRM_FORMAT_NV12: 13118c2ecf20Sopenharmony_ci case DRM_FORMAT_NV21: 13128c2ecf20Sopenharmony_ci switch (fourcc_mod_broadcom_mod(modifier)) { 13138c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_LINEAR: 13148c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND64: 13158c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND128: 13168c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_BROADCOM_SAND256: 13178c2ecf20Sopenharmony_ci return true; 13188c2ecf20Sopenharmony_ci default: 13198c2ecf20Sopenharmony_ci return false; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBX1010102: 13228c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRX1010102: 13238c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBA1010102: 13248c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRA1010102: 13258c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV422: 13268c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU422: 13278c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV420: 13288c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU420: 13298c2ecf20Sopenharmony_ci case DRM_FORMAT_NV16: 13308c2ecf20Sopenharmony_ci case DRM_FORMAT_NV61: 13318c2ecf20Sopenharmony_ci default: 13328c2ecf20Sopenharmony_ci return (modifier == DRM_FORMAT_MOD_LINEAR); 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs vc4_plane_funcs = { 13378c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 13388c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 13398c2ecf20Sopenharmony_ci .destroy = vc4_plane_destroy, 13408c2ecf20Sopenharmony_ci .set_property = NULL, 13418c2ecf20Sopenharmony_ci .reset = vc4_plane_reset, 13428c2ecf20Sopenharmony_ci .atomic_duplicate_state = vc4_plane_duplicate_state, 13438c2ecf20Sopenharmony_ci .atomic_destroy_state = vc4_plane_destroy_state, 13448c2ecf20Sopenharmony_ci .format_mod_supported = vc4_format_mod_supported, 13458c2ecf20Sopenharmony_ci}; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistruct drm_plane *vc4_plane_init(struct drm_device *dev, 13488c2ecf20Sopenharmony_ci enum drm_plane_type type) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci struct drm_plane *plane = NULL; 13518c2ecf20Sopenharmony_ci struct vc4_plane *vc4_plane; 13528c2ecf20Sopenharmony_ci u32 formats[ARRAY_SIZE(hvs_formats)]; 13538c2ecf20Sopenharmony_ci int ret = 0; 13548c2ecf20Sopenharmony_ci unsigned i; 13558c2ecf20Sopenharmony_ci static const uint64_t modifiers[] = { 13568c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED, 13578c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_BROADCOM_SAND128, 13588c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_BROADCOM_SAND64, 13598c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_BROADCOM_SAND256, 13608c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 13618c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_INVALID 13628c2ecf20Sopenharmony_ci }; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), 13658c2ecf20Sopenharmony_ci GFP_KERNEL); 13668c2ecf20Sopenharmony_ci if (!vc4_plane) 13678c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) 13708c2ecf20Sopenharmony_ci formats[i] = hvs_formats[i].drm; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci plane = &vc4_plane->base; 13738c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(dev, plane, 0, 13748c2ecf20Sopenharmony_ci &vc4_plane_funcs, 13758c2ecf20Sopenharmony_ci formats, ARRAY_SIZE(formats), 13768c2ecf20Sopenharmony_ci modifiers, type, NULL); 13778c2ecf20Sopenharmony_ci if (ret) 13788c2ecf20Sopenharmony_ci return ERR_PTR(ret); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci drm_plane_helper_add(plane, &vc4_plane_helper_funcs); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci drm_plane_create_alpha_property(plane); 13838c2ecf20Sopenharmony_ci drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, 13848c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0 | 13858c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_180 | 13868c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_X | 13878c2ecf20Sopenharmony_ci DRM_MODE_REFLECT_Y); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci return plane; 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ciint vc4_plane_create_additional_planes(struct drm_device *drm) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci struct drm_plane *cursor_plane; 13958c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 13968c2ecf20Sopenharmony_ci unsigned int i; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* Set up some arbitrary number of planes. We're not limited 13998c2ecf20Sopenharmony_ci * by a set number of physical registers, just the space in 14008c2ecf20Sopenharmony_ci * the HVS (16k) and how small an plane can be (28 bytes). 14018c2ecf20Sopenharmony_ci * However, each plane we set up takes up some memory, and 14028c2ecf20Sopenharmony_ci * increases the cost of looping over planes, which atomic 14038c2ecf20Sopenharmony_ci * modesetting does quite a bit. As a result, we pick a 14048c2ecf20Sopenharmony_ci * modest number of planes to expose, that should hopefully 14058c2ecf20Sopenharmony_ci * still cover any sane usecase. 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 14088c2ecf20Sopenharmony_ci struct drm_plane *plane = 14098c2ecf20Sopenharmony_ci vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (IS_ERR(plane)) 14128c2ecf20Sopenharmony_ci continue; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci plane->possible_crtcs = 14158c2ecf20Sopenharmony_ci GENMASK(drm->mode_config.num_crtc - 1, 0); 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, drm) { 14198c2ecf20Sopenharmony_ci /* Set up the legacy cursor after overlay initialization, 14208c2ecf20Sopenharmony_ci * since we overlay planes on the CRTC in the order they were 14218c2ecf20Sopenharmony_ci * initialized. 14228c2ecf20Sopenharmony_ci */ 14238c2ecf20Sopenharmony_ci cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); 14248c2ecf20Sopenharmony_ci if (!IS_ERR(cursor_plane)) { 14258c2ecf20Sopenharmony_ci cursor_plane->possible_crtcs = drm_crtc_mask(crtc); 14268c2ecf20Sopenharmony_ci crtc->cursor = cursor_plane; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci return 0; 14318c2ecf20Sopenharmony_ci} 1432