18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 28c2ecf20Sopenharmony_ci/************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the 88c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 98c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 108c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 118c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 128c2ecf20Sopenharmony_ci * the following conditions: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the 158c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 168c2ecf20Sopenharmony_ci * of the Software. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 218c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 228c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 238c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 248c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci **************************************************************************/ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 318c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 328c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "vmwgfx_kms.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define vmw_crtc_to_ldu(x) \ 378c2ecf20Sopenharmony_ci container_of(x, struct vmw_legacy_display_unit, base.crtc) 388c2ecf20Sopenharmony_ci#define vmw_encoder_to_ldu(x) \ 398c2ecf20Sopenharmony_ci container_of(x, struct vmw_legacy_display_unit, base.encoder) 408c2ecf20Sopenharmony_ci#define vmw_connector_to_ldu(x) \ 418c2ecf20Sopenharmony_ci container_of(x, struct vmw_legacy_display_unit, base.connector) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct vmw_legacy_display { 448c2ecf20Sopenharmony_ci struct list_head active; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci unsigned num_active; 478c2ecf20Sopenharmony_ci unsigned last_num_active; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci struct vmw_framebuffer *fb; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * Display unit using the legacy register interface. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistruct vmw_legacy_display_unit { 568c2ecf20Sopenharmony_ci struct vmw_display_unit base; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci struct list_head active; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci list_del_init(&ldu->active); 648c2ecf20Sopenharmony_ci vmw_du_cleanup(&ldu->base); 658c2ecf20Sopenharmony_ci kfree(ldu); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Legacy Display Unit CRTC functions 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void vmw_ldu_crtc_destroy(struct drm_crtc *crtc) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci vmw_ldu_destroy(vmw_crtc_to_ldu(crtc)); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int vmw_ldu_commit_list(struct vmw_private *dev_priv) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct vmw_legacy_display *lds = dev_priv->ldu_priv; 818c2ecf20Sopenharmony_ci struct vmw_legacy_display_unit *entry; 828c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = NULL; 838c2ecf20Sopenharmony_ci struct drm_crtc *crtc = NULL; 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* If there is no display topology the host just assumes 878c2ecf20Sopenharmony_ci * that the guest will set the same layout as the host. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci if (!(dev_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)) { 908c2ecf20Sopenharmony_ci int w = 0, h = 0; 918c2ecf20Sopenharmony_ci list_for_each_entry(entry, &lds->active, active) { 928c2ecf20Sopenharmony_ci crtc = &entry->base.crtc; 938c2ecf20Sopenharmony_ci w = max(w, crtc->x + crtc->mode.hdisplay); 948c2ecf20Sopenharmony_ci h = max(h, crtc->y + crtc->mode.vdisplay); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (crtc == NULL) 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci fb = crtc->primary->state->fb; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0], 1028c2ecf20Sopenharmony_ci fb->format->cpp[0] * 8, 1038c2ecf20Sopenharmony_ci fb->format->depth); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!list_empty(&lds->active)) { 1078c2ecf20Sopenharmony_ci entry = list_entry(lds->active.next, typeof(*entry), active); 1088c2ecf20Sopenharmony_ci fb = entry->base.crtc.primary->state->fb; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0], 1118c2ecf20Sopenharmony_ci fb->format->cpp[0] * 8, fb->format->depth); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Make sure we always show something. */ 1158c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1168c2ecf20Sopenharmony_ci lds->num_active ? lds->num_active : 1); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci i = 0; 1198c2ecf20Sopenharmony_ci list_for_each_entry(entry, &lds->active, active) { 1208c2ecf20Sopenharmony_ci crtc = &entry->base.crtc; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i); 1238c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i); 1248c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, crtc->x); 1258c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, crtc->y); 1268c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, crtc->mode.hdisplay); 1278c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, crtc->mode.vdisplay); 1288c2ecf20Sopenharmony_ci vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci i++; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci BUG_ON(i != lds->num_active); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci lds->last_num_active = lds->num_active; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int vmw_ldu_del_active(struct vmw_private *vmw_priv, 1418c2ecf20Sopenharmony_ci struct vmw_legacy_display_unit *ldu) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct vmw_legacy_display *ld = vmw_priv->ldu_priv; 1448c2ecf20Sopenharmony_ci if (list_empty(&ldu->active)) 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Must init otherwise list_empty(&ldu->active) will not work. */ 1488c2ecf20Sopenharmony_ci list_del_init(&ldu->active); 1498c2ecf20Sopenharmony_ci if (--(ld->num_active) == 0) { 1508c2ecf20Sopenharmony_ci BUG_ON(!ld->fb); 1518c2ecf20Sopenharmony_ci if (ld->fb->unpin) 1528c2ecf20Sopenharmony_ci ld->fb->unpin(ld->fb); 1538c2ecf20Sopenharmony_ci ld->fb = NULL; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int vmw_ldu_add_active(struct vmw_private *vmw_priv, 1608c2ecf20Sopenharmony_ci struct vmw_legacy_display_unit *ldu, 1618c2ecf20Sopenharmony_ci struct vmw_framebuffer *vfb) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct vmw_legacy_display *ld = vmw_priv->ldu_priv; 1648c2ecf20Sopenharmony_ci struct vmw_legacy_display_unit *entry; 1658c2ecf20Sopenharmony_ci struct list_head *at; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci BUG_ON(!ld->num_active && ld->fb); 1688c2ecf20Sopenharmony_ci if (vfb != ld->fb) { 1698c2ecf20Sopenharmony_ci if (ld->fb && ld->fb->unpin) 1708c2ecf20Sopenharmony_ci ld->fb->unpin(ld->fb); 1718c2ecf20Sopenharmony_ci vmw_svga_enable(vmw_priv); 1728c2ecf20Sopenharmony_ci if (vfb->pin) 1738c2ecf20Sopenharmony_ci vfb->pin(vfb); 1748c2ecf20Sopenharmony_ci ld->fb = vfb; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!list_empty(&ldu->active)) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci at = &ld->active; 1818c2ecf20Sopenharmony_ci list_for_each_entry(entry, &ld->active, active) { 1828c2ecf20Sopenharmony_ci if (entry->base.unit > ldu->base.unit) 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci at = &entry->active; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci list_add(&ldu->active, at); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ld->num_active++; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * vmw_ldu_crtc_mode_set_nofb - Enable svga 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * @crtc: CRTC associated with the new screen 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * For LDU, just enable the svga 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistatic void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/** 2078c2ecf20Sopenharmony_ci * vmw_ldu_crtc_atomic_enable - Noop 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * @crtc: CRTC associated with the new screen 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * This is called after a mode set has been completed. Here's 2128c2ecf20Sopenharmony_ci * usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active 2138c2ecf20Sopenharmony_ci * but since for LDU the display plane is closely tied to the 2148c2ecf20Sopenharmony_ci * CRTC, it makes more sense to do those at plane update time. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic void vmw_ldu_crtc_atomic_enable(struct drm_crtc *crtc, 2178c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * vmw_ldu_crtc_atomic_disable - Turns off CRTC 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * @crtc: CRTC to be turned off 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistatic void vmw_ldu_crtc_atomic_disable(struct drm_crtc *crtc, 2278c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs vmw_legacy_crtc_funcs = { 2328c2ecf20Sopenharmony_ci .gamma_set = vmw_du_crtc_gamma_set, 2338c2ecf20Sopenharmony_ci .destroy = vmw_ldu_crtc_destroy, 2348c2ecf20Sopenharmony_ci .reset = vmw_du_crtc_reset, 2358c2ecf20Sopenharmony_ci .atomic_duplicate_state = vmw_du_crtc_duplicate_state, 2368c2ecf20Sopenharmony_ci .atomic_destroy_state = vmw_du_crtc_destroy_state, 2378c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 2388c2ecf20Sopenharmony_ci .get_vblank_counter = vmw_get_vblank_counter, 2398c2ecf20Sopenharmony_ci .enable_vblank = vmw_enable_vblank, 2408c2ecf20Sopenharmony_ci .disable_vblank = vmw_disable_vblank, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * Legacy Display Unit encoder functions 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void vmw_ldu_encoder_destroy(struct drm_encoder *encoder) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci vmw_ldu_destroy(vmw_encoder_to_ldu(encoder)); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs vmw_legacy_encoder_funcs = { 2548c2ecf20Sopenharmony_ci .destroy = vmw_ldu_encoder_destroy, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* 2588c2ecf20Sopenharmony_ci * Legacy Display Unit connector functions 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void vmw_ldu_connector_destroy(struct drm_connector *connector) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci vmw_ldu_destroy(vmw_connector_to_ldu(connector)); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs vmw_legacy_connector_funcs = { 2678c2ecf20Sopenharmony_ci .dpms = vmw_du_connector_dpms, 2688c2ecf20Sopenharmony_ci .detect = vmw_du_connector_detect, 2698c2ecf20Sopenharmony_ci .fill_modes = vmw_du_connector_fill_modes, 2708c2ecf20Sopenharmony_ci .destroy = vmw_ldu_connector_destroy, 2718c2ecf20Sopenharmony_ci .reset = vmw_du_connector_reset, 2728c2ecf20Sopenharmony_ci .atomic_duplicate_state = vmw_du_connector_duplicate_state, 2738c2ecf20Sopenharmony_ci .atomic_destroy_state = vmw_du_connector_destroy_state, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct 2778c2ecf20Sopenharmony_cidrm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* 2818c2ecf20Sopenharmony_ci * Legacy Display Plane Functions 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void 2858c2ecf20Sopenharmony_civmw_ldu_primary_plane_atomic_update(struct drm_plane *plane, 2868c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct vmw_private *dev_priv; 2898c2ecf20Sopenharmony_ci struct vmw_legacy_display_unit *ldu; 2908c2ecf20Sopenharmony_ci struct vmw_framebuffer *vfb; 2918c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 2928c2ecf20Sopenharmony_ci struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ldu = vmw_crtc_to_ldu(crtc); 2968c2ecf20Sopenharmony_ci dev_priv = vmw_priv(plane->dev); 2978c2ecf20Sopenharmony_ci fb = plane->state->fb; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (vfb) 3028c2ecf20Sopenharmony_ci vmw_ldu_add_active(dev_priv, ldu, vfb); 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci vmw_ldu_del_active(dev_priv, ldu); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci vmw_ldu_commit_list(dev_priv); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs vmw_ldu_plane_funcs = { 3118c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 3128c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 3138c2ecf20Sopenharmony_ci .destroy = vmw_du_primary_plane_destroy, 3148c2ecf20Sopenharmony_ci .reset = vmw_du_plane_reset, 3158c2ecf20Sopenharmony_ci .atomic_duplicate_state = vmw_du_plane_duplicate_state, 3168c2ecf20Sopenharmony_ci .atomic_destroy_state = vmw_du_plane_destroy_state, 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs vmw_ldu_cursor_funcs = { 3208c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 3218c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 3228c2ecf20Sopenharmony_ci .destroy = vmw_du_cursor_plane_destroy, 3238c2ecf20Sopenharmony_ci .reset = vmw_du_plane_reset, 3248c2ecf20Sopenharmony_ci .atomic_duplicate_state = vmw_du_plane_duplicate_state, 3258c2ecf20Sopenharmony_ci .atomic_destroy_state = vmw_du_plane_destroy_state, 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* 3298c2ecf20Sopenharmony_ci * Atomic Helpers 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic const struct 3328c2ecf20Sopenharmony_cidrm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = { 3338c2ecf20Sopenharmony_ci .atomic_check = vmw_du_cursor_plane_atomic_check, 3348c2ecf20Sopenharmony_ci .atomic_update = vmw_du_cursor_plane_atomic_update, 3358c2ecf20Sopenharmony_ci .prepare_fb = vmw_du_cursor_plane_prepare_fb, 3368c2ecf20Sopenharmony_ci .cleanup_fb = vmw_du_plane_cleanup_fb, 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic const struct 3408c2ecf20Sopenharmony_cidrm_plane_helper_funcs vmw_ldu_primary_plane_helper_funcs = { 3418c2ecf20Sopenharmony_ci .atomic_check = vmw_du_primary_plane_atomic_check, 3428c2ecf20Sopenharmony_ci .atomic_update = vmw_ldu_primary_plane_atomic_update, 3438c2ecf20Sopenharmony_ci}; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = { 3468c2ecf20Sopenharmony_ci .mode_set_nofb = vmw_ldu_crtc_mode_set_nofb, 3478c2ecf20Sopenharmony_ci .atomic_check = vmw_du_crtc_atomic_check, 3488c2ecf20Sopenharmony_ci .atomic_begin = vmw_du_crtc_atomic_begin, 3498c2ecf20Sopenharmony_ci .atomic_flush = vmw_du_crtc_atomic_flush, 3508c2ecf20Sopenharmony_ci .atomic_enable = vmw_ldu_crtc_atomic_enable, 3518c2ecf20Sopenharmony_ci .atomic_disable = vmw_ldu_crtc_atomic_disable, 3528c2ecf20Sopenharmony_ci}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct vmw_legacy_display_unit *ldu; 3588c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 3598c2ecf20Sopenharmony_ci struct drm_connector *connector; 3608c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 3618c2ecf20Sopenharmony_ci struct drm_plane *primary, *cursor; 3628c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 3638c2ecf20Sopenharmony_ci int ret; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ldu = kzalloc(sizeof(*ldu), GFP_KERNEL); 3668c2ecf20Sopenharmony_ci if (!ldu) 3678c2ecf20Sopenharmony_ci return -ENOMEM; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ldu->base.unit = unit; 3708c2ecf20Sopenharmony_ci crtc = &ldu->base.crtc; 3718c2ecf20Sopenharmony_ci encoder = &ldu->base.encoder; 3728c2ecf20Sopenharmony_ci connector = &ldu->base.connector; 3738c2ecf20Sopenharmony_ci primary = &ldu->base.primary; 3748c2ecf20Sopenharmony_ci cursor = &ldu->base.cursor; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ldu->active); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ldu->base.pref_active = (unit == 0); 3798c2ecf20Sopenharmony_ci ldu->base.pref_width = dev_priv->initial_width; 3808c2ecf20Sopenharmony_ci ldu->base.pref_height = dev_priv->initial_height; 3818c2ecf20Sopenharmony_ci ldu->base.pref_mode = NULL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * Remove this after enabling atomic because property values can 3858c2ecf20Sopenharmony_ci * only exist in a state object 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci ldu->base.is_implicit = true; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Initialize primary plane */ 3908c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(dev, &ldu->base.primary, 3918c2ecf20Sopenharmony_ci 0, &vmw_ldu_plane_funcs, 3928c2ecf20Sopenharmony_ci vmw_primary_plane_formats, 3938c2ecf20Sopenharmony_ci ARRAY_SIZE(vmw_primary_plane_formats), 3948c2ecf20Sopenharmony_ci NULL, DRM_PLANE_TYPE_PRIMARY, NULL); 3958c2ecf20Sopenharmony_ci if (ret) { 3968c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize primary plane"); 3978c2ecf20Sopenharmony_ci goto err_free; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci drm_plane_helper_add(primary, &vmw_ldu_primary_plane_helper_funcs); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Initialize cursor plane */ 4038c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(dev, &ldu->base.cursor, 4048c2ecf20Sopenharmony_ci 0, &vmw_ldu_cursor_funcs, 4058c2ecf20Sopenharmony_ci vmw_cursor_plane_formats, 4068c2ecf20Sopenharmony_ci ARRAY_SIZE(vmw_cursor_plane_formats), 4078c2ecf20Sopenharmony_ci NULL, DRM_PLANE_TYPE_CURSOR, NULL); 4088c2ecf20Sopenharmony_ci if (ret) { 4098c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize cursor plane"); 4108c2ecf20Sopenharmony_ci drm_plane_cleanup(&ldu->base.primary); 4118c2ecf20Sopenharmony_ci goto err_free; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, 4178c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_VIRTUAL); 4188c2ecf20Sopenharmony_ci if (ret) { 4198c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize connector\n"); 4208c2ecf20Sopenharmony_ci goto err_free; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &vmw_ldu_connector_helper_funcs); 4248c2ecf20Sopenharmony_ci connector->status = vmw_du_connector_detect(connector, true); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ret = drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, 4278c2ecf20Sopenharmony_ci DRM_MODE_ENCODER_VIRTUAL, NULL); 4288c2ecf20Sopenharmony_ci if (ret) { 4298c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize encoder\n"); 4308c2ecf20Sopenharmony_ci goto err_free_connector; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci (void) drm_connector_attach_encoder(connector, encoder); 4348c2ecf20Sopenharmony_ci encoder->possible_crtcs = (1 << unit); 4358c2ecf20Sopenharmony_ci encoder->possible_clones = 0; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ret = drm_connector_register(connector); 4388c2ecf20Sopenharmony_ci if (ret) { 4398c2ecf20Sopenharmony_ci DRM_ERROR("Failed to register connector\n"); 4408c2ecf20Sopenharmony_ci goto err_free_encoder; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ret = drm_crtc_init_with_planes(dev, crtc, &ldu->base.primary, 4448c2ecf20Sopenharmony_ci &ldu->base.cursor, 4458c2ecf20Sopenharmony_ci &vmw_legacy_crtc_funcs, NULL); 4468c2ecf20Sopenharmony_ci if (ret) { 4478c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize CRTC\n"); 4488c2ecf20Sopenharmony_ci goto err_free_unregister; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci drm_crtc_helper_add(crtc, &vmw_ldu_crtc_helper_funcs); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci drm_mode_crtc_set_gamma_size(crtc, 256); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 4568c2ecf20Sopenharmony_ci dev_priv->hotplug_mode_update_property, 1); 4578c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 4588c2ecf20Sopenharmony_ci dev->mode_config.suggested_x_property, 0); 4598c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 4608c2ecf20Sopenharmony_ci dev->mode_config.suggested_y_property, 0); 4618c2ecf20Sopenharmony_ci if (dev_priv->implicit_placement_property) 4628c2ecf20Sopenharmony_ci drm_object_attach_property 4638c2ecf20Sopenharmony_ci (&connector->base, 4648c2ecf20Sopenharmony_ci dev_priv->implicit_placement_property, 4658c2ecf20Sopenharmony_ci 1); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cierr_free_unregister: 4708c2ecf20Sopenharmony_ci drm_connector_unregister(connector); 4718c2ecf20Sopenharmony_cierr_free_encoder: 4728c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 4738c2ecf20Sopenharmony_cierr_free_connector: 4748c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 4758c2ecf20Sopenharmony_cierr_free: 4768c2ecf20Sopenharmony_ci kfree(ldu); 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciint vmw_kms_ldu_init_display(struct vmw_private *dev_priv) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct drm_device *dev = dev_priv->dev; 4838c2ecf20Sopenharmony_ci int i, ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (dev_priv->ldu_priv) { 4868c2ecf20Sopenharmony_ci DRM_INFO("ldu system already on\n"); 4878c2ecf20Sopenharmony_ci return -EINVAL; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL); 4918c2ecf20Sopenharmony_ci if (!dev_priv->ldu_priv) 4928c2ecf20Sopenharmony_ci return -ENOMEM; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev_priv->ldu_priv->active); 4958c2ecf20Sopenharmony_ci dev_priv->ldu_priv->num_active = 0; 4968c2ecf20Sopenharmony_ci dev_priv->ldu_priv->last_num_active = 0; 4978c2ecf20Sopenharmony_ci dev_priv->ldu_priv->fb = NULL; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* for old hardware without multimon only enable one display */ 5008c2ecf20Sopenharmony_ci if (dev_priv->capabilities & SVGA_CAP_MULTIMON) 5018c2ecf20Sopenharmony_ci ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); 5028c2ecf20Sopenharmony_ci else 5038c2ecf20Sopenharmony_ci ret = drm_vblank_init(dev, 1); 5048c2ecf20Sopenharmony_ci if (ret != 0) 5058c2ecf20Sopenharmony_ci goto err_free; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci vmw_kms_create_implicit_placement_property(dev_priv); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (dev_priv->capabilities & SVGA_CAP_MULTIMON) 5108c2ecf20Sopenharmony_ci for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) 5118c2ecf20Sopenharmony_ci vmw_ldu_init(dev_priv, i); 5128c2ecf20Sopenharmony_ci else 5138c2ecf20Sopenharmony_ci vmw_ldu_init(dev_priv, 0); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci dev_priv->active_display_unit = vmw_du_legacy; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci drm_mode_config_reset(dev); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci DRM_INFO("Legacy Display Unit initialized\n"); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cierr_free: 5248c2ecf20Sopenharmony_ci kfree(dev_priv->ldu_priv); 5258c2ecf20Sopenharmony_ci dev_priv->ldu_priv = NULL; 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ciint vmw_kms_ldu_close_display(struct vmw_private *dev_priv) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci if (!dev_priv->ldu_priv) 5328c2ecf20Sopenharmony_ci return -ENOSYS; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&dev_priv->ldu_priv->active)); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci kfree(dev_priv->ldu_priv); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciint vmw_kms_ldu_do_bo_dirty(struct vmw_private *dev_priv, 5438c2ecf20Sopenharmony_ci struct vmw_framebuffer *framebuffer, 5448c2ecf20Sopenharmony_ci unsigned int flags, unsigned int color, 5458c2ecf20Sopenharmony_ci struct drm_clip_rect *clips, 5468c2ecf20Sopenharmony_ci unsigned int num_clips, int increment) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci size_t fifo_size; 5498c2ecf20Sopenharmony_ci int i; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci struct { 5528c2ecf20Sopenharmony_ci uint32_t header; 5538c2ecf20Sopenharmony_ci SVGAFifoCmdUpdate body; 5548c2ecf20Sopenharmony_ci } *cmd; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci fifo_size = sizeof(*cmd) * num_clips; 5578c2ecf20Sopenharmony_ci cmd = VMW_FIFO_RESERVE(dev_priv, fifo_size); 5588c2ecf20Sopenharmony_ci if (unlikely(cmd == NULL)) 5598c2ecf20Sopenharmony_ci return -ENOMEM; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci memset(cmd, 0, fifo_size); 5628c2ecf20Sopenharmony_ci for (i = 0; i < num_clips; i++, clips += increment) { 5638c2ecf20Sopenharmony_ci cmd[i].header = SVGA_CMD_UPDATE; 5648c2ecf20Sopenharmony_ci cmd[i].body.x = clips->x1; 5658c2ecf20Sopenharmony_ci cmd[i].body.y = clips->y1; 5668c2ecf20Sopenharmony_ci cmd[i].body.width = clips->x2 - clips->x1; 5678c2ecf20Sopenharmony_ci cmd[i].body.height = clips->y2 - clips->y1; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci vmw_fifo_commit(dev_priv, fifo_size); 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci} 573