Home | History | Annotate | Line # | Download | only in vboxvideo
      1 /*	$NetBSD: vbox_irq.c,v 1.2 2021/12/18 23:45:44 riastradh Exp $	*/
      2 
      3 // SPDX-License-Identifier: MIT
      4 /*
      5  * Copyright (C) 2016-2017 Oracle Corporation
      6  * This file is based on qxl_irq.c
      7  * Copyright 2013 Red Hat Inc.
      8  * Authors: Dave Airlie
      9  *          Alon Levy
     10  *          Michael Thayer <michael.thayer (at) oracle.com,
     11  *          Hans de Goede <hdegoede (at) redhat.com>
     12  */
     13 
     14 #include <sys/cdefs.h>
     15 __KERNEL_RCSID(0, "$NetBSD: vbox_irq.c,v 1.2 2021/12/18 23:45:44 riastradh Exp $");
     16 
     17 #include <linux/pci.h>
     18 #include <drm/drm_irq.h>
     19 #include <drm/drm_probe_helper.h>
     20 
     21 #include "vbox_drv.h"
     22 #include "vboxvideo.h"
     23 
     24 static void vbox_clear_irq(void)
     25 {
     26 	outl((u32)~0, VGA_PORT_HGSMI_HOST);
     27 }
     28 
     29 static u32 vbox_get_flags(struct vbox_private *vbox)
     30 {
     31 	return readl(vbox->guest_heap + HOST_FLAGS_OFFSET);
     32 }
     33 
     34 void vbox_report_hotplug(struct vbox_private *vbox)
     35 {
     36 	schedule_work(&vbox->hotplug_work);
     37 }
     38 
     39 irqreturn_t vbox_irq_handler(int irq, void *arg)
     40 {
     41 	struct drm_device *dev = (struct drm_device *)arg;
     42 	struct vbox_private *vbox = (struct vbox_private *)dev->dev_private;
     43 	u32 host_flags = vbox_get_flags(vbox);
     44 
     45 	if (!(host_flags & HGSMIHOSTFLAGS_IRQ))
     46 		return IRQ_NONE;
     47 
     48 	/*
     49 	 * Due to a bug in the initial host implementation of hot-plug irqs,
     50 	 * the hot-plug and cursor capability flags were never cleared.
     51 	 * Fortunately we can tell when they would have been set by checking
     52 	 * that the VSYNC flag is not set.
     53 	 */
     54 	if (host_flags &
     55 	    (HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES) &&
     56 	    !(host_flags & HGSMIHOSTFLAGS_VSYNC))
     57 		vbox_report_hotplug(vbox);
     58 
     59 	vbox_clear_irq();
     60 
     61 	return IRQ_HANDLED;
     62 }
     63 
     64 /*
     65  * Check that the position hints provided by the host are suitable for GNOME
     66  * shell (i.e. all screens disjoint and hints for all enabled screens) and if
     67  * not replace them with default ones.  Providing valid hints improves the
     68  * chances that we will get a known screen layout for pointer mapping.
     69  */
     70 static void validate_or_set_position_hints(struct vbox_private *vbox)
     71 {
     72 	struct vbva_modehint *hintsi, *hintsj;
     73 	bool valid = true;
     74 	u16 currentx = 0;
     75 	int i, j;
     76 
     77 	for (i = 0; i < vbox->num_crtcs; ++i) {
     78 		for (j = 0; j < i; ++j) {
     79 			hintsi = &vbox->last_mode_hints[i];
     80 			hintsj = &vbox->last_mode_hints[j];
     81 
     82 			if (hintsi->enabled && hintsj->enabled) {
     83 				if (hintsi->dx >= 0xffff ||
     84 				    hintsi->dy >= 0xffff ||
     85 				    hintsj->dx >= 0xffff ||
     86 				    hintsj->dy >= 0xffff ||
     87 				    (hintsi->dx <
     88 					hintsj->dx + (hintsj->cx & 0x8fff) &&
     89 				     hintsi->dx + (hintsi->cx & 0x8fff) >
     90 					hintsj->dx) ||
     91 				    (hintsi->dy <
     92 					hintsj->dy + (hintsj->cy & 0x8fff) &&
     93 				     hintsi->dy + (hintsi->cy & 0x8fff) >
     94 					hintsj->dy))
     95 					valid = false;
     96 			}
     97 		}
     98 	}
     99 	if (!valid)
    100 		for (i = 0; i < vbox->num_crtcs; ++i) {
    101 			if (vbox->last_mode_hints[i].enabled) {
    102 				vbox->last_mode_hints[i].dx = currentx;
    103 				vbox->last_mode_hints[i].dy = 0;
    104 				currentx +=
    105 				    vbox->last_mode_hints[i].cx & 0x8fff;
    106 			}
    107 		}
    108 }
    109 
    110 /* Query the host for the most recent video mode hints. */
    111 static void vbox_update_mode_hints(struct vbox_private *vbox)
    112 {
    113 	struct drm_connector_list_iter conn_iter;
    114 	struct drm_device *dev = &vbox->ddev;
    115 	struct drm_connector *connector;
    116 	struct vbox_connector *vbox_conn;
    117 	struct vbva_modehint *hints;
    118 	u16 flags;
    119 	bool disconnected;
    120 	unsigned int crtc_id;
    121 	int ret;
    122 
    123 	ret = hgsmi_get_mode_hints(vbox->guest_pool, vbox->num_crtcs,
    124 				   vbox->last_mode_hints);
    125 	if (ret) {
    126 		DRM_ERROR("vboxvideo: hgsmi_get_mode_hints failed: %d\n", ret);
    127 		return;
    128 	}
    129 
    130 	validate_or_set_position_hints(vbox);
    131 
    132 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
    133 	drm_connector_list_iter_begin(dev, &conn_iter);
    134 	drm_for_each_connector_iter(connector, &conn_iter) {
    135 		vbox_conn = to_vbox_connector(connector);
    136 
    137 		hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id];
    138 		if (hints->magic != VBVAMODEHINT_MAGIC)
    139 			continue;
    140 
    141 		disconnected = !(hints->enabled);
    142 		crtc_id = vbox_conn->vbox_crtc->crtc_id;
    143 		vbox_conn->mode_hint.width = hints->cx;
    144 		vbox_conn->mode_hint.height = hints->cy;
    145 		vbox_conn->vbox_crtc->x_hint = hints->dx;
    146 		vbox_conn->vbox_crtc->y_hint = hints->dy;
    147 		vbox_conn->mode_hint.disconnected = disconnected;
    148 
    149 		if (vbox_conn->vbox_crtc->disconnected == disconnected)
    150 			continue;
    151 
    152 		if (disconnected)
    153 			flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED;
    154 		else
    155 			flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK;
    156 
    157 		hgsmi_process_display_info(vbox->guest_pool, crtc_id, 0, 0, 0,
    158 					   hints->cx * 4, hints->cx,
    159 					   hints->cy, 0, flags);
    160 
    161 		vbox_conn->vbox_crtc->disconnected = disconnected;
    162 	}
    163 	drm_connector_list_iter_end(&conn_iter);
    164 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
    165 }
    166 
    167 static void vbox_hotplug_worker(struct work_struct *work)
    168 {
    169 	struct vbox_private *vbox = container_of(work, struct vbox_private,
    170 						 hotplug_work);
    171 
    172 	vbox_update_mode_hints(vbox);
    173 	drm_kms_helper_hotplug_event(&vbox->ddev);
    174 }
    175 
    176 int vbox_irq_init(struct vbox_private *vbox)
    177 {
    178 	INIT_WORK(&vbox->hotplug_work, vbox_hotplug_worker);
    179 	vbox_update_mode_hints(vbox);
    180 
    181 	return drm_irq_install(&vbox->ddev, vbox->ddev.pdev->irq);
    182 }
    183 
    184 void vbox_irq_fini(struct vbox_private *vbox)
    185 {
    186 	drm_irq_uninstall(&vbox->ddev);
    187 	flush_work(&vbox->hotplug_work);
    188 }
    189