18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rcar_du_plane.c -- R-Car Display Unit Planes 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "rcar_du_drv.h" 208c2ecf20Sopenharmony_ci#include "rcar_du_group.h" 218c2ecf20Sopenharmony_ci#include "rcar_du_kms.h" 228c2ecf20Sopenharmony_ci#include "rcar_du_plane.h" 238c2ecf20Sopenharmony_ci#include "rcar_du_regs.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 268c2ecf20Sopenharmony_ci * Atomic hardware plane allocator 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * The hardware plane allocator is solely based on the atomic plane states 298c2ecf20Sopenharmony_ci * without keeping any external state to avoid races between .atomic_check() 308c2ecf20Sopenharmony_ci * and .atomic_commit(). 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * The core idea is to avoid using a free planes bitmask that would need to be 338c2ecf20Sopenharmony_ci * shared between check and commit handlers with a collective knowledge based on 348c2ecf20Sopenharmony_ci * the allocated hardware plane(s) for each KMS plane. The allocator then loops 358c2ecf20Sopenharmony_ci * over all plane states to compute the free planes bitmask, allocates hardware 368c2ecf20Sopenharmony_ci * planes based on that bitmask, and stores the result back in the plane states. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * For this to work we need to access the current state of planes not touched by 398c2ecf20Sopenharmony_ci * the atomic update. To ensure that it won't be modified, we need to lock all 408c2ecf20Sopenharmony_ci * planes using drm_atomic_get_plane_state(). This effectively serializes atomic 418c2ecf20Sopenharmony_ci * updates from .atomic_check() up to completion (when swapping the states if 428c2ecf20Sopenharmony_ci * the check step has succeeded) or rollback (when freeing the states if the 438c2ecf20Sopenharmony_ci * check step has failed). 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * Allocation is performed in the .atomic_check() handler and applied 468c2ecf20Sopenharmony_ci * automatically when the core swaps the old and new states. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic bool rcar_du_plane_needs_realloc( 508c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *old_state, 518c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *new_state) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Lowering the number of planes doesn't strictly require reallocation 558c2ecf20Sopenharmony_ci * as the extra hardware plane will be freed when committing, but doing 568c2ecf20Sopenharmony_ci * so could lead to more fragmentation. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci if (!old_state->format || 598c2ecf20Sopenharmony_ci old_state->format->planes != new_state->format->planes) 608c2ecf20Sopenharmony_ci return true; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* Reallocate hardware planes if the source has changed. */ 638c2ecf20Sopenharmony_ci if (old_state->source != new_state->source) 648c2ecf20Sopenharmony_ci return true; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return false; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci unsigned int mask; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (state->hwindex == -1) 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci mask = 1 << state->hwindex; 778c2ecf20Sopenharmony_ci if (state->format->planes == 2) 788c2ecf20Sopenharmony_ci mask |= 1 << ((state->hwindex + 1) % 8); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return mask; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and 858c2ecf20Sopenharmony_ci * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or 868c2ecf20Sopenharmony_ci * DU0/1 plane 1. 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1, 898c2ecf20Sopenharmony_ci * and allocate planes in reverse index order otherwise to ensure maximum 908c2ecf20Sopenharmony_ci * availability of planes 0 and 1. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * The caller is responsible for ensuring that the requested source is 938c2ecf20Sopenharmony_ci * compatible with the DU revision. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_cistatic int rcar_du_plane_hwalloc(struct rcar_du_plane *plane, 968c2ecf20Sopenharmony_ci struct rcar_du_plane_state *state, 978c2ecf20Sopenharmony_ci unsigned int free) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci unsigned int num_planes = state->format->planes; 1008c2ecf20Sopenharmony_ci int fixed = -1; 1018c2ecf20Sopenharmony_ci int i; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (state->source == RCAR_DU_PLANE_VSPD0) { 1048c2ecf20Sopenharmony_ci /* VSPD0 feeds plane 0 on DU0/1. */ 1058c2ecf20Sopenharmony_ci if (plane->group->index != 0) 1068c2ecf20Sopenharmony_ci return -EINVAL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci fixed = 0; 1098c2ecf20Sopenharmony_ci } else if (state->source == RCAR_DU_PLANE_VSPD1) { 1108c2ecf20Sopenharmony_ci /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */ 1118c2ecf20Sopenharmony_ci fixed = plane->group->index == 0 ? 1 : 0; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (fixed >= 0) 1158c2ecf20Sopenharmony_ci return free & (1 << fixed) ? fixed : -EBUSY; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) { 1188c2ecf20Sopenharmony_ci if (!(free & (1 << i))) 1198c2ecf20Sopenharmony_ci continue; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (num_planes == 1 || free & (1 << ((i + 1) % 8))) 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return i < 0 ? -EBUSY : i; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciint rcar_du_atomic_check_planes(struct drm_device *dev, 1298c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct rcar_du_device *rcdu = dev->dev_private; 1328c2ecf20Sopenharmony_ci unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, }; 1338c2ecf20Sopenharmony_ci unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, }; 1348c2ecf20Sopenharmony_ci bool needs_realloc = false; 1358c2ecf20Sopenharmony_ci unsigned int groups = 0; 1368c2ecf20Sopenharmony_ci unsigned int i; 1378c2ecf20Sopenharmony_ci struct drm_plane *drm_plane; 1388c2ecf20Sopenharmony_ci struct drm_plane_state *old_drm_plane_state; 1398c2ecf20Sopenharmony_ci struct drm_plane_state *new_drm_plane_state; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Check if hardware planes need to be reallocated. */ 1428c2ecf20Sopenharmony_ci for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state, 1438c2ecf20Sopenharmony_ci new_drm_plane_state, i) { 1448c2ecf20Sopenharmony_ci struct rcar_du_plane_state *old_plane_state; 1458c2ecf20Sopenharmony_ci struct rcar_du_plane_state *new_plane_state; 1468c2ecf20Sopenharmony_ci struct rcar_du_plane *plane; 1478c2ecf20Sopenharmony_ci unsigned int index; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci plane = to_rcar_plane(drm_plane); 1508c2ecf20Sopenharmony_ci old_plane_state = to_rcar_plane_state(old_drm_plane_state); 1518c2ecf20Sopenharmony_ci new_plane_state = to_rcar_plane_state(new_drm_plane_state); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__, 1548c2ecf20Sopenharmony_ci plane->group->index, plane - plane->group->planes); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * If the plane is being disabled we don't need to go through 1588c2ecf20Sopenharmony_ci * the full reallocation procedure. Just mark the hardware 1598c2ecf20Sopenharmony_ci * plane(s) as freed. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci if (!new_plane_state->format) { 1628c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: plane is being disabled\n", 1638c2ecf20Sopenharmony_ci __func__); 1648c2ecf20Sopenharmony_ci index = plane - plane->group->planes; 1658c2ecf20Sopenharmony_ci group_freed_planes[plane->group->index] |= 1 << index; 1668c2ecf20Sopenharmony_ci new_plane_state->hwindex = -1; 1678c2ecf20Sopenharmony_ci continue; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * If the plane needs to be reallocated mark it as such, and 1728c2ecf20Sopenharmony_ci * mark the hardware plane(s) as free. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) { 1758c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: plane needs reallocation\n", 1768c2ecf20Sopenharmony_ci __func__); 1778c2ecf20Sopenharmony_ci groups |= 1 << plane->group->index; 1788c2ecf20Sopenharmony_ci needs_realloc = true; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci index = plane - plane->group->planes; 1818c2ecf20Sopenharmony_ci group_freed_planes[plane->group->index] |= 1 << index; 1828c2ecf20Sopenharmony_ci new_plane_state->hwindex = -1; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!needs_realloc) 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Grab all plane states for the groups that need reallocation to ensure 1918c2ecf20Sopenharmony_ci * locking and avoid racy updates. This serializes the update operation, 1928c2ecf20Sopenharmony_ci * but there's not much we can do about it as that's the hardware 1938c2ecf20Sopenharmony_ci * design. 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * Compute the used planes mask for each group at the same time to avoid 1968c2ecf20Sopenharmony_ci * looping over the planes separately later. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci while (groups) { 1998c2ecf20Sopenharmony_ci unsigned int index = ffs(groups) - 1; 2008c2ecf20Sopenharmony_ci struct rcar_du_group *group = &rcdu->groups[index]; 2018c2ecf20Sopenharmony_ci unsigned int used_planes = 0; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n", 2048c2ecf20Sopenharmony_ci __func__, index); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (i = 0; i < group->num_planes; ++i) { 2078c2ecf20Sopenharmony_ci struct rcar_du_plane *plane = &group->planes[i]; 2088c2ecf20Sopenharmony_ci struct rcar_du_plane_state *new_plane_state; 2098c2ecf20Sopenharmony_ci struct drm_plane_state *s; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci s = drm_atomic_get_plane_state(state, &plane->plane); 2128c2ecf20Sopenharmony_ci if (IS_ERR(s)) 2138c2ecf20Sopenharmony_ci return PTR_ERR(s); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * If the plane has been freed in the above loop its 2178c2ecf20Sopenharmony_ci * hardware planes must not be added to the used planes 2188c2ecf20Sopenharmony_ci * bitmask. However, the current state doesn't reflect 2198c2ecf20Sopenharmony_ci * the free state yet, as we've modified the new state 2208c2ecf20Sopenharmony_ci * above. Use the local freed planes list to check for 2218c2ecf20Sopenharmony_ci * that condition instead. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (group_freed_planes[index] & (1 << i)) { 2248c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, 2258c2ecf20Sopenharmony_ci "%s: plane (%u,%tu) has been freed, skipping\n", 2268c2ecf20Sopenharmony_ci __func__, plane->group->index, 2278c2ecf20Sopenharmony_ci plane - plane->group->planes); 2288c2ecf20Sopenharmony_ci continue; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci new_plane_state = to_rcar_plane_state(s); 2328c2ecf20Sopenharmony_ci used_planes |= rcar_du_plane_hwmask(new_plane_state); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, 2358c2ecf20Sopenharmony_ci "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n", 2368c2ecf20Sopenharmony_ci __func__, plane->group->index, 2378c2ecf20Sopenharmony_ci plane - plane->group->planes, 2388c2ecf20Sopenharmony_ci new_plane_state->format ? 2398c2ecf20Sopenharmony_ci new_plane_state->format->planes : 0, 2408c2ecf20Sopenharmony_ci new_plane_state->hwindex); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci group_free_planes[index] = 0xff & ~used_planes; 2448c2ecf20Sopenharmony_ci groups &= ~(1 << index); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", 2478c2ecf20Sopenharmony_ci __func__, index, group_free_planes[index]); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* Reallocate hardware planes for each plane that needs it. */ 2518c2ecf20Sopenharmony_ci for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state, 2528c2ecf20Sopenharmony_ci new_drm_plane_state, i) { 2538c2ecf20Sopenharmony_ci struct rcar_du_plane_state *old_plane_state; 2548c2ecf20Sopenharmony_ci struct rcar_du_plane_state *new_plane_state; 2558c2ecf20Sopenharmony_ci struct rcar_du_plane *plane; 2568c2ecf20Sopenharmony_ci unsigned int crtc_planes; 2578c2ecf20Sopenharmony_ci unsigned int free; 2588c2ecf20Sopenharmony_ci int idx; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci plane = to_rcar_plane(drm_plane); 2618c2ecf20Sopenharmony_ci old_plane_state = to_rcar_plane_state(old_drm_plane_state); 2628c2ecf20Sopenharmony_ci new_plane_state = to_rcar_plane_state(new_drm_plane_state); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__, 2658c2ecf20Sopenharmony_ci plane->group->index, plane - plane->group->planes); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* 2688c2ecf20Sopenharmony_ci * Skip planes that are being disabled or don't need to be 2698c2ecf20Sopenharmony_ci * reallocated. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci if (!new_plane_state->format || 2728c2ecf20Sopenharmony_ci !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) 2738c2ecf20Sopenharmony_ci continue; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* 2768c2ecf20Sopenharmony_ci * Try to allocate the plane from the free planes currently 2778c2ecf20Sopenharmony_ci * associated with the target CRTC to avoid restarting the CRTC 2788c2ecf20Sopenharmony_ci * group and thus minimize flicker. If it fails fall back to 2798c2ecf20Sopenharmony_ci * allocating from all free planes. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2 2828c2ecf20Sopenharmony_ci ? plane->group->dptsr_planes 2838c2ecf20Sopenharmony_ci : ~plane->group->dptsr_planes; 2848c2ecf20Sopenharmony_ci free = group_free_planes[plane->group->index]; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci idx = rcar_du_plane_hwalloc(plane, new_plane_state, 2878c2ecf20Sopenharmony_ci free & crtc_planes); 2888c2ecf20Sopenharmony_ci if (idx < 0) 2898c2ecf20Sopenharmony_ci idx = rcar_du_plane_hwalloc(plane, new_plane_state, 2908c2ecf20Sopenharmony_ci free); 2918c2ecf20Sopenharmony_ci if (idx < 0) { 2928c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: no available hardware plane\n", 2938c2ecf20Sopenharmony_ci __func__); 2948c2ecf20Sopenharmony_ci return idx; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n", 2988c2ecf20Sopenharmony_ci __func__, new_plane_state->format->planes, idx); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci new_plane_state->hwindex = idx; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci group_free_planes[plane->group->index] &= 3038c2ecf20Sopenharmony_ci ~rcar_du_plane_hwmask(new_plane_state); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", 3068c2ecf20Sopenharmony_ci __func__, plane->group->index, 3078c2ecf20Sopenharmony_ci group_free_planes[plane->group->index]); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3148c2ecf20Sopenharmony_ci * Plane Setup 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci#define RCAR_DU_COLORKEY_NONE (0 << 24) 3188c2ecf20Sopenharmony_ci#define RCAR_DU_COLORKEY_SOURCE (1 << 24) 3198c2ecf20Sopenharmony_ci#define RCAR_DU_COLORKEY_MASK (1 << 24) 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void rcar_du_plane_write(struct rcar_du_group *rgrp, 3228c2ecf20Sopenharmony_ci unsigned int index, u32 reg, u32 data) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg, 3258c2ecf20Sopenharmony_ci data); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp, 3298c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *state) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci unsigned int src_x = state->state.src.x1 >> 16; 3328c2ecf20Sopenharmony_ci unsigned int src_y = state->state.src.y1 >> 16; 3338c2ecf20Sopenharmony_ci unsigned int index = state->hwindex; 3348c2ecf20Sopenharmony_ci unsigned int pitch; 3358c2ecf20Sopenharmony_ci bool interlaced; 3368c2ecf20Sopenharmony_ci u32 dma[2]; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci interlaced = state->state.crtc->state->adjusted_mode.flags 3398c2ecf20Sopenharmony_ci & DRM_MODE_FLAG_INTERLACE; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (state->source == RCAR_DU_PLANE_MEMORY) { 3428c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->state.fb; 3438c2ecf20Sopenharmony_ci struct drm_gem_cma_object *gem; 3448c2ecf20Sopenharmony_ci unsigned int i; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (state->format->planes == 2) 3478c2ecf20Sopenharmony_ci pitch = fb->pitches[0]; 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci pitch = fb->pitches[0] * 8 / state->format->bpp; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < state->format->planes; ++i) { 3528c2ecf20Sopenharmony_ci gem = drm_fb_cma_get_gem_obj(fb, i); 3538c2ecf20Sopenharmony_ci dma[i] = gem->paddr + fb->offsets[i]; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci pitch = drm_rect_width(&state->state.src) >> 16; 3578c2ecf20Sopenharmony_ci dma[0] = 0; 3588c2ecf20Sopenharmony_ci dma[1] = 0; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * Memory pitch (expressed in pixels). Must be doubled for interlaced 3638c2ecf20Sopenharmony_ci * operation with 32bpp formats. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnMWR, 3668c2ecf20Sopenharmony_ci (interlaced && state->format->bpp == 32) ? 3678c2ecf20Sopenharmony_ci pitch * 2 : pitch); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * The Y position is expressed in raster line units and must be doubled 3718c2ecf20Sopenharmony_ci * for 32bpp formats, according to the R8A7790 datasheet. No mention of 3728c2ecf20Sopenharmony_ci * doubling the Y position is found in the R8A7779 datasheet, but the 3738c2ecf20Sopenharmony_ci * rule seems to apply there as well. 3748c2ecf20Sopenharmony_ci * 3758c2ecf20Sopenharmony_ci * Despite not being documented, doubling seem not to be needed when 3768c2ecf20Sopenharmony_ci * operating in interlaced mode. 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * Similarly, for the second plane, NV12 and NV21 formats seem to 3798c2ecf20Sopenharmony_ci * require a halved Y position value, in both progressive and interlaced 3808c2ecf20Sopenharmony_ci * modes. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnSPXR, src_x); 3838c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnSPYR, src_y * 3848c2ecf20Sopenharmony_ci (!interlaced && state->format->bpp == 32 ? 2 : 1)); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (state->format->planes == 2) { 3898c2ecf20Sopenharmony_ci index = (index + 1) % 8; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnMWR, pitch); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnSPXR, src_x); 3948c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnSPYR, src_y * 3958c2ecf20Sopenharmony_ci (state->format->bpp == 16 ? 2 : 1) / 2); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp, 4028c2ecf20Sopenharmony_ci unsigned int index, 4038c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *state) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci u32 colorkey; 4068c2ecf20Sopenharmony_ci u32 pnmr; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * The PnALPHAR register controls alpha-blending in 16bpp formats 4108c2ecf20Sopenharmony_ci * (ARGB1555 and XRGB1555). 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * For ARGB, set the alpha value to 0, and enable alpha-blending when 4138c2ecf20Sopenharmony_ci * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255. 4148c2ecf20Sopenharmony_ci * 4158c2ecf20Sopenharmony_ci * For XRGB, set the alpha value to the plane-wide alpha value and 4168c2ecf20Sopenharmony_ci * enable alpha-blending regardless of the X bit value. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci if (state->format->fourcc != DRM_FORMAT_XRGB1555) 4198c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); 4208c2ecf20Sopenharmony_ci else 4218c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnALPHAR, 4228c2ecf20Sopenharmony_ci PnALPHAR_ABIT_X | state->state.alpha >> 8); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci pnmr = PnMR_BM_MD | state->format->pnmr; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * Disable color keying when requested. YUV formats have the 4288c2ecf20Sopenharmony_ci * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying 4298c2ecf20Sopenharmony_ci * automatically. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) 4328c2ecf20Sopenharmony_ci pnmr |= PnMR_SPIM_TP_OFF; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* For packed YUV formats we need to select the U/V order. */ 4358c2ecf20Sopenharmony_ci if (state->format->fourcc == DRM_FORMAT_YUYV) 4368c2ecf20Sopenharmony_ci pnmr |= PnMR_YCDF_YUYV; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnMR, pnmr); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci switch (state->format->fourcc) { 4418c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 4428c2ecf20Sopenharmony_ci colorkey = ((state->colorkey & 0xf80000) >> 8) 4438c2ecf20Sopenharmony_ci | ((state->colorkey & 0x00fc00) >> 5) 4448c2ecf20Sopenharmony_ci | ((state->colorkey & 0x0000f8) >> 3); 4458c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB1555: 4498c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 4508c2ecf20Sopenharmony_ci colorkey = ((state->colorkey & 0xf80000) >> 9) 4518c2ecf20Sopenharmony_ci | ((state->colorkey & 0x00f800) >> 6) 4528c2ecf20Sopenharmony_ci | ((state->colorkey & 0x0000f8) >> 3); 4538c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 4578c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 4588c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnTC3R, 4598c2ecf20Sopenharmony_ci PnTC3R_CODE | (state->colorkey & 0xffffff)); 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp, 4658c2ecf20Sopenharmony_ci unsigned int index, 4668c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *state) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci u32 ddcr2 = PnDDCR2_CODE; 4698c2ecf20Sopenharmony_ci u32 ddcr4; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * Data format 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * The data format is selected by the DDDF field in PnMR and the EDF 4758c2ecf20Sopenharmony_ci * field in DDCR4. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci rcar_du_plane_setup_mode(rgrp, index, state); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (state->format->planes == 2) { 4818c2ecf20Sopenharmony_ci if (state->hwindex != index) { 4828c2ecf20Sopenharmony_ci if (state->format->fourcc == DRM_FORMAT_NV12 || 4838c2ecf20Sopenharmony_ci state->format->fourcc == DRM_FORMAT_NV21) 4848c2ecf20Sopenharmony_ci ddcr2 |= PnDDCR2_Y420; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (state->format->fourcc == DRM_FORMAT_NV21) 4878c2ecf20Sopenharmony_ci ddcr2 |= PnDDCR2_NV21; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci ddcr2 |= PnDDCR2_DIVU; 4908c2ecf20Sopenharmony_ci } else { 4918c2ecf20Sopenharmony_ci ddcr2 |= PnDDCR2_DIVY; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci ddcr4 = state->format->edf | PnDDCR4_CODE; 4988c2ecf20Sopenharmony_ci if (state->source != RCAR_DU_PLANE_MEMORY) 4998c2ecf20Sopenharmony_ci ddcr4 |= PnDDCR4_VSPS; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp, 5058c2ecf20Sopenharmony_ci unsigned int index, 5068c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *state) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnMR, 5098c2ecf20Sopenharmony_ci PnMR_SPIM_TP_OFF | state->format->pnmr); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDDCR4, 5128c2ecf20Sopenharmony_ci state->format->edf | PnDDCR4_CODE); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_format(struct rcar_du_group *rgrp, 5168c2ecf20Sopenharmony_ci unsigned int index, 5178c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *state) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct rcar_du_device *rcdu = rgrp->dev; 5208c2ecf20Sopenharmony_ci const struct drm_rect *dst = &state->state.dst; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (rcdu->info->gen < 3) 5238c2ecf20Sopenharmony_ci rcar_du_plane_setup_format_gen2(rgrp, index, state); 5248c2ecf20Sopenharmony_ci else 5258c2ecf20Sopenharmony_ci rcar_du_plane_setup_format_gen3(rgrp, index, state); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Destination position and size */ 5288c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst)); 5298c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst)); 5308c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1); 5318c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (rcdu->info->gen < 3) { 5348c2ecf20Sopenharmony_ci /* Wrap-around and blinking, disabled */ 5358c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnWASPR, 0); 5368c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); 5378c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnBTR, 0); 5388c2ecf20Sopenharmony_ci rcar_du_plane_write(rgrp, index, PnMLR, 0); 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_civoid __rcar_du_plane_setup(struct rcar_du_group *rgrp, 5438c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *state) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct rcar_du_device *rcdu = rgrp->dev; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci rcar_du_plane_setup_format(rgrp, state->hwindex, state); 5488c2ecf20Sopenharmony_ci if (state->format->planes == 2) 5498c2ecf20Sopenharmony_ci rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8, 5508c2ecf20Sopenharmony_ci state); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (rcdu->info->gen < 3) 5538c2ecf20Sopenharmony_ci rcar_du_plane_setup_scanout(rgrp, state); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (state->source == RCAR_DU_PLANE_VSPD1) { 5568c2ecf20Sopenharmony_ci unsigned int vspd1_sink = rgrp->index ? 2 : 0; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (rcdu->vspd1_sink != vspd1_sink) { 5598c2ecf20Sopenharmony_ci rcdu->vspd1_sink = vspd1_sink; 5608c2ecf20Sopenharmony_ci rcar_du_set_dpad0_vsp1_routing(rcdu); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ciint __rcar_du_plane_atomic_check(struct drm_plane *plane, 5668c2ecf20Sopenharmony_ci struct drm_plane_state *state, 5678c2ecf20Sopenharmony_ci const struct rcar_du_format_info **format) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct drm_device *dev = plane->dev; 5708c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 5718c2ecf20Sopenharmony_ci int ret; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (!state->crtc) { 5748c2ecf20Sopenharmony_ci /* 5758c2ecf20Sopenharmony_ci * The visible field is not reset by the DRM core but only 5768c2ecf20Sopenharmony_ci * updated by drm_plane_helper_check_state(), set it manually. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ci state->visible = false; 5798c2ecf20Sopenharmony_ci *format = NULL; 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); 5848c2ecf20Sopenharmony_ci if (IS_ERR(crtc_state)) 5858c2ecf20Sopenharmony_ci return PTR_ERR(crtc_state); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(state, crtc_state, 5888c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 5898c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 5908c2ecf20Sopenharmony_ci true, true); 5918c2ecf20Sopenharmony_ci if (ret < 0) 5928c2ecf20Sopenharmony_ci return ret; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (!state->visible) { 5958c2ecf20Sopenharmony_ci *format = NULL; 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci *format = rcar_du_format_info(state->fb->format->format); 6008c2ecf20Sopenharmony_ci if (*format == NULL) { 6018c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__, 6028c2ecf20Sopenharmony_ci state->fb->format->format); 6038c2ecf20Sopenharmony_ci return -EINVAL; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int rcar_du_plane_atomic_check(struct drm_plane *plane, 6108c2ecf20Sopenharmony_ci struct drm_plane_state *state) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct rcar_du_plane_state *rstate = to_rcar_plane_state(state); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return __rcar_du_plane_atomic_check(plane, state, &rstate->format); 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic void rcar_du_plane_atomic_update(struct drm_plane *plane, 6188c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct rcar_du_plane *rplane = to_rcar_plane(plane); 6218c2ecf20Sopenharmony_ci struct rcar_du_plane_state *old_rstate; 6228c2ecf20Sopenharmony_ci struct rcar_du_plane_state *new_rstate; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (!plane->state->visible) 6258c2ecf20Sopenharmony_ci return; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci rcar_du_plane_setup(rplane); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * Check whether the source has changed from memory to live source or 6318c2ecf20Sopenharmony_ci * from live source to memory. The source has been configured by the 6328c2ecf20Sopenharmony_ci * VSPS bit in the PnDDCR4 register. Although the datasheet states that 6338c2ecf20Sopenharmony_ci * the bit is updated during vertical blanking, it seems that updates 6348c2ecf20Sopenharmony_ci * only occur when the DU group is held in reset through the DSYSR.DRES 6358c2ecf20Sopenharmony_ci * bit. We thus need to restart the group if the source changes. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci old_rstate = to_rcar_plane_state(old_state); 6388c2ecf20Sopenharmony_ci new_rstate = to_rcar_plane_state(plane->state); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) != 6418c2ecf20Sopenharmony_ci (new_rstate->source == RCAR_DU_PLANE_MEMORY)) 6428c2ecf20Sopenharmony_ci rplane->group->need_restart = true; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { 6468c2ecf20Sopenharmony_ci .atomic_check = rcar_du_plane_atomic_check, 6478c2ecf20Sopenharmony_ci .atomic_update = rcar_du_plane_atomic_update, 6488c2ecf20Sopenharmony_ci}; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic struct drm_plane_state * 6518c2ecf20Sopenharmony_circar_du_plane_atomic_duplicate_state(struct drm_plane *plane) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct rcar_du_plane_state *state; 6548c2ecf20Sopenharmony_ci struct rcar_du_plane_state *copy; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (WARN_ON(!plane->state)) 6578c2ecf20Sopenharmony_ci return NULL; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci state = to_rcar_plane_state(plane->state); 6608c2ecf20Sopenharmony_ci copy = kmemdup(state, sizeof(*state), GFP_KERNEL); 6618c2ecf20Sopenharmony_ci if (copy == NULL) 6628c2ecf20Sopenharmony_ci return NULL; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_duplicate_state(plane, ©->state); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return ©->state; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane, 6708c2ecf20Sopenharmony_ci struct drm_plane_state *state) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_destroy_state(state); 6738c2ecf20Sopenharmony_ci kfree(to_rcar_plane_state(state)); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic void rcar_du_plane_reset(struct drm_plane *plane) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct rcar_du_plane_state *state; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (plane->state) { 6818c2ecf20Sopenharmony_ci rcar_du_plane_atomic_destroy_state(plane, plane->state); 6828c2ecf20Sopenharmony_ci plane->state = NULL; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 6868c2ecf20Sopenharmony_ci if (state == NULL) 6878c2ecf20Sopenharmony_ci return; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_reset(plane, &state->state); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci state->hwindex = -1; 6928c2ecf20Sopenharmony_ci state->source = RCAR_DU_PLANE_MEMORY; 6938c2ecf20Sopenharmony_ci state->colorkey = RCAR_DU_COLORKEY_NONE; 6948c2ecf20Sopenharmony_ci state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int rcar_du_plane_atomic_set_property(struct drm_plane *plane, 6988c2ecf20Sopenharmony_ci struct drm_plane_state *state, 6998c2ecf20Sopenharmony_ci struct drm_property *property, 7008c2ecf20Sopenharmony_ci uint64_t val) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci struct rcar_du_plane_state *rstate = to_rcar_plane_state(state); 7038c2ecf20Sopenharmony_ci struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (property == rcdu->props.colorkey) 7068c2ecf20Sopenharmony_ci rstate->colorkey = val; 7078c2ecf20Sopenharmony_ci else 7088c2ecf20Sopenharmony_ci return -EINVAL; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int rcar_du_plane_atomic_get_property(struct drm_plane *plane, 7148c2ecf20Sopenharmony_ci const struct drm_plane_state *state, struct drm_property *property, 7158c2ecf20Sopenharmony_ci uint64_t *val) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci const struct rcar_du_plane_state *rstate = 7188c2ecf20Sopenharmony_ci container_of(state, const struct rcar_du_plane_state, state); 7198c2ecf20Sopenharmony_ci struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (property == rcdu->props.colorkey) 7228c2ecf20Sopenharmony_ci *val = rstate->colorkey; 7238c2ecf20Sopenharmony_ci else 7248c2ecf20Sopenharmony_ci return -EINVAL; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs rcar_du_plane_funcs = { 7308c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 7318c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 7328c2ecf20Sopenharmony_ci .reset = rcar_du_plane_reset, 7338c2ecf20Sopenharmony_ci .destroy = drm_plane_cleanup, 7348c2ecf20Sopenharmony_ci .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state, 7358c2ecf20Sopenharmony_ci .atomic_destroy_state = rcar_du_plane_atomic_destroy_state, 7368c2ecf20Sopenharmony_ci .atomic_set_property = rcar_du_plane_atomic_set_property, 7378c2ecf20Sopenharmony_ci .atomic_get_property = rcar_du_plane_atomic_get_property, 7388c2ecf20Sopenharmony_ci}; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic const uint32_t formats[] = { 7418c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 7428c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB1555, 7438c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 7448c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 7458c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 7468c2ecf20Sopenharmony_ci DRM_FORMAT_UYVY, 7478c2ecf20Sopenharmony_ci DRM_FORMAT_YUYV, 7488c2ecf20Sopenharmony_ci DRM_FORMAT_NV12, 7498c2ecf20Sopenharmony_ci DRM_FORMAT_NV21, 7508c2ecf20Sopenharmony_ci DRM_FORMAT_NV16, 7518c2ecf20Sopenharmony_ci}; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ciint rcar_du_planes_init(struct rcar_du_group *rgrp) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct rcar_du_device *rcdu = rgrp->dev; 7568c2ecf20Sopenharmony_ci unsigned int crtcs; 7578c2ecf20Sopenharmony_ci unsigned int i; 7588c2ecf20Sopenharmony_ci int ret; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* 7618c2ecf20Sopenharmony_ci * Create one primary plane per CRTC in this group and seven overlay 7628c2ecf20Sopenharmony_ci * planes. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci rgrp->num_planes = rgrp->num_crtcs + 7; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci for (i = 0; i < rgrp->num_planes; ++i) { 7698c2ecf20Sopenharmony_ci enum drm_plane_type type = i < rgrp->num_crtcs 7708c2ecf20Sopenharmony_ci ? DRM_PLANE_TYPE_PRIMARY 7718c2ecf20Sopenharmony_ci : DRM_PLANE_TYPE_OVERLAY; 7728c2ecf20Sopenharmony_ci struct rcar_du_plane *plane = &rgrp->planes[i]; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci plane->group = rgrp; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs, 7778c2ecf20Sopenharmony_ci &rcar_du_plane_funcs, formats, 7788c2ecf20Sopenharmony_ci ARRAY_SIZE(formats), 7798c2ecf20Sopenharmony_ci NULL, type, NULL); 7808c2ecf20Sopenharmony_ci if (ret < 0) 7818c2ecf20Sopenharmony_ci return ret; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci drm_plane_helper_add(&plane->plane, 7848c2ecf20Sopenharmony_ci &rcar_du_plane_helper_funcs); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci drm_plane_create_alpha_property(&plane->plane); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (type == DRM_PLANE_TYPE_PRIMARY) { 7898c2ecf20Sopenharmony_ci drm_plane_create_zpos_immutable_property(&plane->plane, 7908c2ecf20Sopenharmony_ci 0); 7918c2ecf20Sopenharmony_ci } else { 7928c2ecf20Sopenharmony_ci drm_object_attach_property(&plane->plane.base, 7938c2ecf20Sopenharmony_ci rcdu->props.colorkey, 7948c2ecf20Sopenharmony_ci RCAR_DU_COLORKEY_NONE); 7958c2ecf20Sopenharmony_ci drm_plane_create_zpos_property(&plane->plane, 1, 1, 7); 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci} 801