2018-10-18 17:03:32 +02:00
// SPDX-License-Identifier: MIT
2017-07-06 16:06:01 +02:00
/*
* Copyright ( C ) 2013 - 2017 Oracle Corporation
* This file is based on ast_mode . c
* Copyright 2012 Red Hat Inc .
* Parts based on xf86 - video - ast
* Copyright ( c ) 2005 ASPEED Technology Inc .
* Authors : Dave Airlie < airlied @ redhat . com >
* Michael Thayer < michael . thayer @ oracle . com ,
* Hans de Goede < hdegoede @ redhat . com >
*/
# include <linux/export.h>
2019-01-26 13:25:23 +01:00
2018-09-29 14:18:20 +02:00
# include <drm/drm_atomic.h>
2018-09-29 14:18:15 +02:00
# include <drm/drm_atomic_helper.h>
2019-01-26 13:25:23 +01:00
# include <drm/drm_fourcc.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_plane_helper.h>
# include <drm/drm_probe_helper.h>
2019-01-26 13:25:23 +01:00
# include <drm/drm_vblank.h>
2017-07-06 16:06:01 +02:00
2019-01-26 13:25:23 +01:00
# include "hgsmi_channels.h"
2017-07-06 16:06:01 +02:00
# include "vbox_drv.h"
# include "vboxvideo.h"
2018-10-18 17:03:31 +02:00
/*
2017-07-06 16:06:01 +02:00
* Set a graphics mode . Poke any required values into registers , do an HGSMI
* mode set and tell the host we support advanced graphics functions .
*/
2018-09-29 14:18:14 +02:00
static void vbox_do_modeset ( struct drm_crtc * crtc )
2017-07-06 16:06:01 +02:00
{
2018-09-29 14:18:18 +02:00
struct drm_framebuffer * fb = crtc - > primary - > state - > fb ;
2017-07-06 16:06:01 +02:00
struct vbox_crtc * vbox_crtc = to_vbox_crtc ( crtc ) ;
struct vbox_private * vbox ;
int width , height , bpp , pitch ;
u16 flags ;
s32 x_offset , y_offset ;
vbox = crtc - > dev - > dev_private ;
2018-09-29 14:18:14 +02:00
width = vbox_crtc - > width ? vbox_crtc - > width : 640 ;
height = vbox_crtc - > height ? vbox_crtc - > height : 480 ;
2018-09-29 14:18:18 +02:00
bpp = fb ? fb - > format - > cpp [ 0 ] * 8 : 32 ;
pitch = fb ? fb - > pitches [ 0 ] : width * bpp / 8 ;
2018-09-29 14:18:14 +02:00
x_offset = vbox - > single_framebuffer ? vbox_crtc - > x : vbox_crtc - > x_hint ;
y_offset = vbox - > single_framebuffer ? vbox_crtc - > y : vbox_crtc - > y_hint ;
2017-07-06 16:06:01 +02:00
/*
* This is the old way of setting graphics modes . It assumed one screen
* and a frame - buffer at the start of video RAM . On older versions of
* VirtualBox , certain parts of the code still assume that the first
* screen is programmed this way , so try to fake it .
*/
2018-09-29 14:18:18 +02:00
if ( vbox_crtc - > crtc_id = = 0 & & fb & &
2017-07-06 16:06:01 +02:00
vbox_crtc - > fb_offset / pitch < 0xffff - crtc - > y & &
vbox_crtc - > fb_offset % ( bpp / 8 ) = = 0 ) {
vbox_write_ioport ( VBE_DISPI_INDEX_XRES , width ) ;
vbox_write_ioport ( VBE_DISPI_INDEX_YRES , height ) ;
vbox_write_ioport ( VBE_DISPI_INDEX_VIRT_WIDTH , pitch * 8 / bpp ) ;
2018-09-29 14:18:18 +02:00
vbox_write_ioport ( VBE_DISPI_INDEX_BPP , bpp ) ;
2017-07-06 16:06:01 +02:00
vbox_write_ioport ( VBE_DISPI_INDEX_ENABLE , VBE_DISPI_ENABLED ) ;
2019-03-05 17:24:49 +02:00
vbox_write_ioport ( VBE_DISPI_INDEX_X_OFFSET ,
2018-09-29 14:18:14 +02:00
vbox_crtc - > fb_offset % pitch / bpp * 8 + vbox_crtc - > x ) ;
2017-07-06 16:06:01 +02:00
vbox_write_ioport ( VBE_DISPI_INDEX_Y_OFFSET ,
2018-09-29 14:18:14 +02:00
vbox_crtc - > fb_offset / pitch + vbox_crtc - > y ) ;
2017-07-06 16:06:01 +02:00
}
flags = VBVA_SCREEN_F_ACTIVE ;
2018-10-18 17:03:28 +02:00
flags | = ( fb & & crtc - > state - > enable ) ? 0 : VBVA_SCREEN_F_BLANK ;
2017-07-06 16:06:01 +02:00
flags | = vbox_crtc - > disconnected ? VBVA_SCREEN_F_DISABLED : 0 ;
hgsmi_process_display_info ( vbox - > guest_pool , vbox_crtc - > crtc_id ,
x_offset , y_offset ,
2018-09-29 14:18:14 +02:00
vbox_crtc - > x * bpp / 8 +
vbox_crtc - > y * pitch ,
2018-09-29 14:18:21 +02:00
pitch , width , height , bpp , flags ) ;
2017-07-06 16:06:01 +02:00
}
static int vbox_set_view ( struct drm_crtc * crtc )
{
struct vbox_crtc * vbox_crtc = to_vbox_crtc ( crtc ) ;
struct vbox_private * vbox = crtc - > dev - > dev_private ;
struct vbva_infoview * p ;
/*
* Tell the host about the view . This design originally targeted the
* Windows XP driver architecture and assumed that each screen would
* have a dedicated frame buffer with the command buffer following it ,
* the whole being a " view " . The host works out which screen a command
* buffer belongs to by checking whether it is in the first view , then
* whether it is in the second and so on . The first match wins . We
* cheat around this by making the first view be the managed memory
* plus the first command buffer , the second the same plus the second
* buffer and so on .
*/
p = hgsmi_buffer_alloc ( vbox - > guest_pool , sizeof ( * p ) ,
HGSMI_CH_VBVA , VBVA_INFO_VIEW ) ;
if ( ! p )
return - ENOMEM ;
p - > view_index = vbox_crtc - > crtc_id ;
p - > view_offset = vbox_crtc - > fb_offset ;
p - > view_size = vbox - > available_vram_size - vbox_crtc - > fb_offset +
vbox_crtc - > crtc_id * VBVA_MIN_BUFFER_SIZE ;
p - > max_screen_size = vbox - > available_vram_size - vbox_crtc - > fb_offset ;
hgsmi_buffer_submit ( vbox - > guest_pool , p ) ;
hgsmi_buffer_free ( vbox - > guest_pool , p ) ;
return 0 ;
}
/*
* Try to map the layout of virtual screens to the range of the input device .
* Return true if we need to re - set the crtc modes due to screen offset
* changes .
*/
static bool vbox_set_up_input_mapping ( struct vbox_private * vbox )
{
struct drm_crtc * crtci ;
struct drm_connector * connectori ;
2018-09-29 14:18:11 +02:00
struct drm_framebuffer * fb , * fb1 = NULL ;
2017-07-06 16:06:01 +02:00
bool single_framebuffer = true ;
bool old_single_framebuffer = vbox - > single_framebuffer ;
u16 width = 0 , height = 0 ;
/*
* Are we using an X . Org - style single large frame - buffer for all crtcs ?
* If so then screen layout can be deduced from the crtc offsets .
* Same fall - back if this is the fbdev frame - buffer .
*/
2018-09-18 19:44:30 +02:00
list_for_each_entry ( crtci , & vbox - > ddev . mode_config . crtc_list , head ) {
2018-09-29 14:18:18 +02:00
fb = crtci - > primary - > state - > fb ;
2018-09-29 14:18:11 +02:00
if ( ! fb )
continue ;
2017-07-06 16:06:01 +02:00
if ( ! fb1 ) {
2018-09-29 14:18:11 +02:00
fb1 = fb ;
2018-09-29 14:18:25 +02:00
if ( to_vbox_framebuffer ( fb1 ) = = & vbox - > afb )
2017-07-06 16:06:01 +02:00
break ;
2018-09-29 14:18:11 +02:00
} else if ( fb ! = fb1 ) {
2017-07-06 16:06:01 +02:00
single_framebuffer = false ;
}
}
2018-09-29 14:18:11 +02:00
if ( ! fb1 )
return false ;
2017-07-06 16:06:01 +02:00
if ( single_framebuffer ) {
2018-09-18 19:44:36 +02:00
vbox - > single_framebuffer = true ;
2018-09-29 14:18:11 +02:00
vbox - > input_mapping_width = fb1 - > width ;
vbox - > input_mapping_height = fb1 - > height ;
2018-09-18 19:44:36 +02:00
return old_single_framebuffer ! = vbox - > single_framebuffer ;
2017-07-06 16:06:01 +02:00
}
/* Otherwise calculate the total span of all screens. */
2018-09-18 19:44:30 +02:00
list_for_each_entry ( connectori , & vbox - > ddev . mode_config . connector_list ,
2017-07-06 16:06:01 +02:00
head ) {
struct vbox_connector * vbox_connector =
to_vbox_connector ( connectori ) ;
struct vbox_crtc * vbox_crtc = vbox_connector - > vbox_crtc ;
width = max_t ( u16 , width , vbox_crtc - > x_hint +
vbox_connector - > mode_hint . width ) ;
height = max_t ( u16 , height , vbox_crtc - > y_hint +
vbox_connector - > mode_hint . height ) ;
}
vbox - > single_framebuffer = false ;
vbox - > input_mapping_width = width ;
vbox - > input_mapping_height = height ;
return old_single_framebuffer ! = vbox - > single_framebuffer ;
}
2018-09-18 19:44:35 +02:00
static void vbox_crtc_set_base_and_mode ( struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
int x , int y )
2018-09-11 09:15:41 +02:00
{
2019-05-08 10:26:27 +02:00
struct drm_gem_vram_object * gbo =
drm_gem_vram_of_gem ( to_vbox_framebuffer ( fb ) - > obj ) ;
2018-09-11 09:15:41 +02:00
struct vbox_private * vbox = crtc - > dev - > dev_private ;
struct vbox_crtc * vbox_crtc = to_vbox_crtc ( crtc ) ;
2018-09-29 14:18:20 +02:00
bool needs_modeset = drm_atomic_crtc_needs_modeset ( crtc - > state ) ;
2017-07-06 16:06:01 +02:00
2018-09-11 09:15:41 +02:00
mutex_lock ( & vbox - > hw_mutex ) ;
2018-10-18 17:03:29 +02:00
if ( crtc - > state - > enable ) {
vbox_crtc - > width = crtc - > state - > mode . hdisplay ;
vbox_crtc - > height = crtc - > state - > mode . vdisplay ;
}
2018-09-29 14:18:14 +02:00
vbox_crtc - > x = x ;
vbox_crtc - > y = y ;
2019-05-08 10:26:27 +02:00
vbox_crtc - > fb_offset = drm_gem_vram_offset ( gbo ) ;
2018-09-11 09:15:41 +02:00
/* vbox_do_modeset() checks vbox->single_framebuffer so update it now */
2018-09-29 14:18:20 +02:00
if ( needs_modeset & & vbox_set_up_input_mapping ( vbox ) ) {
2017-07-06 16:06:01 +02:00
struct drm_crtc * crtci ;
2018-09-18 19:44:30 +02:00
list_for_each_entry ( crtci , & vbox - > ddev . mode_config . crtc_list ,
2017-07-06 16:06:01 +02:00
head ) {
2018-09-11 09:15:42 +02:00
if ( crtci = = crtc )
continue ;
2018-09-29 14:18:14 +02:00
vbox_do_modeset ( crtci ) ;
2017-07-06 16:06:01 +02:00
}
}
2018-09-11 09:15:41 +02:00
vbox_set_view ( crtc ) ;
2018-09-29 14:18:14 +02:00
vbox_do_modeset ( crtc ) ;
2017-07-06 16:06:01 +02:00
2018-09-29 14:18:20 +02:00
if ( needs_modeset )
2018-09-11 09:15:41 +02:00
hgsmi_update_input_mapping ( vbox - > guest_pool , 0 , 0 ,
vbox - > input_mapping_width ,
vbox - > input_mapping_height ) ;
mutex_unlock ( & vbox - > hw_mutex ) ;
2017-07-06 16:06:01 +02:00
}
2018-09-29 14:18:22 +02:00
static void vbox_crtc_atomic_enable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2017-07-06 16:06:01 +02:00
{
2018-09-29 14:18:16 +02:00
}
2018-09-18 19:44:35 +02:00
2018-09-29 14:18:22 +02:00
static void vbox_crtc_atomic_disable ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2017-07-06 16:06:01 +02:00
{
}
2018-09-29 14:18:16 +02:00
static void vbox_crtc_atomic_flush ( struct drm_crtc * crtc ,
struct drm_crtc_state * old_crtc_state )
2017-07-06 16:06:01 +02:00
{
2018-09-29 14:18:16 +02:00
struct drm_pending_vblank_event * event ;
unsigned long flags ;
if ( crtc - > state & & crtc - > state - > event ) {
event = crtc - > state - > event ;
crtc - > state - > event = NULL ;
spin_lock_irqsave ( & crtc - > dev - > event_lock , flags ) ;
drm_crtc_send_vblank_event ( crtc , event ) ;
spin_unlock_irqrestore ( & crtc - > dev - > event_lock , flags ) ;
}
2017-07-06 16:06:01 +02:00
}
static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = {
2018-09-29 14:18:22 +02:00
. atomic_enable = vbox_crtc_atomic_enable ,
. atomic_disable = vbox_crtc_atomic_disable ,
2018-09-29 14:18:16 +02:00
. atomic_flush = vbox_crtc_atomic_flush ,
2017-07-06 16:06:01 +02:00
} ;
static void vbox_crtc_destroy ( struct drm_crtc * crtc )
{
drm_crtc_cleanup ( crtc ) ;
kfree ( crtc ) ;
}
static const struct drm_crtc_funcs vbox_crtc_funcs = {
2018-09-29 14:18:19 +02:00
. set_config = drm_atomic_helper_set_config ,
2018-09-29 14:18:20 +02:00
. page_flip = drm_atomic_helper_page_flip ,
2017-07-06 16:06:01 +02:00
/* .gamma_set = vbox_crtc_gamma_set, */
. destroy = vbox_crtc_destroy ,
2018-09-29 14:18:17 +02:00
. reset = drm_atomic_helper_crtc_reset ,
. atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_crtc_destroy_state ,
2017-07-06 16:06:01 +02:00
} ;
2018-09-29 14:18:16 +02:00
static int vbox_primary_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * new_state )
{
2018-09-29 14:18:23 +02:00
struct drm_crtc_state * crtc_state = NULL ;
if ( new_state - > crtc ) {
crtc_state = drm_atomic_get_existing_crtc_state (
new_state - > state , new_state - > crtc ) ;
if ( WARN_ON ( ! crtc_state ) )
return - EINVAL ;
}
return drm_atomic_helper_check_plane_state ( new_state , crtc_state ,
DRM_PLANE_HELPER_NO_SCALING ,
DRM_PLANE_HELPER_NO_SCALING ,
false , true ) ;
2018-09-29 14:18:16 +02:00
}
static void vbox_primary_atomic_update ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct drm_crtc * crtc = plane - > state - > crtc ;
struct drm_framebuffer * fb = plane - > state - > fb ;
2018-10-18 17:03:29 +02:00
vbox_crtc_set_base_and_mode ( crtc , fb ,
2018-09-29 14:18:16 +02:00
plane - > state - > src_x > > 16 ,
plane - > state - > src_y > > 16 ) ;
}
2018-10-10 20:17:05 +00:00
static void vbox_primary_atomic_disable ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
2018-09-29 14:18:16 +02:00
{
struct drm_crtc * crtc = old_state - > crtc ;
/* vbox_do_modeset checks plane->state->fb and will disable if NULL */
2018-10-18 17:03:29 +02:00
vbox_crtc_set_base_and_mode ( crtc , old_state - > fb ,
2018-09-29 14:18:16 +02:00
old_state - > src_x > > 16 ,
old_state - > src_y > > 16 ) ;
}
static int vbox_primary_prepare_fb ( struct drm_plane * plane ,
struct drm_plane_state * new_state )
{
2019-05-08 10:26:27 +02:00
struct drm_gem_vram_object * gbo ;
2018-09-29 14:18:16 +02:00
int ret ;
if ( ! new_state - > fb )
return 0 ;
2019-05-08 10:26:27 +02:00
gbo = drm_gem_vram_of_gem ( to_vbox_framebuffer ( new_state - > fb ) - > obj ) ;
ret = drm_gem_vram_pin ( gbo , DRM_GEM_VRAM_PL_FLAG_VRAM ) ;
2018-09-29 14:18:16 +02:00
if ( ret )
DRM_WARN ( " Error %d pinning new fb, out of video mem? \n " , ret ) ;
return ret ;
}
static void vbox_primary_cleanup_fb ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
2019-05-08 10:26:27 +02:00
struct drm_gem_vram_object * gbo ;
2018-09-29 14:18:16 +02:00
if ( ! old_state - > fb )
return ;
2019-05-08 10:26:27 +02:00
gbo = drm_gem_vram_of_gem ( to_vbox_framebuffer ( old_state - > fb ) - > obj ) ;
drm_gem_vram_unpin ( gbo ) ;
2018-09-29 14:18:16 +02:00
}
2018-09-29 14:18:15 +02:00
static int vbox_cursor_atomic_check ( struct drm_plane * plane ,
struct drm_plane_state * new_state )
{
2018-09-29 14:18:23 +02:00
struct drm_crtc_state * crtc_state = NULL ;
2018-09-29 14:18:15 +02:00
u32 width = new_state - > crtc_w ;
u32 height = new_state - > crtc_h ;
2018-09-29 14:18:23 +02:00
int ret ;
if ( new_state - > crtc ) {
crtc_state = drm_atomic_get_existing_crtc_state (
new_state - > state , new_state - > crtc ) ;
if ( WARN_ON ( ! crtc_state ) )
return - EINVAL ;
}
ret = drm_atomic_helper_check_plane_state ( new_state , crtc_state ,
DRM_PLANE_HELPER_NO_SCALING ,
DRM_PLANE_HELPER_NO_SCALING ,
true , true ) ;
if ( ret )
return ret ;
2018-09-29 14:18:15 +02:00
if ( ! new_state - > fb )
return 0 ;
if ( width > VBOX_MAX_CURSOR_WIDTH | | height > VBOX_MAX_CURSOR_HEIGHT | |
width = = 0 | | height = = 0 )
return - EINVAL ;
return 0 ;
}
2018-10-18 17:03:31 +02:00
/*
2018-09-29 14:18:15 +02:00
* Copy the ARGB image and generate the mask , which is needed in case the host
* does not support ARGB cursors . The mask is a 1 BPP bitmap with the bit set
* if the corresponding alpha value in the ARGB image is greater than 0xF0 .
*/
static void copy_cursor_image ( u8 * src , u8 * dst , u32 width , u32 height ,
size_t mask_size )
{
size_t line_size = ( width + 7 ) / 8 ;
u32 i , j ;
memcpy ( dst + mask_size , src , width * height * 4 ) ;
for ( i = 0 ; i < height ; + + i )
for ( j = 0 ; j < width ; + + j )
if ( ( ( u32 * ) src ) [ i * width + j ] > 0xf0000000 )
dst [ i * line_size + j / 8 ] | = ( 0x80 > > ( j % 8 ) ) ;
}
static void vbox_cursor_atomic_update ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
struct vbox_private * vbox =
container_of ( plane - > dev , struct vbox_private , ddev ) ;
struct vbox_crtc * vbox_crtc = to_vbox_crtc ( plane - > state - > crtc ) ;
struct drm_framebuffer * fb = plane - > state - > fb ;
2019-05-08 10:26:27 +02:00
struct drm_gem_vram_object * gbo =
drm_gem_vram_of_gem ( to_vbox_framebuffer ( fb ) - > obj ) ;
2018-09-29 14:18:15 +02:00
u32 width = plane - > state - > crtc_w ;
u32 height = plane - > state - > crtc_h ;
size_t data_size , mask_size ;
u32 flags ;
u8 * src ;
/*
* VirtualBox uses the host windowing system to draw the cursor so
* moves are a no - op , we only need to upload new cursor sprites .
*/
if ( fb = = old_state - > fb )
return ;
mutex_lock ( & vbox - > hw_mutex ) ;
vbox_crtc - > cursor_enabled = true ;
/* pinning is done in prepare/cleanup framebuffer */
2019-05-08 10:26:27 +02:00
src = drm_gem_vram_kmap ( gbo , true , NULL ) ;
2018-09-29 14:18:15 +02:00
if ( IS_ERR ( src ) ) {
2018-10-11 10:59:41 +03:00
mutex_unlock ( & vbox - > hw_mutex ) ;
2018-09-29 14:18:15 +02:00
DRM_WARN ( " Could not kmap cursor bo, skipping update \n " ) ;
return ;
}
/*
* The mask must be calculated based on the alpha
* channel , one bit per ARGB word , and must be 32 - bit
* padded .
*/
mask_size = ( ( width + 7 ) / 8 * height + 3 ) & ~ 3 ;
data_size = width * height * 4 + mask_size ;
copy_cursor_image ( src , vbox - > cursor_data , width , height , mask_size ) ;
2019-05-08 10:26:27 +02:00
drm_gem_vram_kunmap ( gbo ) ;
2018-09-29 14:18:15 +02:00
flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE |
VBOX_MOUSE_POINTER_ALPHA ;
hgsmi_update_pointer_shape ( vbox - > guest_pool , flags ,
min_t ( u32 , max ( fb - > hot_x , 0 ) , width ) ,
min_t ( u32 , max ( fb - > hot_y , 0 ) , height ) ,
width , height , vbox - > cursor_data , data_size ) ;
mutex_unlock ( & vbox - > hw_mutex ) ;
}
2018-10-10 20:17:05 +00:00
static void vbox_cursor_atomic_disable ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
2018-09-29 14:18:15 +02:00
{
struct vbox_private * vbox =
container_of ( plane - > dev , struct vbox_private , ddev ) ;
struct vbox_crtc * vbox_crtc = to_vbox_crtc ( old_state - > crtc ) ;
bool cursor_enabled = false ;
struct drm_crtc * crtci ;
mutex_lock ( & vbox - > hw_mutex ) ;
vbox_crtc - > cursor_enabled = false ;
list_for_each_entry ( crtci , & vbox - > ddev . mode_config . crtc_list , head ) {
if ( to_vbox_crtc ( crtci ) - > cursor_enabled )
cursor_enabled = true ;
}
if ( ! cursor_enabled )
hgsmi_update_pointer_shape ( vbox - > guest_pool , 0 , 0 , 0 ,
0 , 0 , NULL , 0 ) ;
mutex_unlock ( & vbox - > hw_mutex ) ;
}
static int vbox_cursor_prepare_fb ( struct drm_plane * plane ,
struct drm_plane_state * new_state )
{
2019-05-08 10:26:27 +02:00
struct drm_gem_vram_object * gbo ;
2018-09-29 14:18:15 +02:00
if ( ! new_state - > fb )
return 0 ;
2019-05-08 10:26:27 +02:00
gbo = drm_gem_vram_of_gem ( to_vbox_framebuffer ( new_state - > fb ) - > obj ) ;
return drm_gem_vram_pin ( gbo , DRM_GEM_VRAM_PL_FLAG_SYSTEM ) ;
2018-09-29 14:18:15 +02:00
}
static void vbox_cursor_cleanup_fb ( struct drm_plane * plane ,
struct drm_plane_state * old_state )
{
2019-05-08 10:26:27 +02:00
struct drm_gem_vram_object * gbo ;
2018-09-29 14:18:15 +02:00
if ( ! plane - > state - > fb )
return ;
2019-05-08 10:26:27 +02:00
gbo = drm_gem_vram_of_gem ( to_vbox_framebuffer ( plane - > state - > fb ) - > obj ) ;
drm_gem_vram_unpin ( gbo ) ;
2018-09-29 14:18:15 +02:00
}
2018-11-22 02:49:05 -08:00
static const u32 vbox_cursor_plane_formats [ ] = {
2018-09-29 14:18:15 +02:00
DRM_FORMAT_ARGB8888 ,
} ;
static const struct drm_plane_helper_funcs vbox_cursor_helper_funcs = {
. atomic_check = vbox_cursor_atomic_check ,
. atomic_update = vbox_cursor_atomic_update ,
. atomic_disable = vbox_cursor_atomic_disable ,
. prepare_fb = vbox_cursor_prepare_fb ,
. cleanup_fb = vbox_cursor_cleanup_fb ,
} ;
static const struct drm_plane_funcs vbox_cursor_plane_funcs = {
2018-09-29 14:18:19 +02:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2018-09-29 14:18:15 +02:00
. destroy = drm_primary_helper_destroy ,
2018-09-29 14:18:17 +02:00
. reset = drm_atomic_helper_plane_reset ,
. atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_plane_destroy_state ,
2018-09-29 14:18:15 +02:00
} ;
2018-11-22 02:49:05 -08:00
static const u32 vbox_primary_plane_formats [ ] = {
2018-09-18 19:44:33 +02:00
DRM_FORMAT_XRGB8888 ,
DRM_FORMAT_ARGB8888 ,
} ;
2018-09-29 14:18:16 +02:00
static const struct drm_plane_helper_funcs vbox_primary_helper_funcs = {
. atomic_check = vbox_primary_atomic_check ,
. atomic_update = vbox_primary_atomic_update ,
. atomic_disable = vbox_primary_atomic_disable ,
. prepare_fb = vbox_primary_prepare_fb ,
. cleanup_fb = vbox_primary_cleanup_fb ,
} ;
2018-09-18 19:44:33 +02:00
static const struct drm_plane_funcs vbox_primary_plane_funcs = {
2018-09-29 14:18:19 +02:00
. update_plane = drm_atomic_helper_update_plane ,
. disable_plane = drm_atomic_helper_disable_plane ,
2018-09-18 19:44:33 +02:00
. destroy = drm_primary_helper_destroy ,
2018-09-29 14:18:17 +02:00
. reset = drm_atomic_helper_plane_reset ,
. atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_plane_destroy_state ,
2018-09-18 19:44:33 +02:00
} ;
static struct drm_plane * vbox_create_plane ( struct vbox_private * vbox ,
unsigned int possible_crtcs ,
enum drm_plane_type type )
{
const struct drm_plane_helper_funcs * helper_funcs = NULL ;
const struct drm_plane_funcs * funcs ;
struct drm_plane * plane ;
2018-11-22 02:49:05 -08:00
const u32 * formats ;
2018-09-18 19:44:33 +02:00
int num_formats ;
int err ;
if ( type = = DRM_PLANE_TYPE_PRIMARY ) {
funcs = & vbox_primary_plane_funcs ;
formats = vbox_primary_plane_formats ;
2018-09-29 14:18:16 +02:00
helper_funcs = & vbox_primary_helper_funcs ;
2018-09-18 19:44:33 +02:00
num_formats = ARRAY_SIZE ( vbox_primary_plane_formats ) ;
2018-09-29 14:18:15 +02:00
} else if ( type = = DRM_PLANE_TYPE_CURSOR ) {
funcs = & vbox_cursor_plane_funcs ;
formats = vbox_cursor_plane_formats ;
helper_funcs = & vbox_cursor_helper_funcs ;
num_formats = ARRAY_SIZE ( vbox_cursor_plane_formats ) ;
2018-09-18 19:44:33 +02:00
} else {
return ERR_PTR ( - EINVAL ) ;
}
plane = kzalloc ( sizeof ( * plane ) , GFP_KERNEL ) ;
if ( ! plane )
return ERR_PTR ( - ENOMEM ) ;
err = drm_universal_plane_init ( & vbox - > ddev , plane , possible_crtcs ,
funcs , formats , num_formats ,
NULL , type , NULL ) ;
if ( err )
goto free_plane ;
drm_plane_helper_add ( plane , helper_funcs ) ;
return plane ;
free_plane :
kfree ( plane ) ;
return ERR_PTR ( - EINVAL ) ;
}
2017-07-06 16:06:01 +02:00
static struct vbox_crtc * vbox_crtc_init ( struct drm_device * dev , unsigned int i )
{
2018-09-18 19:44:33 +02:00
struct vbox_private * vbox =
container_of ( dev , struct vbox_private , ddev ) ;
2018-09-29 14:18:15 +02:00
struct drm_plane * cursor = NULL ;
2017-07-06 16:06:01 +02:00
struct vbox_crtc * vbox_crtc ;
2018-09-18 19:44:33 +02:00
struct drm_plane * primary ;
2018-09-29 14:18:15 +02:00
u32 caps = 0 ;
2018-09-18 19:44:33 +02:00
int ret ;
2017-07-06 16:06:01 +02:00
2018-09-29 14:18:15 +02:00
ret = hgsmi_query_conf ( vbox - > guest_pool ,
VBOX_VBVA_CONF32_CURSOR_CAPABILITIES , & caps ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2017-07-06 16:06:01 +02:00
vbox_crtc = kzalloc ( sizeof ( * vbox_crtc ) , GFP_KERNEL ) ;
if ( ! vbox_crtc )
2018-09-18 19:44:33 +02:00
return ERR_PTR ( - ENOMEM ) ;
primary = vbox_create_plane ( vbox , 1 < < i , DRM_PLANE_TYPE_PRIMARY ) ;
if ( IS_ERR ( primary ) ) {
ret = PTR_ERR ( primary ) ;
goto free_mem ;
}
2017-07-06 16:06:01 +02:00
2018-09-29 14:18:15 +02:00
if ( ( caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE ) ) {
cursor = vbox_create_plane ( vbox , 1 < < i , DRM_PLANE_TYPE_CURSOR ) ;
if ( IS_ERR ( cursor ) ) {
ret = PTR_ERR ( cursor ) ;
goto clean_primary ;
}
} else {
DRM_WARN ( " VirtualBox host is too old, no cursor support \n " ) ;
}
2017-07-06 16:06:01 +02:00
vbox_crtc - > crtc_id = i ;
2018-09-29 14:18:15 +02:00
ret = drm_crtc_init_with_planes ( dev , & vbox_crtc - > base , primary , cursor ,
2018-09-18 19:44:33 +02:00
& vbox_crtc_funcs , NULL ) ;
if ( ret )
2018-09-29 14:18:15 +02:00
goto clean_cursor ;
2018-09-18 19:44:33 +02:00
2017-07-06 16:06:01 +02:00
drm_mode_crtc_set_gamma_size ( & vbox_crtc - > base , 256 ) ;
drm_crtc_helper_add ( & vbox_crtc - > base , & vbox_crtc_helper_funcs ) ;
return vbox_crtc ;
2018-09-18 19:44:33 +02:00
2018-09-29 14:18:15 +02:00
clean_cursor :
if ( cursor ) {
drm_plane_cleanup ( cursor ) ;
kfree ( cursor ) ;
}
2018-09-18 19:44:33 +02:00
clean_primary :
drm_plane_cleanup ( primary ) ;
kfree ( primary ) ;
free_mem :
kfree ( vbox_crtc ) ;
return ERR_PTR ( ret ) ;
2017-07-06 16:06:01 +02:00
}
static void vbox_encoder_destroy ( struct drm_encoder * encoder )
{
drm_encoder_cleanup ( encoder ) ;
kfree ( encoder ) ;
}
static const struct drm_encoder_funcs vbox_enc_funcs = {
. destroy = vbox_encoder_destroy ,
} ;
static struct drm_encoder * vbox_encoder_init ( struct drm_device * dev ,
unsigned int i )
{
struct vbox_encoder * vbox_encoder ;
vbox_encoder = kzalloc ( sizeof ( * vbox_encoder ) , GFP_KERNEL ) ;
if ( ! vbox_encoder )
return NULL ;
drm_encoder_init ( dev , & vbox_encoder - > base , & vbox_enc_funcs ,
DRM_MODE_ENCODER_DAC , NULL ) ;
vbox_encoder - > base . possible_crtcs = 1 < < i ;
return & vbox_encoder - > base ;
}
2018-10-18 17:03:31 +02:00
/*
2017-07-06 16:06:01 +02:00
* Generate EDID data with a mode - unique serial number for the virtual
2018-10-18 17:03:31 +02:00
* monitor to try to persuade Unity that different modes correspond to
* different monitors and it should not try to force the same resolution on
* them .
2017-07-06 16:06:01 +02:00
*/
static void vbox_set_edid ( struct drm_connector * connector , int width ,
int height )
{
enum { EDID_SIZE = 128 } ;
unsigned char edid [ EDID_SIZE ] = {
0x00 , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0x00 , /* header */
0x58 , 0x58 , /* manufacturer (VBX) */
0x00 , 0x00 , /* product code */
0x00 , 0x00 , 0x00 , 0x00 , /* serial number goes here */
0x01 , /* week of manufacture */
0x00 , /* year of manufacture */
0x01 , 0x03 , /* EDID version */
0x80 , /* capabilities - digital */
0x00 , /* horiz. res in cm, zero for projectors */
0x00 , /* vert. res in cm */
0x78 , /* display gamma (120 == 2.2). */
0xEE , /* features (standby, suspend, off, RGB, std */
/* colour space, preferred timing mode) */
0xEE , 0x91 , 0xA3 , 0x54 , 0x4C , 0x99 , 0x26 , 0x0F , 0x50 , 0x54 ,
/* chromaticity for standard colour space. */
0x00 , 0x00 , 0x00 , /* no default timings */
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , /* no standard timings */
0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x06 , 0x00 , 0x02 , 0x02 ,
0x02 , 0x02 ,
/* descriptor block 1 goes below */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
/* descriptor block 2, monitor ranges */
0x00 , 0x00 , 0x00 , 0xFD , 0x00 ,
0x00 , 0xC8 , 0x00 , 0xC8 , 0x64 , 0x00 , 0x0A , 0x20 , 0x20 , 0x20 ,
0x20 , 0x20 ,
/* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
0x20 ,
/* descriptor block 3, monitor name */
0x00 , 0x00 , 0x00 , 0xFC , 0x00 ,
' V ' , ' B ' , ' O ' , ' X ' , ' ' , ' m ' , ' o ' , ' n ' , ' i ' , ' t ' , ' o ' , ' r ' ,
' \n ' ,
/* descriptor block 4: dummy data */
0x00 , 0x00 , 0x00 , 0x10 , 0x00 ,
0x0A , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 ,
0x20 , 0x20 , 0x20 , 0x20 , 0x20 , 0x20 ,
0x20 ,
0x00 , /* number of extensions */
0x00 /* checksum goes here */
} ;
int clock = ( width + 6 ) * ( height + 6 ) * 60 / 10000 ;
unsigned int i , sum = 0 ;
edid [ 12 ] = width & 0xff ;
edid [ 13 ] = width > > 8 ;
edid [ 14 ] = height & 0xff ;
edid [ 15 ] = height > > 8 ;
edid [ 54 ] = clock & 0xff ;
edid [ 55 ] = clock > > 8 ;
edid [ 56 ] = width & 0xff ;
edid [ 58 ] = ( width > > 4 ) & 0xf0 ;
edid [ 59 ] = height & 0xff ;
edid [ 61 ] = ( height > > 4 ) & 0xf0 ;
for ( i = 0 ; i < EDID_SIZE - 1 ; + + i )
sum + = edid [ i ] ;
edid [ EDID_SIZE - 1 ] = ( 0x100 - ( sum & 0xFF ) ) & 0xFF ;
2018-07-09 10:40:06 +02:00
drm_connector_update_edid_property ( connector , ( struct edid * ) edid ) ;
2017-07-06 16:06:01 +02:00
}
static int vbox_get_modes ( struct drm_connector * connector )
{
struct vbox_connector * vbox_connector = NULL ;
struct drm_display_mode * mode = NULL ;
struct vbox_private * vbox = NULL ;
unsigned int num_modes = 0 ;
int preferred_width , preferred_height ;
vbox_connector = to_vbox_connector ( connector ) ;
vbox = connector - > dev - > dev_private ;
2019-03-04 17:47:22 +01:00
2017-07-06 16:06:01 +02:00
hgsmi_report_flags_location ( vbox - > guest_pool , GUEST_HEAP_OFFSET ( vbox ) +
HOST_FLAGS_OFFSET ) ;
if ( vbox_connector - > vbox_crtc - > crtc_id = = 0 )
vbox_report_caps ( vbox ) ;
2019-03-04 17:47:22 +01:00
2017-07-06 16:06:01 +02:00
num_modes = drm_add_modes_noedid ( connector , 2560 , 1600 ) ;
preferred_width = vbox_connector - > mode_hint . width ?
vbox_connector - > mode_hint . width : 1024 ;
preferred_height = vbox_connector - > mode_hint . height ?
vbox_connector - > mode_hint . height : 768 ;
mode = drm_cvt_mode ( connector - > dev , preferred_width , preferred_height ,
60 , false , false , false ) ;
if ( mode ) {
mode - > type | = DRM_MODE_TYPE_PREFERRED ;
drm_mode_probed_add ( connector , mode ) ;
+ + num_modes ;
}
vbox_set_edid ( connector , preferred_width , preferred_height ) ;
2017-10-12 20:10:25 +02:00
if ( vbox_connector - > vbox_crtc - > x_hint ! = - 1 )
drm_object_property_set_value ( & connector - > base ,
2018-09-18 19:44:30 +02:00
vbox - > ddev . mode_config . suggested_x_property ,
2017-10-12 20:10:25 +02:00
vbox_connector - > vbox_crtc - > x_hint ) ;
else
drm_object_property_set_value ( & connector - > base ,
2018-09-18 19:44:30 +02:00
vbox - > ddev . mode_config . suggested_x_property , 0 ) ;
2017-10-12 20:10:25 +02:00
if ( vbox_connector - > vbox_crtc - > y_hint ! = - 1 )
drm_object_property_set_value ( & connector - > base ,
2018-09-18 19:44:30 +02:00
vbox - > ddev . mode_config . suggested_y_property ,
2017-10-12 20:10:25 +02:00
vbox_connector - > vbox_crtc - > y_hint ) ;
else
drm_object_property_set_value ( & connector - > base ,
2018-09-18 19:44:30 +02:00
vbox - > ddev . mode_config . suggested_y_property , 0 ) ;
2017-07-06 16:06:01 +02:00
return num_modes ;
}
static void vbox_connector_destroy ( struct drm_connector * connector )
{
drm_connector_unregister ( connector ) ;
drm_connector_cleanup ( connector ) ;
kfree ( connector ) ;
}
static enum drm_connector_status
vbox_connector_detect ( struct drm_connector * connector , bool force )
{
struct vbox_connector * vbox_connector ;
vbox_connector = to_vbox_connector ( connector ) ;
return vbox_connector - > mode_hint . disconnected ?
connector_status_disconnected : connector_status_connected ;
}
static int vbox_fill_modes ( struct drm_connector * connector , u32 max_x ,
u32 max_y )
{
struct vbox_connector * vbox_connector ;
struct drm_device * dev ;
struct drm_display_mode * mode , * iterator ;
vbox_connector = to_vbox_connector ( connector ) ;
dev = vbox_connector - > base . dev ;
list_for_each_entry_safe ( mode , iterator , & connector - > modes , head ) {
list_del ( & mode - > head ) ;
drm_mode_destroy ( dev , mode ) ;
}
return drm_helper_probe_single_connector_modes ( connector , max_x , max_y ) ;
}
static const struct drm_connector_helper_funcs vbox_connector_helper_funcs = {
. get_modes = vbox_get_modes ,
} ;
static const struct drm_connector_funcs vbox_connector_funcs = {
. detect = vbox_connector_detect ,
. fill_modes = vbox_fill_modes ,
. destroy = vbox_connector_destroy ,
2018-09-29 14:18:19 +02:00
. reset = drm_atomic_helper_connector_reset ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
2017-07-06 16:06:01 +02:00
} ;
static int vbox_connector_init ( struct drm_device * dev ,
struct vbox_crtc * vbox_crtc ,
struct drm_encoder * encoder )
{
struct vbox_connector * vbox_connector ;
struct drm_connector * connector ;
vbox_connector = kzalloc ( sizeof ( * vbox_connector ) , GFP_KERNEL ) ;
if ( ! vbox_connector )
return - ENOMEM ;
connector = & vbox_connector - > base ;
vbox_connector - > vbox_crtc = vbox_crtc ;
drm_connector_init ( dev , connector , & vbox_connector_funcs ,
DRM_MODE_CONNECTOR_VGA ) ;
drm_connector_helper_add ( connector , & vbox_connector_helper_funcs ) ;
connector - > interlace_allowed = 0 ;
connector - > doublescan_allowed = 0 ;
drm_mode_create_suggested_offset_properties ( dev ) ;
drm_object_attach_property ( & connector - > base ,
2017-10-12 20:10:25 +02:00
dev - > mode_config . suggested_x_property , 0 ) ;
2017-07-06 16:06:01 +02:00
drm_object_attach_property ( & connector - > base ,
2017-10-12 20:10:25 +02:00
dev - > mode_config . suggested_y_property , 0 ) ;
2017-07-06 16:06:01 +02:00
2018-07-09 10:40:07 +02:00
drm_connector_attach_encoder ( connector , encoder ) ;
2017-07-06 16:06:01 +02:00
return 0 ;
}
2018-09-18 19:44:28 +02:00
static struct drm_framebuffer * vbox_user_framebuffer_create (
struct drm_device * dev ,
struct drm_file * filp ,
const struct drm_mode_fb_cmd2 * mode_cmd )
{
2018-09-18 19:44:30 +02:00
struct vbox_private * vbox =
container_of ( dev , struct vbox_private , ddev ) ;
2018-09-18 19:44:28 +02:00
struct drm_gem_object * obj ;
struct vbox_framebuffer * vbox_fb ;
int ret = - ENOMEM ;
obj = drm_gem_object_lookup ( filp , mode_cmd - > handles [ 0 ] ) ;
if ( ! obj )
return ERR_PTR ( - ENOENT ) ;
vbox_fb = kzalloc ( sizeof ( * vbox_fb ) , GFP_KERNEL ) ;
if ( ! vbox_fb )
goto err_unref_obj ;
2018-09-18 19:44:30 +02:00
ret = vbox_framebuffer_init ( vbox , vbox_fb , mode_cmd , obj ) ;
2018-09-18 19:44:28 +02:00
if ( ret )
goto err_free_vbox_fb ;
return & vbox_fb - > base ;
err_free_vbox_fb :
kfree ( vbox_fb ) ;
err_unref_obj :
drm_gem_object_put_unlocked ( obj ) ;
return ERR_PTR ( ret ) ;
}
static const struct drm_mode_config_funcs vbox_mode_funcs = {
. fb_create = vbox_user_framebuffer_create ,
2018-09-29 14:18:19 +02:00
. atomic_check = drm_atomic_helper_check ,
. atomic_commit = drm_atomic_helper_commit ,
2018-09-18 19:44:28 +02:00
} ;
2018-09-18 19:44:30 +02:00
int vbox_mode_init ( struct vbox_private * vbox )
2017-07-06 16:06:01 +02:00
{
2018-09-18 19:44:30 +02:00
struct drm_device * dev = & vbox - > ddev ;
2017-07-06 16:06:01 +02:00
struct drm_encoder * encoder ;
struct vbox_crtc * vbox_crtc ;
unsigned int i ;
int ret ;
2018-09-18 19:44:28 +02:00
drm_mode_config_init ( dev ) ;
dev - > mode_config . funcs = ( void * ) & vbox_mode_funcs ;
2018-09-29 14:18:15 +02:00
dev - > mode_config . min_width = 0 ;
dev - > mode_config . min_height = 0 ;
2018-09-18 19:44:28 +02:00
dev - > mode_config . preferred_depth = 24 ;
dev - > mode_config . max_width = VBE_DISPI_MAX_XRES ;
dev - > mode_config . max_height = VBE_DISPI_MAX_YRES ;
2017-07-06 16:06:01 +02:00
for ( i = 0 ; i < vbox - > num_crtcs ; + + i ) {
vbox_crtc = vbox_crtc_init ( dev , i ) ;
2018-09-18 19:44:33 +02:00
if ( IS_ERR ( vbox_crtc ) ) {
ret = PTR_ERR ( vbox_crtc ) ;
2018-09-18 19:44:28 +02:00
goto err_drm_mode_cleanup ;
}
2017-07-06 16:06:01 +02:00
encoder = vbox_encoder_init ( dev , i ) ;
2018-09-18 19:44:28 +02:00
if ( ! encoder ) {
ret = - ENOMEM ;
goto err_drm_mode_cleanup ;
}
2017-07-06 16:06:01 +02:00
ret = vbox_connector_init ( dev , vbox_crtc , encoder ) ;
if ( ret )
2018-09-18 19:44:28 +02:00
goto err_drm_mode_cleanup ;
2017-07-06 16:06:01 +02:00
}
2018-09-29 14:18:17 +02:00
drm_mode_config_reset ( dev ) ;
2017-07-06 16:06:01 +02:00
return 0 ;
2018-09-18 19:44:28 +02:00
err_drm_mode_cleanup :
drm_mode_config_cleanup ( dev ) ;
return ret ;
2017-07-06 16:06:01 +02:00
}
2018-09-18 19:44:30 +02:00
void vbox_mode_fini ( struct vbox_private * vbox )
2017-07-06 16:06:01 +02:00
{
2018-09-18 19:44:30 +02:00
drm_mode_config_cleanup ( & vbox - > ddev ) ;
2017-07-06 16:06:01 +02:00
}