2018-10-18 17:03:32 +02:00
// SPDX-License-Identifier: MIT
2017-07-06 16:06:01 +02:00
/*
* Copyright ( C ) 2016 - 2017 Oracle Corporation
* This file is based on qxl_irq . c
* Copyright 2013 Red Hat Inc .
* Authors : Dave Airlie
* Alon Levy
* Michael Thayer < michael . thayer @ oracle . com ,
* Hans de Goede < hdegoede @ redhat . com >
*/
2019-01-17 22:03:34 +01:00
# include <linux/pci.h>
# include <drm/drm_irq.h>
# include <drm/drm_probe_helper.h>
2017-07-06 16:06:01 +02:00
# include "vbox_drv.h"
# include "vboxvideo.h"
static void vbox_clear_irq ( void )
{
outl ( ( u32 ) ~ 0 , VGA_PORT_HGSMI_HOST ) ;
}
static u32 vbox_get_flags ( struct vbox_private * vbox )
{
return readl ( vbox - > guest_heap + HOST_FLAGS_OFFSET ) ;
}
void vbox_report_hotplug ( struct vbox_private * vbox )
{
schedule_work ( & vbox - > hotplug_work ) ;
}
irqreturn_t vbox_irq_handler ( int irq , void * arg )
{
struct drm_device * dev = ( struct drm_device * ) arg ;
2020-04-15 09:39:39 +02:00
struct vbox_private * vbox = to_vbox_dev ( dev ) ;
2017-07-06 16:06:01 +02:00
u32 host_flags = vbox_get_flags ( vbox ) ;
if ( ! ( host_flags & HGSMIHOSTFLAGS_IRQ ) )
return IRQ_NONE ;
/*
* Due to a bug in the initial host implementation of hot - plug irqs ,
* the hot - plug and cursor capability flags were never cleared .
* Fortunately we can tell when they would have been set by checking
* that the VSYNC flag is not set .
*/
if ( host_flags &
( HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES ) & &
! ( host_flags & HGSMIHOSTFLAGS_VSYNC ) )
vbox_report_hotplug ( vbox ) ;
vbox_clear_irq ( ) ;
return IRQ_HANDLED ;
}
2018-10-18 17:03:31 +02:00
/*
2017-07-06 16:06:01 +02:00
* Check that the position hints provided by the host are suitable for GNOME
* shell ( i . e . all screens disjoint and hints for all enabled screens ) and if
* not replace them with default ones . Providing valid hints improves the
* chances that we will get a known screen layout for pointer mapping .
*/
static void validate_or_set_position_hints ( struct vbox_private * vbox )
{
struct vbva_modehint * hintsi , * hintsj ;
bool valid = true ;
u16 currentx = 0 ;
int i , j ;
for ( i = 0 ; i < vbox - > num_crtcs ; + + i ) {
for ( j = 0 ; j < i ; + + j ) {
hintsi = & vbox - > last_mode_hints [ i ] ;
hintsj = & vbox - > last_mode_hints [ j ] ;
if ( hintsi - > enabled & & hintsj - > enabled ) {
if ( hintsi - > dx > = 0xffff | |
hintsi - > dy > = 0xffff | |
hintsj - > dx > = 0xffff | |
hintsj - > dy > = 0xffff | |
( hintsi - > dx <
hintsj - > dx + ( hintsj - > cx & 0x8fff ) & &
hintsi - > dx + ( hintsi - > cx & 0x8fff ) >
hintsj - > dx ) | |
( hintsi - > dy <
hintsj - > dy + ( hintsj - > cy & 0x8fff ) & &
hintsi - > dy + ( hintsi - > cy & 0x8fff ) >
hintsj - > dy ) )
valid = false ;
}
}
}
if ( ! valid )
for ( i = 0 ; i < vbox - > num_crtcs ; + + i ) {
if ( vbox - > last_mode_hints [ i ] . enabled ) {
vbox - > last_mode_hints [ i ] . dx = currentx ;
vbox - > last_mode_hints [ i ] . dy = 0 ;
currentx + =
vbox - > last_mode_hints [ i ] . cx & 0x8fff ;
}
}
}
2018-10-18 17:03:31 +02:00
/* Query the host for the most recent video mode hints. */
2017-07-06 16:06:01 +02:00
static void vbox_update_mode_hints ( struct vbox_private * vbox )
{
2019-03-04 17:47:23 +01:00
struct drm_connector_list_iter conn_iter ;
2018-09-18 19:44:30 +02:00
struct drm_device * dev = & vbox - > ddev ;
2017-07-06 16:06:01 +02:00
struct drm_connector * connector ;
struct vbox_connector * vbox_conn ;
struct vbva_modehint * hints ;
u16 flags ;
bool disconnected ;
unsigned int crtc_id ;
int ret ;
ret = hgsmi_get_mode_hints ( vbox - > guest_pool , vbox - > num_crtcs ,
vbox - > last_mode_hints ) ;
if ( ret ) {
DRM_ERROR ( " vboxvideo: hgsmi_get_mode_hints failed: %d \n " , ret ) ;
return ;
}
validate_or_set_position_hints ( vbox ) ;
2019-03-04 17:47:23 +01:00
drm_modeset_lock ( & dev - > mode_config . connection_mutex , NULL ) ;
drm_connector_list_iter_begin ( dev , & conn_iter ) ;
drm_for_each_connector_iter ( connector , & conn_iter ) {
2017-07-06 16:06:01 +02:00
vbox_conn = to_vbox_connector ( connector ) ;
hints = & vbox - > last_mode_hints [ vbox_conn - > vbox_crtc - > crtc_id ] ;
if ( hints - > magic ! = VBVAMODEHINT_MAGIC )
continue ;
disconnected = ! ( hints - > enabled ) ;
crtc_id = vbox_conn - > vbox_crtc - > crtc_id ;
2017-10-12 20:10:25 +02:00
vbox_conn - > mode_hint . width = hints - > cx ;
vbox_conn - > mode_hint . height = hints - > cy ;
2017-07-06 16:06:01 +02:00
vbox_conn - > vbox_crtc - > x_hint = hints - > dx ;
vbox_conn - > vbox_crtc - > y_hint = hints - > dy ;
vbox_conn - > mode_hint . disconnected = disconnected ;
if ( vbox_conn - > vbox_crtc - > disconnected = = disconnected )
continue ;
if ( disconnected )
flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED ;
else
flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK ;
hgsmi_process_display_info ( vbox - > guest_pool , crtc_id , 0 , 0 , 0 ,
hints - > cx * 4 , hints - > cx ,
hints - > cy , 0 , flags ) ;
vbox_conn - > vbox_crtc - > disconnected = disconnected ;
}
2019-03-04 17:47:23 +01:00
drm_connector_list_iter_end ( & conn_iter ) ;
drm_modeset_unlock ( & dev - > mode_config . connection_mutex ) ;
2017-07-06 16:06:01 +02:00
}
static void vbox_hotplug_worker ( struct work_struct * work )
{
struct vbox_private * vbox = container_of ( work , struct vbox_private ,
hotplug_work ) ;
vbox_update_mode_hints ( vbox ) ;
2018-09-18 19:44:30 +02:00
drm_kms_helper_hotplug_event ( & vbox - > ddev ) ;
2017-07-06 16:06:01 +02:00
}
int vbox_irq_init ( struct vbox_private * vbox )
{
INIT_WORK ( & vbox - > hotplug_work , vbox_hotplug_worker ) ;
vbox_update_mode_hints ( vbox ) ;
2018-09-18 19:44:30 +02:00
return drm_irq_install ( & vbox - > ddev , vbox - > ddev . pdev - > irq ) ;
2017-07-06 16:06:01 +02:00
}
void vbox_irq_fini ( struct vbox_private * vbox )
{
2018-09-18 19:44:30 +02:00
drm_irq_uninstall ( & vbox - > ddev ) ;
2017-07-06 16:06:01 +02:00
flush_work ( & vbox - > hotplug_work ) ;
}