18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016-2017 Oracle Corporation 48c2ecf20Sopenharmony_ci * This file is based on qxl_irq.c 58c2ecf20Sopenharmony_ci * Copyright 2013 Red Hat Inc. 68c2ecf20Sopenharmony_ci * Authors: Dave Airlie 78c2ecf20Sopenharmony_ci * Alon Levy 88c2ecf20Sopenharmony_ci * Michael Thayer <michael.thayer@oracle.com, 98c2ecf20Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_irq.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "vbox_drv.h" 178c2ecf20Sopenharmony_ci#include "vboxvideo.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic void vbox_clear_irq(void) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci outl((u32)~0, VGA_PORT_HGSMI_HOST); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic u32 vbox_get_flags(struct vbox_private *vbox) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci return readl(vbox->guest_heap + HOST_FLAGS_OFFSET); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_civoid vbox_report_hotplug(struct vbox_private *vbox) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci schedule_work(&vbox->hotplug_work); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciirqreturn_t vbox_irq_handler(int irq, void *arg) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct drm_device *dev = (struct drm_device *)arg; 378c2ecf20Sopenharmony_ci struct vbox_private *vbox = to_vbox_dev(dev); 388c2ecf20Sopenharmony_ci u32 host_flags = vbox_get_flags(vbox); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!(host_flags & HGSMIHOSTFLAGS_IRQ)) 418c2ecf20Sopenharmony_ci return IRQ_NONE; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* 448c2ecf20Sopenharmony_ci * Due to a bug in the initial host implementation of hot-plug irqs, 458c2ecf20Sopenharmony_ci * the hot-plug and cursor capability flags were never cleared. 468c2ecf20Sopenharmony_ci * Fortunately we can tell when they would have been set by checking 478c2ecf20Sopenharmony_ci * that the VSYNC flag is not set. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci if (host_flags & 508c2ecf20Sopenharmony_ci (HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES) && 518c2ecf20Sopenharmony_ci !(host_flags & HGSMIHOSTFLAGS_VSYNC)) 528c2ecf20Sopenharmony_ci vbox_report_hotplug(vbox); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci vbox_clear_irq(); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * Check that the position hints provided by the host are suitable for GNOME 618c2ecf20Sopenharmony_ci * shell (i.e. all screens disjoint and hints for all enabled screens) and if 628c2ecf20Sopenharmony_ci * not replace them with default ones. Providing valid hints improves the 638c2ecf20Sopenharmony_ci * chances that we will get a known screen layout for pointer mapping. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic void validate_or_set_position_hints(struct vbox_private *vbox) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct vbva_modehint *hintsi, *hintsj; 688c2ecf20Sopenharmony_ci bool valid = true; 698c2ecf20Sopenharmony_ci u16 currentx = 0; 708c2ecf20Sopenharmony_ci int i, j; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 0; i < vbox->num_crtcs; ++i) { 738c2ecf20Sopenharmony_ci for (j = 0; j < i; ++j) { 748c2ecf20Sopenharmony_ci hintsi = &vbox->last_mode_hints[i]; 758c2ecf20Sopenharmony_ci hintsj = &vbox->last_mode_hints[j]; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (hintsi->enabled && hintsj->enabled) { 788c2ecf20Sopenharmony_ci if (hintsi->dx >= 0xffff || 798c2ecf20Sopenharmony_ci hintsi->dy >= 0xffff || 808c2ecf20Sopenharmony_ci hintsj->dx >= 0xffff || 818c2ecf20Sopenharmony_ci hintsj->dy >= 0xffff || 828c2ecf20Sopenharmony_ci (hintsi->dx < 838c2ecf20Sopenharmony_ci hintsj->dx + (hintsj->cx & 0x8fff) && 848c2ecf20Sopenharmony_ci hintsi->dx + (hintsi->cx & 0x8fff) > 858c2ecf20Sopenharmony_ci hintsj->dx) || 868c2ecf20Sopenharmony_ci (hintsi->dy < 878c2ecf20Sopenharmony_ci hintsj->dy + (hintsj->cy & 0x8fff) && 888c2ecf20Sopenharmony_ci hintsi->dy + (hintsi->cy & 0x8fff) > 898c2ecf20Sopenharmony_ci hintsj->dy)) 908c2ecf20Sopenharmony_ci valid = false; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci if (!valid) 958c2ecf20Sopenharmony_ci for (i = 0; i < vbox->num_crtcs; ++i) { 968c2ecf20Sopenharmony_ci if (vbox->last_mode_hints[i].enabled) { 978c2ecf20Sopenharmony_ci vbox->last_mode_hints[i].dx = currentx; 988c2ecf20Sopenharmony_ci vbox->last_mode_hints[i].dy = 0; 998c2ecf20Sopenharmony_ci currentx += 1008c2ecf20Sopenharmony_ci vbox->last_mode_hints[i].cx & 0x8fff; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Query the host for the most recent video mode hints. */ 1068c2ecf20Sopenharmony_cistatic void vbox_update_mode_hints(struct vbox_private *vbox) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 1098c2ecf20Sopenharmony_ci struct drm_device *dev = &vbox->ddev; 1108c2ecf20Sopenharmony_ci struct drm_connector *connector; 1118c2ecf20Sopenharmony_ci struct vbox_connector *vbox_conn; 1128c2ecf20Sopenharmony_ci struct vbva_modehint *hints; 1138c2ecf20Sopenharmony_ci u16 flags; 1148c2ecf20Sopenharmony_ci bool disconnected; 1158c2ecf20Sopenharmony_ci unsigned int crtc_id; 1168c2ecf20Sopenharmony_ci int ret; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = hgsmi_get_mode_hints(vbox->guest_pool, vbox->num_crtcs, 1198c2ecf20Sopenharmony_ci vbox->last_mode_hints); 1208c2ecf20Sopenharmony_ci if (ret) { 1218c2ecf20Sopenharmony_ci DRM_ERROR("vboxvideo: hgsmi_get_mode_hints failed: %d\n", ret); 1228c2ecf20Sopenharmony_ci return; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci validate_or_set_position_hints(vbox); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); 1288c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 1298c2ecf20Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 1308c2ecf20Sopenharmony_ci vbox_conn = to_vbox_connector(connector); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id]; 1338c2ecf20Sopenharmony_ci if (hints->magic != VBVAMODEHINT_MAGIC) 1348c2ecf20Sopenharmony_ci continue; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci disconnected = !(hints->enabled); 1378c2ecf20Sopenharmony_ci crtc_id = vbox_conn->vbox_crtc->crtc_id; 1388c2ecf20Sopenharmony_ci vbox_conn->mode_hint.width = hints->cx; 1398c2ecf20Sopenharmony_ci vbox_conn->mode_hint.height = hints->cy; 1408c2ecf20Sopenharmony_ci vbox_conn->vbox_crtc->x_hint = hints->dx; 1418c2ecf20Sopenharmony_ci vbox_conn->vbox_crtc->y_hint = hints->dy; 1428c2ecf20Sopenharmony_ci vbox_conn->mode_hint.disconnected = disconnected; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (vbox_conn->vbox_crtc->disconnected == disconnected) 1458c2ecf20Sopenharmony_ci continue; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (disconnected) 1488c2ecf20Sopenharmony_ci flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci hgsmi_process_display_info(vbox->guest_pool, crtc_id, 0, 0, 0, 1538c2ecf20Sopenharmony_ci hints->cx * 4, hints->cx, 1548c2ecf20Sopenharmony_ci hints->cy, 0, flags); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci vbox_conn->vbox_crtc->disconnected = disconnected; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 1598c2ecf20Sopenharmony_ci drm_modeset_unlock(&dev->mode_config.connection_mutex); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void vbox_hotplug_worker(struct work_struct *work) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct vbox_private *vbox = container_of(work, struct vbox_private, 1658c2ecf20Sopenharmony_ci hotplug_work); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci vbox_update_mode_hints(vbox); 1688c2ecf20Sopenharmony_ci drm_kms_helper_hotplug_event(&vbox->ddev); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciint vbox_irq_init(struct vbox_private *vbox) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci INIT_WORK(&vbox->hotplug_work, vbox_hotplug_worker); 1748c2ecf20Sopenharmony_ci vbox_update_mode_hints(vbox); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return drm_irq_install(&vbox->ddev, vbox->ddev.pdev->irq); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_civoid vbox_irq_fini(struct vbox_private *vbox) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci drm_irq_uninstall(&vbox->ddev); 1828c2ecf20Sopenharmony_ci flush_work(&vbox->hotplug_work); 1838c2ecf20Sopenharmony_ci} 184