Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: MIT
0002 /*
0003  * Copyright (C) 2016-2017 Oracle Corporation
0004  * This file is based on qxl_irq.c
0005  * Copyright 2013 Red Hat Inc.
0006  * Authors: Dave Airlie
0007  *          Alon Levy
0008  *          Michael Thayer <michael.thayer@oracle.com,
0009  *          Hans de Goede <hdegoede@redhat.com>
0010  */
0011 
0012 #include <linux/pci.h>
0013 
0014 #include <drm/drm_drv.h>
0015 #include <drm/drm_probe_helper.h>
0016 
0017 #include "vbox_drv.h"
0018 #include "vboxvideo.h"
0019 
0020 static void vbox_clear_irq(void)
0021 {
0022     outl((u32)~0, VGA_PORT_HGSMI_HOST);
0023 }
0024 
0025 static u32 vbox_get_flags(struct vbox_private *vbox)
0026 {
0027     return readl(vbox->guest_heap + HOST_FLAGS_OFFSET);
0028 }
0029 
0030 void vbox_report_hotplug(struct vbox_private *vbox)
0031 {
0032     schedule_work(&vbox->hotplug_work);
0033 }
0034 
0035 static irqreturn_t vbox_irq_handler(int irq, void *arg)
0036 {
0037     struct drm_device *dev = (struct drm_device *)arg;
0038     struct vbox_private *vbox = to_vbox_dev(dev);
0039     u32 host_flags = vbox_get_flags(vbox);
0040 
0041     if (!(host_flags & HGSMIHOSTFLAGS_IRQ))
0042         return IRQ_NONE;
0043 
0044     /*
0045      * Due to a bug in the initial host implementation of hot-plug irqs,
0046      * the hot-plug and cursor capability flags were never cleared.
0047      * Fortunately we can tell when they would have been set by checking
0048      * that the VSYNC flag is not set.
0049      */
0050     if (host_flags &
0051         (HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES) &&
0052         !(host_flags & HGSMIHOSTFLAGS_VSYNC))
0053         vbox_report_hotplug(vbox);
0054 
0055     vbox_clear_irq();
0056 
0057     return IRQ_HANDLED;
0058 }
0059 
0060 /*
0061  * Check that the position hints provided by the host are suitable for GNOME
0062  * shell (i.e. all screens disjoint and hints for all enabled screens) and if
0063  * not replace them with default ones.  Providing valid hints improves the
0064  * chances that we will get a known screen layout for pointer mapping.
0065  */
0066 static void validate_or_set_position_hints(struct vbox_private *vbox)
0067 {
0068     struct vbva_modehint *hintsi, *hintsj;
0069     bool valid = true;
0070     u16 currentx = 0;
0071     int i, j;
0072 
0073     for (i = 0; i < vbox->num_crtcs; ++i) {
0074         for (j = 0; j < i; ++j) {
0075             hintsi = &vbox->last_mode_hints[i];
0076             hintsj = &vbox->last_mode_hints[j];
0077 
0078             if (hintsi->enabled && hintsj->enabled) {
0079                 if (hintsi->dx >= 0xffff ||
0080                     hintsi->dy >= 0xffff ||
0081                     hintsj->dx >= 0xffff ||
0082                     hintsj->dy >= 0xffff ||
0083                     (hintsi->dx <
0084                     hintsj->dx + (hintsj->cx & 0x8fff) &&
0085                      hintsi->dx + (hintsi->cx & 0x8fff) >
0086                     hintsj->dx) ||
0087                     (hintsi->dy <
0088                     hintsj->dy + (hintsj->cy & 0x8fff) &&
0089                      hintsi->dy + (hintsi->cy & 0x8fff) >
0090                     hintsj->dy))
0091                     valid = false;
0092             }
0093         }
0094     }
0095     if (!valid)
0096         for (i = 0; i < vbox->num_crtcs; ++i) {
0097             if (vbox->last_mode_hints[i].enabled) {
0098                 vbox->last_mode_hints[i].dx = currentx;
0099                 vbox->last_mode_hints[i].dy = 0;
0100                 currentx +=
0101                     vbox->last_mode_hints[i].cx & 0x8fff;
0102             }
0103         }
0104 }
0105 
0106 /* Query the host for the most recent video mode hints. */
0107 static void vbox_update_mode_hints(struct vbox_private *vbox)
0108 {
0109     struct drm_connector_list_iter conn_iter;
0110     struct drm_device *dev = &vbox->ddev;
0111     struct drm_connector *connector;
0112     struct vbox_connector *vbox_conn;
0113     struct vbva_modehint *hints;
0114     u16 flags;
0115     bool disconnected;
0116     unsigned int crtc_id;
0117     int ret;
0118 
0119     ret = hgsmi_get_mode_hints(vbox->guest_pool, vbox->num_crtcs,
0120                    vbox->last_mode_hints);
0121     if (ret) {
0122         DRM_ERROR("vboxvideo: hgsmi_get_mode_hints failed: %d\n", ret);
0123         return;
0124     }
0125 
0126     validate_or_set_position_hints(vbox);
0127 
0128     drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
0129     drm_connector_list_iter_begin(dev, &conn_iter);
0130     drm_for_each_connector_iter(connector, &conn_iter) {
0131         vbox_conn = to_vbox_connector(connector);
0132 
0133         hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id];
0134         if (hints->magic != VBVAMODEHINT_MAGIC)
0135             continue;
0136 
0137         disconnected = !(hints->enabled);
0138         crtc_id = vbox_conn->vbox_crtc->crtc_id;
0139         vbox_conn->mode_hint.width = hints->cx;
0140         vbox_conn->mode_hint.height = hints->cy;
0141         vbox_conn->vbox_crtc->x_hint = hints->dx;
0142         vbox_conn->vbox_crtc->y_hint = hints->dy;
0143         vbox_conn->mode_hint.disconnected = disconnected;
0144 
0145         if (vbox_conn->vbox_crtc->disconnected == disconnected)
0146             continue;
0147 
0148         if (disconnected)
0149             flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED;
0150         else
0151             flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK;
0152 
0153         hgsmi_process_display_info(vbox->guest_pool, crtc_id, 0, 0, 0,
0154                        hints->cx * 4, hints->cx,
0155                        hints->cy, 0, flags);
0156 
0157         vbox_conn->vbox_crtc->disconnected = disconnected;
0158     }
0159     drm_connector_list_iter_end(&conn_iter);
0160     drm_modeset_unlock(&dev->mode_config.connection_mutex);
0161 }
0162 
0163 static void vbox_hotplug_worker(struct work_struct *work)
0164 {
0165     struct vbox_private *vbox = container_of(work, struct vbox_private,
0166                          hotplug_work);
0167 
0168     vbox_update_mode_hints(vbox);
0169     drm_kms_helper_hotplug_event(&vbox->ddev);
0170 }
0171 
0172 int vbox_irq_init(struct vbox_private *vbox)
0173 {
0174     struct drm_device *dev = &vbox->ddev;
0175     struct pci_dev *pdev = to_pci_dev(dev->dev);
0176 
0177     INIT_WORK(&vbox->hotplug_work, vbox_hotplug_worker);
0178     vbox_update_mode_hints(vbox);
0179 
0180     /* PCI devices require shared interrupts. */
0181     return request_irq(pdev->irq, vbox_irq_handler, IRQF_SHARED, dev->driver->name, dev);
0182 }
0183 
0184 void vbox_irq_fini(struct vbox_private *vbox)
0185 {
0186     struct drm_device *dev = &vbox->ddev;
0187     struct pci_dev *pdev = to_pci_dev(dev->dev);
0188 
0189     free_irq(pdev->irq, dev);
0190     flush_work(&vbox->hotplug_work);
0191 }