18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013-2017 Oracle Corporation 48c2ecf20Sopenharmony_ci * This file is based on ast_main.c 58c2ecf20Sopenharmony_ci * Copyright 2012 Red Hat Inc. 68c2ecf20Sopenharmony_ci * Authors: Dave Airlie <airlied@redhat.com>, 78c2ecf20Sopenharmony_ci * Michael Thayer <michael.thayer@oracle.com, 88c2ecf20Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/vbox_err.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_damage_helper.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "vbox_drv.h" 178c2ecf20Sopenharmony_ci#include "vboxvideo_guest.h" 188c2ecf20Sopenharmony_ci#include "vboxvideo_vbe.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_civoid vbox_report_caps(struct vbox_private *vbox) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION | 238c2ecf20Sopenharmony_ci VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* The host only accepts VIDEO_MODE_HINTS if it is send separately. */ 268c2ecf20Sopenharmony_ci hgsmi_send_caps_info(vbox->guest_pool, caps); 278c2ecf20Sopenharmony_ci caps |= VBVACAPS_VIDEO_MODE_HINTS; 288c2ecf20Sopenharmony_ci hgsmi_send_caps_info(vbox->guest_pool, caps); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int vbox_accel_init(struct vbox_private *vbox) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct vbva_buffer *vbva; 348c2ecf20Sopenharmony_ci unsigned int i; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci vbox->vbva_info = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 378c2ecf20Sopenharmony_ci sizeof(*vbox->vbva_info), GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (!vbox->vbva_info) 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* Take a command buffer for each screen from the end of usable VRAM. */ 428c2ecf20Sopenharmony_ci vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci vbox->vbva_buffers = pci_iomap_range(vbox->ddev.pdev, 0, 458c2ecf20Sopenharmony_ci vbox->available_vram_size, 468c2ecf20Sopenharmony_ci vbox->num_crtcs * 478c2ecf20Sopenharmony_ci VBVA_MIN_BUFFER_SIZE); 488c2ecf20Sopenharmony_ci if (!vbox->vbva_buffers) 498c2ecf20Sopenharmony_ci return -ENOMEM; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci for (i = 0; i < vbox->num_crtcs; ++i) { 528c2ecf20Sopenharmony_ci vbva_setup_buffer_context(&vbox->vbva_info[i], 538c2ecf20Sopenharmony_ci vbox->available_vram_size + 548c2ecf20Sopenharmony_ci i * VBVA_MIN_BUFFER_SIZE, 558c2ecf20Sopenharmony_ci VBVA_MIN_BUFFER_SIZE); 568c2ecf20Sopenharmony_ci vbva = (void __force *)vbox->vbva_buffers + 578c2ecf20Sopenharmony_ci i * VBVA_MIN_BUFFER_SIZE; 588c2ecf20Sopenharmony_ci if (!vbva_enable(&vbox->vbva_info[i], 598c2ecf20Sopenharmony_ci vbox->guest_pool, vbva, i)) { 608c2ecf20Sopenharmony_ci /* very old host or driver error. */ 618c2ecf20Sopenharmony_ci DRM_ERROR("vboxvideo: vbva_enable failed\n"); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void vbox_accel_fini(struct vbox_private *vbox) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned int i; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 0; i < vbox->num_crtcs; ++i) 738c2ecf20Sopenharmony_ci vbva_disable(&vbox->vbva_info[i], vbox->guest_pool, i); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Do we support the 4.3 plus mode hint reporting interface? */ 778c2ecf20Sopenharmony_cistatic bool have_hgsmi_mode_hints(struct vbox_private *vbox) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci u32 have_hints, have_cursor; 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ret = hgsmi_query_conf(vbox->guest_pool, 838c2ecf20Sopenharmony_ci VBOX_VBVA_CONF32_MODE_HINT_REPORTING, 848c2ecf20Sopenharmony_ci &have_hints); 858c2ecf20Sopenharmony_ci if (ret) 868c2ecf20Sopenharmony_ci return false; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ret = hgsmi_query_conf(vbox->guest_pool, 898c2ecf20Sopenharmony_ci VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, 908c2ecf20Sopenharmony_ci &have_cursor); 918c2ecf20Sopenharmony_ci if (ret) 928c2ecf20Sopenharmony_ci return false; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cibool vbox_check_supported(u16 id) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci u16 dispi_id; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_ID, id); 1028c2ecf20Sopenharmony_ci dispi_id = inw(VBE_DISPI_IOPORT_DATA); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return dispi_id == id; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ciint vbox_hw_init(struct vbox_private *vbox) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci int ret = -ENOMEM; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA); 1128c2ecf20Sopenharmony_ci vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci DRM_INFO("VRAM %08x\n", vbox->full_vram_size); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Map guest-heap at end of vram */ 1178c2ecf20Sopenharmony_ci vbox->guest_heap = 1188c2ecf20Sopenharmony_ci pci_iomap_range(vbox->ddev.pdev, 0, GUEST_HEAP_OFFSET(vbox), 1198c2ecf20Sopenharmony_ci GUEST_HEAP_SIZE); 1208c2ecf20Sopenharmony_ci if (!vbox->guest_heap) 1218c2ecf20Sopenharmony_ci return -ENOMEM; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ 1248c2ecf20Sopenharmony_ci vbox->guest_pool = devm_gen_pool_create(vbox->ddev.dev, 4, -1, 1258c2ecf20Sopenharmony_ci "vboxvideo-accel"); 1268c2ecf20Sopenharmony_ci if (IS_ERR(vbox->guest_pool)) 1278c2ecf20Sopenharmony_ci return PTR_ERR(vbox->guest_pool); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = gen_pool_add_virt(vbox->guest_pool, 1308c2ecf20Sopenharmony_ci (unsigned long)vbox->guest_heap, 1318c2ecf20Sopenharmony_ci GUEST_HEAP_OFFSET(vbox), 1328c2ecf20Sopenharmony_ci GUEST_HEAP_USABLE_SIZE, -1); 1338c2ecf20Sopenharmony_ci if (ret) 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = hgsmi_test_query_conf(vbox->guest_pool); 1378c2ecf20Sopenharmony_ci if (ret) { 1388c2ecf20Sopenharmony_ci DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n"); 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Reduce available VRAM size to reflect the guest heap. */ 1438c2ecf20Sopenharmony_ci vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox); 1448c2ecf20Sopenharmony_ci /* Linux drm represents monitors as a 32-bit array. */ 1458c2ecf20Sopenharmony_ci hgsmi_query_conf(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT, 1468c2ecf20Sopenharmony_ci &vbox->num_crtcs); 1478c2ecf20Sopenharmony_ci vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!have_hgsmi_mode_hints(vbox)) { 1508c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci vbox->last_mode_hints = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs, 1558c2ecf20Sopenharmony_ci sizeof(struct vbva_modehint), 1568c2ecf20Sopenharmony_ci GFP_KERNEL); 1578c2ecf20Sopenharmony_ci if (!vbox->last_mode_hints) 1588c2ecf20Sopenharmony_ci return -ENOMEM; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = vbox_accel_init(vbox); 1618c2ecf20Sopenharmony_ci if (ret) 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_civoid vbox_hw_fini(struct vbox_private *vbox) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci vbox_accel_fini(vbox); 1708c2ecf20Sopenharmony_ci} 171