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