2009-12-10 03:19:58 +03:00
/**************************************************************************
*
2015-07-29 22:38:02 +03:00
* Copyright © 2009 - 2015 VMware , Inc . , Palo Alto , CA . , USA
2009-12-10 03:19:58 +03:00
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sub license , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial portions
* of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NON - INFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS , AUTHORS AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM ,
* DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR
* OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "vmwgfx_drv.h"
2012-10-02 21:01:07 +04:00
# include <drm/vmwgfx_drm.h>
2011-10-04 22:13:26 +04:00
# include "vmwgfx_kms.h"
2015-07-09 07:20:39 +03:00
# include "device_include/svga3d_caps.h"
2009-12-10 03:19:58 +03:00
2014-01-31 13:21:10 +04:00
struct svga_3d_compat_cap {
SVGA3dCapsRecordHeader header ;
SVGA3dCapPair pairs [ SVGA3D_DEVCAP_MAX ] ;
} ;
2009-12-10 03:19:58 +03:00
int vmw_getparam_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct vmw_private * dev_priv = vmw_priv ( dev ) ;
struct drm_vmw_getparam_arg * param =
( struct drm_vmw_getparam_arg * ) data ;
2014-01-31 13:21:10 +04:00
struct vmw_fpriv * vmw_fp = vmw_fpriv ( file_priv ) ;
2009-12-10 03:19:58 +03:00
switch ( param - > param ) {
case DRM_VMW_PARAM_NUM_STREAMS :
param - > value = vmw_overlay_num_overlays ( dev_priv ) ;
break ;
case DRM_VMW_PARAM_NUM_FREE_STREAMS :
param - > value = vmw_overlay_num_free_overlays ( dev_priv ) ;
break ;
case DRM_VMW_PARAM_3D :
2010-01-30 06:38:06 +03:00
param - > value = vmw_fifo_have_3d ( dev_priv ) ? 1 : 0 ;
2009-12-10 03:19:58 +03:00
break ;
2010-02-09 22:41:55 +03:00
case DRM_VMW_PARAM_HW_CAPS :
param - > value = dev_priv - > capabilities ;
break ;
case DRM_VMW_PARAM_FIFO_CAPS :
param - > value = dev_priv - > fifo . capabilities ;
break ;
2010-10-05 14:43:06 +04:00
case DRM_VMW_PARAM_MAX_FB_SIZE :
2012-11-21 13:32:36 +04:00
param - > value = dev_priv - > prim_bb_mem ;
2010-10-05 14:43:06 +04:00
break ;
2011-09-02 00:18:41 +04:00
case DRM_VMW_PARAM_FIFO_HW_VERSION :
{
2015-10-28 12:44:04 +03:00
u32 * fifo_mem = dev_priv - > mmio_virt ;
2011-11-28 16:19:08 +04:00
const struct vmw_fifo_state * fifo = & dev_priv - > fifo ;
2011-09-02 00:18:41 +04:00
2014-01-31 13:21:10 +04:00
if ( ( dev_priv - > capabilities & SVGA_CAP_GBOBJECTS ) ) {
param - > value = SVGA3D_HWVERSION_WS8_B1 ;
break ;
}
2011-11-28 16:19:08 +04:00
param - > value =
2015-10-28 12:44:04 +03:00
vmw_mmio_read ( fifo_mem +
( ( fifo - > capabilities &
SVGA_FIFO_CAP_3D_HWVERSION_REVISED ) ?
SVGA_FIFO_3D_HWVERSION_REVISED :
SVGA_FIFO_3D_HWVERSION ) ) ;
2011-09-02 00:18:41 +04:00
break ;
}
2012-11-21 13:37:20 +04:00
case DRM_VMW_PARAM_MAX_SURF_MEMORY :
2014-01-31 13:21:10 +04:00
if ( ( dev_priv - > capabilities & SVGA_CAP_GBOBJECTS ) & &
! vmw_fp - > gb_aware )
param - > value = dev_priv - > max_mob_pages * PAGE_SIZE / 2 ;
else
param - > value = dev_priv - > memory_size ;
2012-11-21 13:37:20 +04:00
break ;
case DRM_VMW_PARAM_3D_CAPS_SIZE :
2014-01-31 13:21:10 +04:00
if ( ( dev_priv - > capabilities & SVGA_CAP_GBOBJECTS ) & &
vmw_fp - > gb_aware )
param - > value = SVGA3D_DEVCAP_MAX * sizeof ( uint32_t ) ;
else if ( dev_priv - > capabilities & SVGA_CAP_GBOBJECTS )
param - > value = sizeof ( struct svga_3d_compat_cap ) +
sizeof ( uint32_t ) ;
2012-11-21 13:37:20 +04:00
else
param - > value = ( SVGA_FIFO_3D_CAPS_LAST -
2014-01-31 13:21:10 +04:00
SVGA_FIFO_3D_CAPS + 1 ) *
sizeof ( uint32_t ) ;
2012-11-21 13:37:20 +04:00
break ;
2012-11-21 15:34:47 +04:00
case DRM_VMW_PARAM_MAX_MOB_MEMORY :
2014-01-31 13:21:10 +04:00
vmw_fp - > gb_aware = true ;
2012-11-21 15:34:47 +04:00
param - > value = dev_priv - > max_mob_pages * PAGE_SIZE ;
break ;
2014-02-12 15:07:38 +04:00
case DRM_VMW_PARAM_MAX_MOB_SIZE :
param - > value = dev_priv - > max_mob_size ;
break ;
2015-06-26 11:42:06 +03:00
case DRM_VMW_PARAM_SCREEN_TARGET :
param - > value =
( dev_priv - > active_display_unit = = vmw_du_screen_target ) ;
break ;
2015-08-10 20:39:35 +03:00
case DRM_VMW_PARAM_DX :
param - > value = dev_priv - > has_dx ;
break ;
2009-12-10 03:19:58 +03:00
default :
return - EINVAL ;
}
return 0 ;
}
2011-09-02 00:18:41 +04:00
2015-10-26 14:42:31 +03:00
static u32 vmw_mask_multisample ( unsigned int cap , u32 fmt_value )
{
/* If the header is updated, update the format test as well! */
BUILD_BUG_ON ( SVGA3D_DEVCAP_DXFMT_BC5_UNORM + 1 ! = SVGA3D_DEVCAP_MAX ) ;
if ( cap > = SVGA3D_DEVCAP_DXFMT_X8R8G8B8 & &
cap < = SVGA3D_DEVCAP_DXFMT_BC5_UNORM )
fmt_value & = ~ ( SVGADX_DXFMT_MULTISAMPLE_2 |
SVGADX_DXFMT_MULTISAMPLE_4 |
SVGADX_DXFMT_MULTISAMPLE_8 ) ;
else if ( cap = = SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES )
return 0 ;
return fmt_value ;
}
2014-01-31 13:21:10 +04:00
static int vmw_fill_compat_cap ( struct vmw_private * dev_priv , void * bounce ,
size_t size )
{
struct svga_3d_compat_cap * compat_cap =
( struct svga_3d_compat_cap * ) bounce ;
unsigned int i ;
size_t pair_offset = offsetof ( struct svga_3d_compat_cap , pairs ) ;
unsigned int max_size ;
if ( size < pair_offset )
return - EINVAL ;
max_size = ( size - pair_offset ) / sizeof ( SVGA3dCapPair ) ;
if ( max_size > SVGA3D_DEVCAP_MAX )
max_size = SVGA3D_DEVCAP_MAX ;
compat_cap - > header . length =
( pair_offset + max_size * sizeof ( SVGA3dCapPair ) ) / sizeof ( u32 ) ;
compat_cap - > header . type = SVGA3DCAPS_RECORD_DEVCAPS ;
2015-01-14 13:33:39 +03:00
spin_lock ( & dev_priv - > cap_lock ) ;
2014-01-31 13:21:10 +04:00
for ( i = 0 ; i < max_size ; + + i ) {
vmw_write ( dev_priv , SVGA_REG_DEV_CAP , i ) ;
compat_cap - > pairs [ i ] [ 0 ] = i ;
2015-10-26 14:42:31 +03:00
compat_cap - > pairs [ i ] [ 1 ] = vmw_mask_multisample
( i , vmw_read ( dev_priv , SVGA_REG_DEV_CAP ) ) ;
2014-01-31 13:21:10 +04:00
}
2015-01-14 13:33:39 +03:00
spin_unlock ( & dev_priv - > cap_lock ) ;
2014-01-31 13:21:10 +04:00
return 0 ;
}
2011-09-02 00:18:41 +04:00
int vmw_get_cap_3d_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_vmw_get_3d_cap_arg * arg =
( struct drm_vmw_get_3d_cap_arg * ) data ;
struct vmw_private * dev_priv = vmw_priv ( dev ) ;
uint32_t size ;
2015-10-28 12:44:04 +03:00
u32 * fifo_mem ;
2011-09-02 00:18:41 +04:00
void __user * buffer = ( void __user * ) ( ( unsigned long ) ( arg - > buffer ) ) ;
void * bounce ;
int ret ;
2012-11-21 13:37:20 +04:00
bool gb_objects = ! ! ( dev_priv - > capabilities & SVGA_CAP_GBOBJECTS ) ;
2014-01-31 13:21:10 +04:00
struct vmw_fpriv * vmw_fp = vmw_fpriv ( file_priv ) ;
2011-09-02 00:18:41 +04:00
2017-03-27 12:15:12 +03:00
if ( unlikely ( arg - > pad64 ! = 0 | | arg - > max_size = = 0 ) ) {
2011-09-02 00:18:41 +04:00
DRM_ERROR ( " Illegal GET_3D_CAP argument. \n " ) ;
return - EINVAL ;
}
2014-01-31 13:21:10 +04:00
if ( gb_objects & & vmw_fp - > gb_aware )
size = SVGA3D_DEVCAP_MAX * sizeof ( uint32_t ) ;
else if ( gb_objects )
size = sizeof ( struct svga_3d_compat_cap ) + sizeof ( uint32_t ) ;
2012-11-21 13:37:20 +04:00
else
2014-01-31 13:21:10 +04:00
size = ( SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1 ) *
sizeof ( uint32_t ) ;
2011-09-02 00:18:41 +04:00
if ( arg - > max_size < size )
size = arg - > max_size ;
2014-01-31 13:21:10 +04:00
bounce = vzalloc ( size ) ;
2011-09-02 00:18:41 +04:00
if ( unlikely ( bounce = = NULL ) ) {
DRM_ERROR ( " Failed to allocate bounce buffer for 3D caps. \n " ) ;
return - ENOMEM ;
}
2014-01-31 13:21:10 +04:00
if ( gb_objects & & vmw_fp - > gb_aware ) {
int i , num ;
2012-11-21 13:37:20 +04:00
uint32_t * bounce32 = ( uint32_t * ) bounce ;
2014-01-31 13:21:10 +04:00
num = size / sizeof ( uint32_t ) ;
2015-08-10 20:45:11 +03:00
if ( num > SVGA3D_DEVCAP_MAX )
num = SVGA3D_DEVCAP_MAX ;
2014-01-31 13:21:10 +04:00
2015-01-14 13:33:39 +03:00
spin_lock ( & dev_priv - > cap_lock ) ;
2014-01-31 13:21:10 +04:00
for ( i = 0 ; i < num ; + + i ) {
2012-11-21 13:37:20 +04:00
vmw_write ( dev_priv , SVGA_REG_DEV_CAP , i ) ;
2015-10-26 14:42:31 +03:00
* bounce32 + + = vmw_mask_multisample
( i , vmw_read ( dev_priv , SVGA_REG_DEV_CAP ) ) ;
2012-11-21 13:37:20 +04:00
}
2015-01-14 13:33:39 +03:00
spin_unlock ( & dev_priv - > cap_lock ) ;
2014-01-31 13:21:10 +04:00
} else if ( gb_objects ) {
ret = vmw_fill_compat_cap ( dev_priv , bounce , size ) ;
if ( unlikely ( ret ! = 0 ) )
goto out_err ;
2012-11-21 13:37:20 +04:00
} else {
fifo_mem = dev_priv - > mmio_virt ;
2015-10-28 12:44:04 +03:00
memcpy ( bounce , & fifo_mem [ SVGA_FIFO_3D_CAPS ] , size ) ;
2012-11-21 13:37:20 +04:00
}
2011-09-02 00:18:41 +04:00
ret = copy_to_user ( buffer , bounce , size ) ;
2012-11-12 15:07:24 +04:00
if ( ret )
ret = - EFAULT ;
2014-01-31 13:21:10 +04:00
out_err :
2011-09-02 00:18:41 +04:00
vfree ( bounce ) ;
if ( unlikely ( ret ! = 0 ) )
DRM_ERROR ( " Failed to report 3D caps info. \n " ) ;
return ret ;
}
2011-10-04 22:13:26 +04:00
int vmw_present_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct ttm_object_file * tfile = vmw_fpriv ( file_priv ) - > tfile ;
struct vmw_private * dev_priv = vmw_priv ( dev ) ;
struct drm_vmw_present_arg * arg =
( struct drm_vmw_present_arg * ) data ;
struct vmw_surface * surface ;
struct drm_vmw_rect __user * clips_ptr ;
struct drm_vmw_rect * clips = NULL ;
2012-12-03 00:53:40 +04:00
struct drm_framebuffer * fb ;
2011-10-04 22:13:26 +04:00
struct vmw_framebuffer * vfb ;
2012-11-20 16:19:35 +04:00
struct vmw_resource * res ;
2011-10-04 22:13:26 +04:00
uint32_t num_clips ;
int ret ;
num_clips = arg - > num_clips ;
2015-04-02 12:39:45 +03:00
clips_ptr = ( struct drm_vmw_rect __user * ) ( unsigned long ) arg - > clips_ptr ;
2011-10-04 22:13:26 +04:00
if ( unlikely ( num_clips = = 0 ) )
return 0 ;
if ( clips_ptr = = NULL ) {
DRM_ERROR ( " Variable clips_ptr must be specified. \n " ) ;
ret = - EINVAL ;
goto out_clips ;
}
2011-11-30 01:08:00 +04:00
clips = kcalloc ( num_clips , sizeof ( * clips ) , GFP_KERNEL ) ;
2011-10-04 22:13:26 +04:00
if ( clips = = NULL ) {
DRM_ERROR ( " Failed to allocate clip rect list. \n " ) ;
ret = - ENOMEM ;
goto out_clips ;
}
ret = copy_from_user ( clips , clips_ptr , num_clips * sizeof ( * clips ) ) ;
if ( ret ) {
DRM_ERROR ( " Failed to copy clip rects from userspace. \n " ) ;
2011-10-18 10:09:19 +04:00
ret = - EFAULT ;
2011-10-04 22:13:26 +04:00
goto out_no_copy ;
}
2012-12-02 04:48:38 +04:00
drm_modeset_lock_all ( dev ) ;
2011-10-04 22:13:26 +04:00
2017-03-15 09:25:07 +03:00
fb = drm_framebuffer_lookup ( dev , file_priv , arg - > fb_id ) ;
2012-12-03 00:53:40 +04:00
if ( ! fb ) {
2011-10-04 22:13:26 +04:00
DRM_ERROR ( " Invalid framebuffer id. \n " ) ;
2013-10-17 14:35:06 +04:00
ret = - ENOENT ;
2011-10-04 22:13:26 +04:00
goto out_no_fb ;
}
2012-12-03 00:53:40 +04:00
vfb = vmw_framebuffer_to_vfb ( fb ) ;
2011-10-04 22:13:26 +04:00
2014-02-27 15:34:51 +04:00
ret = ttm_read_lock ( & dev_priv - > reservation_sem , true ) ;
2011-10-04 22:13:26 +04:00
if ( unlikely ( ret ! = 0 ) )
goto out_no_ttm_lock ;
2012-11-20 16:19:35 +04:00
ret = vmw_user_resource_lookup_handle ( dev_priv , tfile , arg - > sid ,
user_surface_converter ,
& res ) ;
2011-10-04 22:13:26 +04:00
if ( ret )
goto out_no_surface ;
2012-11-20 16:19:35 +04:00
surface = vmw_res_to_srf ( res ) ;
2011-10-04 22:13:26 +04:00
ret = vmw_kms_present ( dev_priv , file_priv ,
vfb , surface , arg - > sid ,
arg - > dest_x , arg - > dest_y ,
clips , num_clips ) ;
/* vmw_user_surface_lookup takes one ref so does new_fb */
vmw_surface_unreference ( & surface ) ;
out_no_surface :
2014-02-27 15:34:51 +04:00
ttm_read_unlock ( & dev_priv - > reservation_sem ) ;
2011-10-04 22:13:26 +04:00
out_no_ttm_lock :
2018-03-12 02:33:13 +03:00
drm_framebuffer_put ( fb ) ;
2011-10-04 22:13:26 +04:00
out_no_fb :
2012-12-02 04:48:38 +04:00
drm_modeset_unlock_all ( dev ) ;
2011-10-04 22:13:26 +04:00
out_no_copy :
kfree ( clips ) ;
out_clips :
return ret ;
}
int vmw_present_readback_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct vmw_private * dev_priv = vmw_priv ( dev ) ;
struct drm_vmw_present_readback_arg * arg =
( struct drm_vmw_present_readback_arg * ) data ;
struct drm_vmw_fence_rep __user * user_fence_rep =
( struct drm_vmw_fence_rep __user * )
( unsigned long ) arg - > fence_rep ;
struct drm_vmw_rect __user * clips_ptr ;
struct drm_vmw_rect * clips = NULL ;
2012-12-03 00:53:40 +04:00
struct drm_framebuffer * fb ;
2011-10-04 22:13:26 +04:00
struct vmw_framebuffer * vfb ;
uint32_t num_clips ;
int ret ;
num_clips = arg - > num_clips ;
2015-04-02 12:39:45 +03:00
clips_ptr = ( struct drm_vmw_rect __user * ) ( unsigned long ) arg - > clips_ptr ;
2011-10-04 22:13:26 +04:00
if ( unlikely ( num_clips = = 0 ) )
return 0 ;
if ( clips_ptr = = NULL ) {
DRM_ERROR ( " Argument clips_ptr must be specified. \n " ) ;
ret = - EINVAL ;
goto out_clips ;
}
2011-11-30 01:08:00 +04:00
clips = kcalloc ( num_clips , sizeof ( * clips ) , GFP_KERNEL ) ;
2011-10-04 22:13:26 +04:00
if ( clips = = NULL ) {
DRM_ERROR ( " Failed to allocate clip rect list. \n " ) ;
ret = - ENOMEM ;
goto out_clips ;
}
ret = copy_from_user ( clips , clips_ptr , num_clips * sizeof ( * clips ) ) ;
if ( ret ) {
DRM_ERROR ( " Failed to copy clip rects from userspace. \n " ) ;
2011-10-18 10:09:19 +04:00
ret = - EFAULT ;
2011-10-04 22:13:26 +04:00
goto out_no_copy ;
}
2012-12-02 04:48:38 +04:00
drm_modeset_lock_all ( dev ) ;
2011-10-04 22:13:26 +04:00
2017-03-15 09:25:07 +03:00
fb = drm_framebuffer_lookup ( dev , file_priv , arg - > fb_id ) ;
2012-12-03 00:53:40 +04:00
if ( ! fb ) {
2011-10-04 22:13:26 +04:00
DRM_ERROR ( " Invalid framebuffer id. \n " ) ;
2013-10-17 14:35:06 +04:00
ret = - ENOENT ;
2011-10-04 22:13:26 +04:00
goto out_no_fb ;
}
2012-12-03 00:53:40 +04:00
vfb = vmw_framebuffer_to_vfb ( fb ) ;
2011-10-04 22:13:26 +04:00
if ( ! vfb - > dmabuf ) {
DRM_ERROR ( " Framebuffer not dmabuf backed. \n " ) ;
ret = - EINVAL ;
2012-12-11 19:28:34 +04:00
goto out_no_ttm_lock ;
2011-10-04 22:13:26 +04:00
}
2014-02-27 15:34:51 +04:00
ret = ttm_read_lock ( & dev_priv - > reservation_sem , true ) ;
2011-10-04 22:13:26 +04:00
if ( unlikely ( ret ! = 0 ) )
goto out_no_ttm_lock ;
ret = vmw_kms_readback ( dev_priv , file_priv ,
vfb , user_fence_rep ,
clips , num_clips ) ;
2014-02-27 15:34:51 +04:00
ttm_read_unlock ( & dev_priv - > reservation_sem ) ;
2011-10-04 22:13:26 +04:00
out_no_ttm_lock :
2018-03-12 02:33:13 +03:00
drm_framebuffer_put ( fb ) ;
2011-10-04 22:13:26 +04:00
out_no_fb :
2012-12-02 04:48:38 +04:00
drm_modeset_unlock_all ( dev ) ;
2011-10-04 22:13:26 +04:00
out_no_copy :
kfree ( clips ) ;
out_clips :
return ret ;
}
2011-10-10 14:23:27 +04:00
/**
* vmw_fops_poll - wrapper around the drm_poll function
*
* @ filp : See the linux fops poll documentation .
* @ wait : See the linux fops poll documentation .
*
* Wrapper around the drm_poll function that makes sure the device is
* processing the fifo if drm_poll decides to wait .
*/
2017-07-03 13:39:46 +03:00
__poll_t vmw_fops_poll ( struct file * filp , struct poll_table_struct * wait )
2011-10-10 14:23:27 +04:00
{
struct drm_file * file_priv = filp - > private_data ;
struct vmw_private * dev_priv =
vmw_priv ( file_priv - > minor - > dev ) ;
vmw_fifo_ping_host ( dev_priv , SVGA_SYNC_GENERIC ) ;
return drm_poll ( filp , wait ) ;
}
/**
* vmw_fops_read - wrapper around the drm_read function
*
* @ filp : See the linux fops read documentation .
* @ buffer : See the linux fops read documentation .
* @ count : See the linux fops read documentation .
* offset : See the linux fops read documentation .
*
* Wrapper around the drm_read function that makes sure the device is
* processing the fifo if drm_read decides to wait .
*/
ssize_t vmw_fops_read ( struct file * filp , char __user * buffer ,
size_t count , loff_t * offset )
{
struct drm_file * file_priv = filp - > private_data ;
struct vmw_private * dev_priv =
vmw_priv ( file_priv - > minor - > dev ) ;
vmw_fifo_ping_host ( dev_priv , SVGA_SYNC_GENERIC ) ;
return drm_read ( filp , buffer , count , offset ) ;
}