vbox_irq.c 4.9 KB

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