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