162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ 462306a36Sopenharmony_ci * Author: Rob Clark <rob.clark@linaro.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <drm/drm_atomic.h> 862306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 962306a36Sopenharmony_ci#include <drm/drm_blend.h> 1062306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 1162306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 1262306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "omap_dmm_tiler.h" 1562306a36Sopenharmony_ci#include "omap_drv.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * plane funcs 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct omap_plane_state { 2462306a36Sopenharmony_ci /* Must be first. */ 2562306a36Sopenharmony_ci struct drm_plane_state base; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci struct omap_hw_overlay *overlay; 2862306a36Sopenharmony_ci struct omap_hw_overlay *r_overlay; /* right overlay */ 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define to_omap_plane(x) container_of(x, struct omap_plane, base) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct omap_plane { 3462306a36Sopenharmony_ci struct drm_plane base; 3562306a36Sopenharmony_ci enum omap_plane_id id; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cibool is_omap_plane_dual_overlay(struct drm_plane_state *state) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct omap_plane_state *omap_state = to_omap_plane_state(state); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return !!omap_state->r_overlay; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int omap_plane_prepare_fb(struct drm_plane *plane, 4662306a36Sopenharmony_ci struct drm_plane_state *new_state) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci if (!new_state->fb) 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci drm_gem_plane_helper_prepare_fb(plane, new_state); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return omap_framebuffer_pin(new_state->fb); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void omap_plane_cleanup_fb(struct drm_plane *plane, 5762306a36Sopenharmony_ci struct drm_plane_state *old_state) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (old_state->fb) 6062306a36Sopenharmony_ci omap_framebuffer_unpin(old_state->fb); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void omap_plane_atomic_update(struct drm_plane *plane, 6462306a36Sopenharmony_ci struct drm_atomic_state *state) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct omap_drm_private *priv = plane->dev->dev_private; 6762306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 6862306a36Sopenharmony_ci plane); 6962306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 7062306a36Sopenharmony_ci plane); 7162306a36Sopenharmony_ci struct omap_plane_state *new_omap_state; 7262306a36Sopenharmony_ci struct omap_plane_state *old_omap_state; 7362306a36Sopenharmony_ci struct omap_overlay_info info, r_info; 7462306a36Sopenharmony_ci enum omap_plane_id ovl_id, r_ovl_id; 7562306a36Sopenharmony_ci int ret; 7662306a36Sopenharmony_ci bool dual_ovl; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci new_omap_state = to_omap_plane_state(new_state); 7962306a36Sopenharmony_ci old_omap_state = to_omap_plane_state(old_state); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci dual_ovl = is_omap_plane_dual_overlay(new_state); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Cleanup previously held overlay if needed */ 8462306a36Sopenharmony_ci if (old_omap_state->overlay) 8562306a36Sopenharmony_ci omap_overlay_update_state(priv, old_omap_state->overlay); 8662306a36Sopenharmony_ci if (old_omap_state->r_overlay) 8762306a36Sopenharmony_ci omap_overlay_update_state(priv, old_omap_state->r_overlay); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!new_omap_state->overlay) { 9062306a36Sopenharmony_ci DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name); 9162306a36Sopenharmony_ci return; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ovl_id = new_omap_state->overlay->id; 9562306a36Sopenharmony_ci DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc, 9662306a36Sopenharmony_ci new_state->fb); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 9962306a36Sopenharmony_ci info.rotation_type = OMAP_DSS_ROT_NONE; 10062306a36Sopenharmony_ci info.rotation = DRM_MODE_ROTATE_0; 10162306a36Sopenharmony_ci info.global_alpha = new_state->alpha >> 8; 10262306a36Sopenharmony_ci info.zorder = new_state->normalized_zpos; 10362306a36Sopenharmony_ci if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) 10462306a36Sopenharmony_ci info.pre_mult_alpha = 1; 10562306a36Sopenharmony_ci else 10662306a36Sopenharmony_ci info.pre_mult_alpha = 0; 10762306a36Sopenharmony_ci info.color_encoding = new_state->color_encoding; 10862306a36Sopenharmony_ci info.color_range = new_state->color_range; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci r_info = info; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* update scanout: */ 11362306a36Sopenharmony_ci omap_framebuffer_update_scanout(new_state->fb, new_state, &info, 11462306a36Sopenharmony_ci dual_ovl ? &r_info : NULL); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci DBG("%s: %dx%d -> %dx%d (%d)", 11762306a36Sopenharmony_ci new_omap_state->overlay->name, info.width, info.height, 11862306a36Sopenharmony_ci info.out_width, info.out_height, info.screen_width); 11962306a36Sopenharmony_ci DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, 12062306a36Sopenharmony_ci &info.paddr, &info.p_uv_addr); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (dual_ovl) { 12362306a36Sopenharmony_ci r_ovl_id = new_omap_state->r_overlay->id; 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * If the current plane uses 2 hw planes the very next 12662306a36Sopenharmony_ci * zorder is used by the r_overlay so we just use the 12762306a36Sopenharmony_ci * main overlay zorder + 1 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci r_info.zorder = info.zorder + 1; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci DBG("%s: %dx%d -> %dx%d (%d)", 13262306a36Sopenharmony_ci new_omap_state->r_overlay->name, 13362306a36Sopenharmony_ci r_info.width, r_info.height, 13462306a36Sopenharmony_ci r_info.out_width, r_info.out_height, r_info.screen_width); 13562306a36Sopenharmony_ci DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y, 13662306a36Sopenharmony_ci &r_info.paddr, &r_info.p_uv_addr); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* and finally, update omapdss: */ 14062306a36Sopenharmony_ci ret = dispc_ovl_setup(priv->dispc, ovl_id, &info, 14162306a36Sopenharmony_ci omap_crtc_timings(new_state->crtc), false, 14262306a36Sopenharmony_ci omap_crtc_channel(new_state->crtc)); 14362306a36Sopenharmony_ci if (ret) { 14462306a36Sopenharmony_ci dev_err(plane->dev->dev, "Failed to setup plane %s\n", 14562306a36Sopenharmony_ci plane->name); 14662306a36Sopenharmony_ci dispc_ovl_enable(priv->dispc, ovl_id, false); 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci dispc_ovl_enable(priv->dispc, ovl_id, true); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (dual_ovl) { 15362306a36Sopenharmony_ci ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_info, 15462306a36Sopenharmony_ci omap_crtc_timings(new_state->crtc), false, 15562306a36Sopenharmony_ci omap_crtc_channel(new_state->crtc)); 15662306a36Sopenharmony_ci if (ret) { 15762306a36Sopenharmony_ci dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n", 15862306a36Sopenharmony_ci plane->name); 15962306a36Sopenharmony_ci dispc_ovl_enable(priv->dispc, r_ovl_id, false); 16062306a36Sopenharmony_ci dispc_ovl_enable(priv->dispc, ovl_id, false); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci dispc_ovl_enable(priv->dispc, r_ovl_id, true); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void omap_plane_atomic_disable(struct drm_plane *plane, 16962306a36Sopenharmony_ci struct drm_atomic_state *state) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct omap_drm_private *priv = plane->dev->dev_private; 17262306a36Sopenharmony_ci struct omap_plane *omap_plane = to_omap_plane(plane); 17362306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 17462306a36Sopenharmony_ci plane); 17562306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 17662306a36Sopenharmony_ci plane); 17762306a36Sopenharmony_ci struct omap_plane_state *new_omap_state; 17862306a36Sopenharmony_ci struct omap_plane_state *old_omap_state; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci new_omap_state = to_omap_plane_state(new_state); 18162306a36Sopenharmony_ci old_omap_state = to_omap_plane_state(old_state); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!old_omap_state->overlay) 18462306a36Sopenharmony_ci return; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci new_state->rotation = DRM_MODE_ROTATE_0; 18762306a36Sopenharmony_ci new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci omap_overlay_update_state(priv, old_omap_state->overlay); 19062306a36Sopenharmony_ci new_omap_state->overlay = NULL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (is_omap_plane_dual_overlay(old_state)) { 19362306a36Sopenharmony_ci omap_overlay_update_state(priv, old_omap_state->r_overlay); 19462306a36Sopenharmony_ci new_omap_state->r_overlay = NULL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int omap_plane_atomic_check(struct drm_plane *plane, 20162306a36Sopenharmony_ci struct drm_atomic_state *state) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 20462306a36Sopenharmony_ci plane); 20562306a36Sopenharmony_ci struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, 20662306a36Sopenharmony_ci plane); 20762306a36Sopenharmony_ci struct omap_drm_private *priv = plane->dev->dev_private; 20862306a36Sopenharmony_ci struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state); 20962306a36Sopenharmony_ci struct omap_global_state *omap_overlay_global_state; 21062306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 21162306a36Sopenharmony_ci bool new_r_hw_overlay = false; 21262306a36Sopenharmony_ci bool new_hw_overlay = false; 21362306a36Sopenharmony_ci u32 max_width, max_height; 21462306a36Sopenharmony_ci struct drm_crtc *crtc; 21562306a36Sopenharmony_ci u16 width, height; 21662306a36Sopenharmony_ci u32 caps = 0; 21762306a36Sopenharmony_ci u32 fourcc; 21862306a36Sopenharmony_ci int ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci omap_overlay_global_state = omap_get_global_state(state); 22162306a36Sopenharmony_ci if (IS_ERR(omap_overlay_global_state)) 22262306a36Sopenharmony_ci return PTR_ERR(omap_overlay_global_state); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci dispc_ovl_get_max_size(priv->dispc, &width, &height); 22562306a36Sopenharmony_ci max_width = width << 16; 22662306a36Sopenharmony_ci max_height = height << 16; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc; 22962306a36Sopenharmony_ci if (!crtc) 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); 23362306a36Sopenharmony_ci /* we should have a crtc state if the plane is attached to a crtc */ 23462306a36Sopenharmony_ci if (WARN_ON(!crtc_state)) 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * Note: these are just sanity checks to filter out totally bad scaling 23962306a36Sopenharmony_ci * factors. The real limits must be calculated case by case, and 24062306a36Sopenharmony_ci * unfortunately we currently do those checks only at the commit 24162306a36Sopenharmony_ci * phase in dispc. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 24462306a36Sopenharmony_ci FRAC_16_16(1, 8), FRAC_16_16(8, 1), 24562306a36Sopenharmony_ci true, true); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci DBG("%s: visible %d -> %d", plane->name, 25062306a36Sopenharmony_ci old_plane_state->visible, new_plane_state->visible); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!new_plane_state->visible) { 25362306a36Sopenharmony_ci omap_overlay_release(state, omap_state->overlay); 25462306a36Sopenharmony_ci omap_overlay_release(state, omap_state->r_overlay); 25562306a36Sopenharmony_ci omap_state->overlay = NULL; 25662306a36Sopenharmony_ci omap_state->r_overlay = NULL; 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay) 26462306a36Sopenharmony_ci return -EINVAL; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay) 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Make sure dimensions are within bounds. */ 27062306a36Sopenharmony_ci if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height) 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) { 27562306a36Sopenharmony_ci bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) || 27862306a36Sopenharmony_ci new_plane_state->crtc_w / 2 & 1)) { 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * When calculating the split overlay width 28162306a36Sopenharmony_ci * and it yield an odd value we will need to adjust 28262306a36Sopenharmony_ci * the indivual width +/- 1. So make sure it fits 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if (new_plane_state->src_w <= ((2 * width - 1) << 16) && 28562306a36Sopenharmony_ci new_plane_state->crtc_w <= (2 * width - 1)) 28662306a36Sopenharmony_ci new_r_hw_overlay = true; 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci return -EINVAL; 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci if (new_plane_state->src_w <= (2 * max_width) && 29162306a36Sopenharmony_ci new_plane_state->crtc_w <= (2 * width)) 29262306a36Sopenharmony_ci new_r_hw_overlay = true; 29362306a36Sopenharmony_ci else 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (new_plane_state->rotation != DRM_MODE_ROTATE_0 && 29962306a36Sopenharmony_ci !omap_framebuffer_supports_rotation(new_plane_state->fb)) 30062306a36Sopenharmony_ci return -EINVAL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w || 30362306a36Sopenharmony_ci (new_plane_state->src_h >> 16) != new_plane_state->crtc_h) 30462306a36Sopenharmony_ci caps |= OMAP_DSS_OVL_CAP_SCALE; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci fourcc = new_plane_state->fb->format->format; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * (re)allocate hw overlay if we don't have one or 31062306a36Sopenharmony_ci * there is a caps mismatch 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) { 31362306a36Sopenharmony_ci new_hw_overlay = true; 31462306a36Sopenharmony_ci } else { 31562306a36Sopenharmony_ci /* check supported format */ 31662306a36Sopenharmony_ci if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id, 31762306a36Sopenharmony_ci fourcc)) 31862306a36Sopenharmony_ci new_hw_overlay = true; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * check if we need two overlays and only have 1 or 32362306a36Sopenharmony_ci * if we had 2 overlays but will only need 1 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci if ((new_r_hw_overlay && !omap_state->r_overlay) || 32662306a36Sopenharmony_ci (!new_r_hw_overlay && omap_state->r_overlay)) 32762306a36Sopenharmony_ci new_hw_overlay = true; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (new_hw_overlay) { 33062306a36Sopenharmony_ci struct omap_hw_overlay *old_ovl = omap_state->overlay; 33162306a36Sopenharmony_ci struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay; 33262306a36Sopenharmony_ci struct omap_hw_overlay *new_ovl = NULL; 33362306a36Sopenharmony_ci struct omap_hw_overlay *new_r_ovl = NULL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci omap_overlay_release(state, old_ovl); 33662306a36Sopenharmony_ci omap_overlay_release(state, old_r_ovl); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl, 33962306a36Sopenharmony_ci new_r_hw_overlay ? &new_r_ovl : NULL); 34062306a36Sopenharmony_ci if (ret) { 34162306a36Sopenharmony_ci DBG("%s: failed to assign hw_overlay", plane->name); 34262306a36Sopenharmony_ci omap_state->overlay = NULL; 34362306a36Sopenharmony_ci omap_state->r_overlay = NULL; 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci omap_state->overlay = new_ovl; 34862306a36Sopenharmony_ci if (new_r_hw_overlay) 34962306a36Sopenharmony_ci omap_state->r_overlay = new_r_ovl; 35062306a36Sopenharmony_ci else 35162306a36Sopenharmony_ci omap_state->r_overlay = NULL; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (omap_state->r_overlay) 35762306a36Sopenharmony_ci DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs omap_plane_helper_funcs = { 36362306a36Sopenharmony_ci .prepare_fb = omap_plane_prepare_fb, 36462306a36Sopenharmony_ci .cleanup_fb = omap_plane_cleanup_fb, 36562306a36Sopenharmony_ci .atomic_check = omap_plane_atomic_check, 36662306a36Sopenharmony_ci .atomic_update = omap_plane_atomic_update, 36762306a36Sopenharmony_ci .atomic_disable = omap_plane_atomic_disable, 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void omap_plane_destroy(struct drm_plane *plane) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct omap_plane *omap_plane = to_omap_plane(plane); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci DBG("%s", plane->name); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci drm_plane_cleanup(plane); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci kfree(omap_plane); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* helper to install properties which are common to planes and crtcs */ 38262306a36Sopenharmony_civoid omap_plane_install_properties(struct drm_plane *plane, 38362306a36Sopenharmony_ci struct drm_mode_object *obj) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct drm_device *dev = plane->dev; 38662306a36Sopenharmony_ci struct omap_drm_private *priv = dev->dev_private; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (priv->has_dmm) { 38962306a36Sopenharmony_ci if (!plane->rotation_property) 39062306a36Sopenharmony_ci drm_plane_create_rotation_property(plane, 39162306a36Sopenharmony_ci DRM_MODE_ROTATE_0, 39262306a36Sopenharmony_ci DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 | 39362306a36Sopenharmony_ci DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 | 39462306a36Sopenharmony_ci DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* Attach the rotation property also to the crtc object */ 39762306a36Sopenharmony_ci if (plane->rotation_property && obj != &plane->base) 39862306a36Sopenharmony_ci drm_object_attach_property(obj, plane->rotation_property, 39962306a36Sopenharmony_ci DRM_MODE_ROTATE_0); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci drm_object_attach_property(obj, priv->zorder_prop, 0); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void omap_plane_reset(struct drm_plane *plane) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct omap_plane_state *omap_state; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (plane->state) 41062306a36Sopenharmony_ci drm_atomic_helper_plane_destroy_state(plane, plane->state); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); 41362306a36Sopenharmony_ci if (!omap_state) 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci __drm_atomic_helper_plane_reset(plane, &omap_state->base); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic struct drm_plane_state * 42062306a36Sopenharmony_ciomap_plane_atomic_duplicate_state(struct drm_plane *plane) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct omap_plane_state *state, *current_state; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (WARN_ON(!plane->state)) 42562306a36Sopenharmony_ci return NULL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci current_state = to_omap_plane_state(plane->state); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci state = kmalloc(sizeof(*state), GFP_KERNEL); 43062306a36Sopenharmony_ci if (!state) 43162306a36Sopenharmony_ci return NULL; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci __drm_atomic_helper_plane_duplicate_state(plane, &state->base); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci state->overlay = current_state->overlay; 43662306a36Sopenharmony_ci state->r_overlay = current_state->r_overlay; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return &state->base; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic void omap_plane_atomic_print_state(struct drm_printer *p, 44262306a36Sopenharmony_ci const struct drm_plane_state *state) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct omap_plane_state *omap_state = to_omap_plane_state(state); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (omap_state->overlay) 44762306a36Sopenharmony_ci drm_printf(p, "\toverlay=%s (caps=0x%x)\n", 44862306a36Sopenharmony_ci omap_state->overlay->name, 44962306a36Sopenharmony_ci omap_state->overlay->caps); 45062306a36Sopenharmony_ci else 45162306a36Sopenharmony_ci drm_printf(p, "\toverlay=None\n"); 45262306a36Sopenharmony_ci if (omap_state->r_overlay) 45362306a36Sopenharmony_ci drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n", 45462306a36Sopenharmony_ci omap_state->r_overlay->name, 45562306a36Sopenharmony_ci omap_state->r_overlay->caps); 45662306a36Sopenharmony_ci else 45762306a36Sopenharmony_ci drm_printf(p, "\tr_overlay=None\n"); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int omap_plane_atomic_set_property(struct drm_plane *plane, 46162306a36Sopenharmony_ci struct drm_plane_state *state, 46262306a36Sopenharmony_ci struct drm_property *property, 46362306a36Sopenharmony_ci u64 val) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct omap_drm_private *priv = plane->dev->dev_private; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (property == priv->zorder_prop) 46862306a36Sopenharmony_ci state->zpos = val; 46962306a36Sopenharmony_ci else 47062306a36Sopenharmony_ci return -EINVAL; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int omap_plane_atomic_get_property(struct drm_plane *plane, 47662306a36Sopenharmony_ci const struct drm_plane_state *state, 47762306a36Sopenharmony_ci struct drm_property *property, 47862306a36Sopenharmony_ci u64 *val) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct omap_drm_private *priv = plane->dev->dev_private; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (property == priv->zorder_prop) 48362306a36Sopenharmony_ci *val = state->zpos; 48462306a36Sopenharmony_ci else 48562306a36Sopenharmony_ci return -EINVAL; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic const struct drm_plane_funcs omap_plane_funcs = { 49162306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 49262306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 49362306a36Sopenharmony_ci .reset = omap_plane_reset, 49462306a36Sopenharmony_ci .destroy = omap_plane_destroy, 49562306a36Sopenharmony_ci .atomic_duplicate_state = omap_plane_atomic_duplicate_state, 49662306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 49762306a36Sopenharmony_ci .atomic_set_property = omap_plane_atomic_set_property, 49862306a36Sopenharmony_ci .atomic_get_property = omap_plane_atomic_get_property, 49962306a36Sopenharmony_ci .atomic_print_state = omap_plane_atomic_print_state, 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic bool omap_plane_supports_yuv(struct drm_plane *plane) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct omap_drm_private *priv = plane->dev->dev_private; 50562306a36Sopenharmony_ci struct omap_plane *omap_plane = to_omap_plane(plane); 50662306a36Sopenharmony_ci const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 50762306a36Sopenharmony_ci u32 i; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci for (i = 0; formats[i]; i++) 51062306a36Sopenharmony_ci if (formats[i] == DRM_FORMAT_YUYV || 51162306a36Sopenharmony_ci formats[i] == DRM_FORMAT_UYVY || 51262306a36Sopenharmony_ci formats[i] == DRM_FORMAT_NV12) 51362306a36Sopenharmony_ci return true; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return false; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* initialize plane */ 51962306a36Sopenharmony_cistruct drm_plane *omap_plane_init(struct drm_device *dev, 52062306a36Sopenharmony_ci int idx, enum drm_plane_type type, 52162306a36Sopenharmony_ci u32 possible_crtcs) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct omap_drm_private *priv = dev->dev_private; 52462306a36Sopenharmony_ci unsigned int num_planes = dispc_get_num_ovls(priv->dispc); 52562306a36Sopenharmony_ci struct drm_plane *plane; 52662306a36Sopenharmony_ci struct omap_plane *omap_plane; 52762306a36Sopenharmony_ci unsigned int zpos; 52862306a36Sopenharmony_ci int ret; 52962306a36Sopenharmony_ci u32 nformats; 53062306a36Sopenharmony_ci const u32 *formats; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (WARN_ON(idx >= num_planes)) 53362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); 53662306a36Sopenharmony_ci if (!omap_plane) 53762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci omap_plane->id = idx; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci DBG("%d: type=%d", omap_plane->id, type); 54262306a36Sopenharmony_ci DBG(" crtc_mask: 0x%04x", possible_crtcs); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 54562306a36Sopenharmony_ci for (nformats = 0; formats[nformats]; ++nformats) 54662306a36Sopenharmony_ci ; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci plane = &omap_plane->base; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci ret = drm_universal_plane_init(dev, plane, possible_crtcs, 55162306a36Sopenharmony_ci &omap_plane_funcs, formats, 55262306a36Sopenharmony_ci nformats, NULL, type, NULL); 55362306a36Sopenharmony_ci if (ret < 0) 55462306a36Sopenharmony_ci goto error; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci drm_plane_helper_add(plane, &omap_plane_helper_funcs); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci omap_plane_install_properties(plane, &plane->base); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * Set the zpos default depending on whether we are a primary or overlay 56262306a36Sopenharmony_ci * plane. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci if (plane->type == DRM_PLANE_TYPE_PRIMARY) 56562306a36Sopenharmony_ci zpos = 0; 56662306a36Sopenharmony_ci else 56762306a36Sopenharmony_ci zpos = omap_plane->id; 56862306a36Sopenharmony_ci drm_plane_create_zpos_property(plane, zpos, 0, num_planes - 1); 56962306a36Sopenharmony_ci drm_plane_create_alpha_property(plane); 57062306a36Sopenharmony_ci drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) | 57162306a36Sopenharmony_ci BIT(DRM_MODE_BLEND_COVERAGE)); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (omap_plane_supports_yuv(plane)) 57462306a36Sopenharmony_ci drm_plane_create_color_properties(plane, 57562306a36Sopenharmony_ci BIT(DRM_COLOR_YCBCR_BT601) | 57662306a36Sopenharmony_ci BIT(DRM_COLOR_YCBCR_BT709), 57762306a36Sopenharmony_ci BIT(DRM_COLOR_YCBCR_FULL_RANGE) | 57862306a36Sopenharmony_ci BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), 57962306a36Sopenharmony_ci DRM_COLOR_YCBCR_BT601, 58062306a36Sopenharmony_ci DRM_COLOR_YCBCR_FULL_RANGE); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return plane; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cierror: 58562306a36Sopenharmony_ci dev_err(dev->dev, "%s(): could not create plane: %d\n", 58662306a36Sopenharmony_ci __func__, omap_plane->id); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci kfree(omap_plane); 58962306a36Sopenharmony_ci return NULL; 59062306a36Sopenharmony_ci} 591