linux/drivers/staging/vboxvideo/vbox_irq.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016-2017 Oracle Corporation
   3 * This file is based on qxl_irq.c
   4 * Copyright 2013 Red Hat Inc.
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a
   7 * copy of this software and associated documentation files (the "Software"),
   8 * to deal in the Software without restriction, including without limitation
   9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  10 * and/or sell copies of the Software, and to permit persons to whom the
  11 * Software is furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  22 * OTHER DEALINGS IN THE SOFTWARE.
  23 *
  24 * Authors: Dave Airlie
  25 *          Alon Levy
  26 *          Michael Thayer <michael.thayer@oracle.com,
  27 *          Hans de Goede <hdegoede@redhat.com>
  28 */
  29
  30#include <drm/drm_crtc_helper.h>
  31
  32#include "vbox_drv.h"
  33#include "vboxvideo.h"
  34
  35static void vbox_clear_irq(void)
  36{
  37        outl((u32)~0, VGA_PORT_HGSMI_HOST);
  38}
  39
  40static u32 vbox_get_flags(struct vbox_private *vbox)
  41{
  42        return readl(vbox->guest_heap + HOST_FLAGS_OFFSET);
  43}
  44
  45void vbox_report_hotplug(struct vbox_private *vbox)
  46{
  47        schedule_work(&vbox->hotplug_work);
  48}
  49
  50irqreturn_t vbox_irq_handler(int irq, void *arg)
  51{
  52        struct drm_device *dev = (struct drm_device *)arg;
  53        struct vbox_private *vbox = (struct vbox_private *)dev->dev_private;
  54        u32 host_flags = vbox_get_flags(vbox);
  55
  56        if (!(host_flags & HGSMIHOSTFLAGS_IRQ))
  57                return IRQ_NONE;
  58
  59        /*
  60         * Due to a bug in the initial host implementation of hot-plug irqs,
  61         * the hot-plug and cursor capability flags were never cleared.
  62         * Fortunately we can tell when they would have been set by checking
  63         * that the VSYNC flag is not set.
  64         */
  65        if (host_flags &
  66            (HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES) &&
  67            !(host_flags & HGSMIHOSTFLAGS_VSYNC))
  68                vbox_report_hotplug(vbox);
  69
  70        vbox_clear_irq();
  71
  72        return IRQ_HANDLED;
  73}
  74
  75/**
  76 * Check that the position hints provided by the host are suitable for GNOME
  77 * shell (i.e. all screens disjoint and hints for all enabled screens) and if
  78 * not replace them with default ones.  Providing valid hints improves the
  79 * chances that we will get a known screen layout for pointer mapping.
  80 */
  81static void validate_or_set_position_hints(struct vbox_private *vbox)
  82{
  83        struct vbva_modehint *hintsi, *hintsj;
  84        bool valid = true;
  85        u16 currentx = 0;
  86        int i, j;
  87
  88        for (i = 0; i < vbox->num_crtcs; ++i) {
  89                for (j = 0; j < i; ++j) {
  90                        hintsi = &vbox->last_mode_hints[i];
  91                        hintsj = &vbox->last_mode_hints[j];
  92
  93                        if (hintsi->enabled && hintsj->enabled) {
  94                                if (hintsi->dx >= 0xffff ||
  95                                    hintsi->dy >= 0xffff ||
  96                                    hintsj->dx >= 0xffff ||
  97                                    hintsj->dy >= 0xffff ||
  98                                    (hintsi->dx <
  99                                        hintsj->dx + (hintsj->cx & 0x8fff) &&
 100                                     hintsi->dx + (hintsi->cx & 0x8fff) >
 101                                        hintsj->dx) ||
 102                                    (hintsi->dy <
 103                                        hintsj->dy + (hintsj->cy & 0x8fff) &&
 104                                     hintsi->dy + (hintsi->cy & 0x8fff) >
 105                                        hintsj->dy))
 106                                        valid = false;
 107                        }
 108                }
 109        }
 110        if (!valid)
 111                for (i = 0; i < vbox->num_crtcs; ++i) {
 112                        if (vbox->last_mode_hints[i].enabled) {
 113                                vbox->last_mode_hints[i].dx = currentx;
 114                                vbox->last_mode_hints[i].dy = 0;
 115                                currentx +=
 116                                    vbox->last_mode_hints[i].cx & 0x8fff;
 117                        }
 118                }
 119}
 120
 121/**
 122 * Query the host for the most recent video mode hints.
 123 */
 124static void vbox_update_mode_hints(struct vbox_private *vbox)
 125{
 126        struct drm_device *dev = vbox->dev;
 127        struct drm_connector *connector;
 128        struct vbox_connector *vbox_conn;
 129        struct vbva_modehint *hints;
 130        u16 flags;
 131        bool disconnected;
 132        unsigned int crtc_id;
 133        int ret;
 134
 135        ret = hgsmi_get_mode_hints(vbox->guest_pool, vbox->num_crtcs,
 136                                   vbox->last_mode_hints);
 137        if (ret) {
 138                DRM_ERROR("vboxvideo: hgsmi_get_mode_hints failed: %d\n", ret);
 139                return;
 140        }
 141
 142        validate_or_set_position_hints(vbox);
 143        drm_modeset_lock_all(dev);
 144        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 145                vbox_conn = to_vbox_connector(connector);
 146
 147                hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id];
 148                if (hints->magic != VBVAMODEHINT_MAGIC)
 149                        continue;
 150
 151                disconnected = !(hints->enabled);
 152                crtc_id = vbox_conn->vbox_crtc->crtc_id;
 153                vbox_conn->mode_hint.width = hints->cx;
 154                vbox_conn->mode_hint.height = hints->cy;
 155                vbox_conn->vbox_crtc->x_hint = hints->dx;
 156                vbox_conn->vbox_crtc->y_hint = hints->dy;
 157                vbox_conn->mode_hint.disconnected = disconnected;
 158
 159                if (vbox_conn->vbox_crtc->disconnected == disconnected)
 160                        continue;
 161
 162                if (disconnected)
 163                        flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED;
 164                else
 165                        flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK;
 166
 167                hgsmi_process_display_info(vbox->guest_pool, crtc_id, 0, 0, 0,
 168                                           hints->cx * 4, hints->cx,
 169                                           hints->cy, 0, flags);
 170
 171                vbox_conn->vbox_crtc->disconnected = disconnected;
 172        }
 173        drm_modeset_unlock_all(dev);
 174}
 175
 176static void vbox_hotplug_worker(struct work_struct *work)
 177{
 178        struct vbox_private *vbox = container_of(work, struct vbox_private,
 179                                                 hotplug_work);
 180
 181        vbox_update_mode_hints(vbox);
 182        drm_kms_helper_hotplug_event(vbox->dev);
 183}
 184
 185int vbox_irq_init(struct vbox_private *vbox)
 186{
 187        INIT_WORK(&vbox->hotplug_work, vbox_hotplug_worker);
 188        vbox_update_mode_hints(vbox);
 189
 190        return drm_irq_install(vbox->dev, vbox->dev->pdev->irq);
 191}
 192
 193void vbox_irq_fini(struct vbox_private *vbox)
 194{
 195        drm_irq_uninstall(vbox->dev);
 196        flush_work(&vbox->hotplug_work);
 197}
 198