xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/vboxvideo/vbox_irq.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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