2009-12-10 00:19:58 +00:00
/**************************************************************************
*
* Copyright © 2009 VMware , Inc . , Palo Alto , CA . , USA
* 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 18:01:07 +01:00
# include <drm/vmwgfx_drm.h>
2011-10-04 20:13:26 +02:00
# include "vmwgfx_kms.h"
2009-12-10 00:19:58 +00:00
2014-01-31 10:21:10 +01:00
struct svga_3d_compat_cap {
SVGA3dCapsRecordHeader header ;
SVGA3dCapPair pairs [ SVGA3D_DEVCAP_MAX ] ;
} ;
2009-12-10 00:19:58 +00: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 10:21:10 +01:00
struct vmw_fpriv * vmw_fp = vmw_fpriv ( file_priv ) ;
2009-12-10 00:19:58 +00: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 03:38:06 +00:00
param - > value = vmw_fifo_have_3d ( dev_priv ) ? 1 : 0 ;
2009-12-10 00:19:58 +00:00
break ;
2010-02-09 19:41:55 +00: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 12:43:06 +02:00
case DRM_VMW_PARAM_MAX_FB_SIZE :
2012-11-21 10:32:36 +01:00
param - > value = dev_priv - > prim_bb_mem ;
2010-10-05 12:43:06 +02:00
break ;
2011-09-01 20:18:41 +00:00
case DRM_VMW_PARAM_FIFO_HW_VERSION :
{
2015-04-02 02:39:45 -07:00
u32 __iomem * fifo_mem = dev_priv - > mmio_virt ;
2011-11-28 13:19:08 +01:00
const struct vmw_fifo_state * fifo = & dev_priv - > fifo ;
2011-09-01 20:18:41 +00:00
2014-01-31 10:21:10 +01:00
if ( ( dev_priv - > capabilities & SVGA_CAP_GBOBJECTS ) ) {
param - > value = SVGA3D_HWVERSION_WS8_B1 ;
break ;
}
2011-11-28 13:19:08 +01:00
param - > value =
ioread32 ( fifo_mem +
( ( fifo - > capabilities &
SVGA_FIFO_CAP_3D_HWVERSION_REVISED ) ?
SVGA_FIFO_3D_HWVERSION_REVISED :
SVGA_FIFO_3D_HWVERSION ) ) ;
2011-09-01 20:18:41 +00:00
break ;
}
2012-11-21 10:37:20 +01:00
case DRM_VMW_PARAM_MAX_SURF_MEMORY :
2014-01-31 10:21:10 +01: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 10:37:20 +01:00
break ;
case DRM_VMW_PARAM_3D_CAPS_SIZE :
2014-01-31 10:21:10 +01: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 10:37:20 +01:00
else
param - > value = ( SVGA_FIFO_3D_CAPS_LAST -
2014-01-31 10:21:10 +01:00
SVGA_FIFO_3D_CAPS + 1 ) *
sizeof ( uint32_t ) ;
2012-11-21 10:37:20 +01:00
break ;
2012-11-21 12:34:47 +01:00
case DRM_VMW_PARAM_MAX_MOB_MEMORY :
2014-01-31 10:21:10 +01:00
vmw_fp - > gb_aware = true ;
2012-11-21 12:34:47 +01:00
param - > value = dev_priv - > max_mob_pages * PAGE_SIZE ;
break ;
2014-02-12 12:07:38 +01:00
case DRM_VMW_PARAM_MAX_MOB_SIZE :
param - > value = dev_priv - > max_mob_size ;
break ;
2015-06-26 01:42:06 -07:00
case DRM_VMW_PARAM_SCREEN_TARGET :
param - > value =
( dev_priv - > active_display_unit = = vmw_du_screen_target ) ;
break ;
2009-12-10 00:19:58 +00:00
default :
DRM_ERROR ( " Illegal vmwgfx get param request: %d \n " ,
param - > param ) ;
return - EINVAL ;
}
return 0 ;
}
2011-09-01 20:18:41 +00:00
2014-01-31 10:21:10 +01: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 02:33:39 -08:00
spin_lock ( & dev_priv - > cap_lock ) ;
2014-01-31 10:21:10 +01:00
for ( i = 0 ; i < max_size ; + + i ) {
vmw_write ( dev_priv , SVGA_REG_DEV_CAP , i ) ;
compat_cap - > pairs [ i ] [ 0 ] = i ;
compat_cap - > pairs [ i ] [ 1 ] = vmw_read ( dev_priv , SVGA_REG_DEV_CAP ) ;
}
2015-01-14 02:33:39 -08:00
spin_unlock ( & dev_priv - > cap_lock ) ;
2014-01-31 10:21:10 +01:00
return 0 ;
}
2011-09-01 20:18:41 +00: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-04-02 02:39:45 -07:00
u32 __iomem * fifo_mem ;
2011-09-01 20:18:41 +00:00
void __user * buffer = ( void __user * ) ( ( unsigned long ) ( arg - > buffer ) ) ;
void * bounce ;
int ret ;
2012-11-21 10:37:20 +01:00
bool gb_objects = ! ! ( dev_priv - > capabilities & SVGA_CAP_GBOBJECTS ) ;
2014-01-31 10:21:10 +01:00
struct vmw_fpriv * vmw_fp = vmw_fpriv ( file_priv ) ;
2011-09-01 20:18:41 +00:00
if ( unlikely ( arg - > pad64 ! = 0 ) ) {
DRM_ERROR ( " Illegal GET_3D_CAP argument. \n " ) ;
return - EINVAL ;
}
2014-01-31 10:21:10 +01: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 10:37:20 +01:00
else
2014-01-31 10:21:10 +01:00
size = ( SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1 ) *
sizeof ( uint32_t ) ;
2011-09-01 20:18:41 +00:00
if ( arg - > max_size < size )
size = arg - > max_size ;
2014-01-31 10:21:10 +01:00
bounce = vzalloc ( size ) ;
2011-09-01 20:18:41 +00:00
if ( unlikely ( bounce = = NULL ) ) {
DRM_ERROR ( " Failed to allocate bounce buffer for 3D caps. \n " ) ;
return - ENOMEM ;
}
2014-01-31 10:21:10 +01:00
if ( gb_objects & & vmw_fp - > gb_aware ) {
int i , num ;
2012-11-21 10:37:20 +01:00
uint32_t * bounce32 = ( uint32_t * ) bounce ;
2014-01-31 10:21:10 +01:00
num = size / sizeof ( uint32_t ) ;
if ( num > SVGA3D_DEVCAP_MAX )
num = SVGA3D_DEVCAP_MAX ;
2015-01-14 02:33:39 -08:00
spin_lock ( & dev_priv - > cap_lock ) ;
2014-01-31 10:21:10 +01:00
for ( i = 0 ; i < num ; + + i ) {
2012-11-21 10:37:20 +01:00
vmw_write ( dev_priv , SVGA_REG_DEV_CAP , i ) ;
* bounce32 + + = vmw_read ( dev_priv , SVGA_REG_DEV_CAP ) ;
}
2015-01-14 02:33:39 -08:00
spin_unlock ( & dev_priv - > cap_lock ) ;
2014-01-31 10:21:10 +01:00
} else if ( gb_objects ) {
ret = vmw_fill_compat_cap ( dev_priv , bounce , size ) ;
if ( unlikely ( ret ! = 0 ) )
goto out_err ;
2012-11-21 10:37:20 +01:00
} else {
fifo_mem = dev_priv - > mmio_virt ;
memcpy_fromio ( bounce , & fifo_mem [ SVGA_FIFO_3D_CAPS ] , size ) ;
}
2011-09-01 20:18:41 +00:00
ret = copy_to_user ( buffer , bounce , size ) ;
2012-11-12 11:07:24 +00:00
if ( ret )
ret = - EFAULT ;
2014-01-31 10:21:10 +01:00
out_err :
2011-09-01 20:18:41 +00:00
vfree ( bounce ) ;
if ( unlikely ( ret ! = 0 ) )
DRM_ERROR ( " Failed to report 3D caps info. \n " ) ;
return ret ;
}
2011-10-04 20:13:26 +02: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-02 21:53:40 +01:00
struct drm_framebuffer * fb ;
2011-10-04 20:13:26 +02:00
struct vmw_framebuffer * vfb ;
2012-11-20 12:19:35 +00:00
struct vmw_resource * res ;
2011-10-04 20:13:26 +02:00
uint32_t num_clips ;
int ret ;
num_clips = arg - > num_clips ;
2015-04-02 02:39:45 -07:00
clips_ptr = ( struct drm_vmw_rect __user * ) ( unsigned long ) arg - > clips_ptr ;
2011-10-04 20:13:26 +02: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-29 22:08:00 +01:00
clips = kcalloc ( num_clips , sizeof ( * clips ) , GFP_KERNEL ) ;
2011-10-04 20:13:26 +02: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 09:09:19 +03:00
ret = - EFAULT ;
2011-10-04 20:13:26 +02:00
goto out_no_copy ;
}
2012-12-02 01:48:38 +01:00
drm_modeset_lock_all ( dev ) ;
2011-10-04 20:13:26 +02:00
2012-12-02 21:53:40 +01:00
fb = drm_framebuffer_lookup ( dev , arg - > fb_id ) ;
if ( ! fb ) {
2011-10-04 20:13:26 +02:00
DRM_ERROR ( " Invalid framebuffer id. \n " ) ;
2013-10-17 13:35:06 +03:00
ret = - ENOENT ;
2011-10-04 20:13:26 +02:00
goto out_no_fb ;
}
2012-12-02 21:53:40 +01:00
vfb = vmw_framebuffer_to_vfb ( fb ) ;
2011-10-04 20:13:26 +02:00
2014-02-27 12:34:51 +01:00
ret = ttm_read_lock ( & dev_priv - > reservation_sem , true ) ;
2011-10-04 20:13:26 +02:00
if ( unlikely ( ret ! = 0 ) )
goto out_no_ttm_lock ;
2012-11-20 12:19:35 +00:00
ret = vmw_user_resource_lookup_handle ( dev_priv , tfile , arg - > sid ,
user_surface_converter ,
& res ) ;
2011-10-04 20:13:26 +02:00
if ( ret )
goto out_no_surface ;
2012-11-20 12:19:35 +00:00
surface = vmw_res_to_srf ( res ) ;
2011-10-04 20:13:26 +02: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 12:34:51 +01:00
ttm_read_unlock ( & dev_priv - > reservation_sem ) ;
2011-10-04 20:13:26 +02:00
out_no_ttm_lock :
2012-12-11 16:28:34 +01:00
drm_framebuffer_unreference ( fb ) ;
2011-10-04 20:13:26 +02:00
out_no_fb :
2012-12-02 01:48:38 +01:00
drm_modeset_unlock_all ( dev ) ;
2011-10-04 20:13:26 +02: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-02 21:53:40 +01:00
struct drm_framebuffer * fb ;
2011-10-04 20:13:26 +02:00
struct vmw_framebuffer * vfb ;
uint32_t num_clips ;
int ret ;
num_clips = arg - > num_clips ;
2015-04-02 02:39:45 -07:00
clips_ptr = ( struct drm_vmw_rect __user * ) ( unsigned long ) arg - > clips_ptr ;
2011-10-04 20:13:26 +02: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-29 22:08:00 +01:00
clips = kcalloc ( num_clips , sizeof ( * clips ) , GFP_KERNEL ) ;
2011-10-04 20:13:26 +02: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 09:09:19 +03:00
ret = - EFAULT ;
2011-10-04 20:13:26 +02:00
goto out_no_copy ;
}
2012-12-02 01:48:38 +01:00
drm_modeset_lock_all ( dev ) ;
2011-10-04 20:13:26 +02:00
2012-12-02 21:53:40 +01:00
fb = drm_framebuffer_lookup ( dev , arg - > fb_id ) ;
if ( ! fb ) {
2011-10-04 20:13:26 +02:00
DRM_ERROR ( " Invalid framebuffer id. \n " ) ;
2013-10-17 13:35:06 +03:00
ret = - ENOENT ;
2011-10-04 20:13:26 +02:00
goto out_no_fb ;
}
2012-12-02 21:53:40 +01:00
vfb = vmw_framebuffer_to_vfb ( fb ) ;
2011-10-04 20:13:26 +02:00
if ( ! vfb - > dmabuf ) {
DRM_ERROR ( " Framebuffer not dmabuf backed. \n " ) ;
ret = - EINVAL ;
2012-12-11 16:28:34 +01:00
goto out_no_ttm_lock ;
2011-10-04 20:13:26 +02:00
}
2014-02-27 12:34:51 +01:00
ret = ttm_read_lock ( & dev_priv - > reservation_sem , true ) ;
2011-10-04 20:13:26 +02: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 12:34:51 +01:00
ttm_read_unlock ( & dev_priv - > reservation_sem ) ;
2011-10-04 20:13:26 +02:00
out_no_ttm_lock :
2012-12-11 16:28:34 +01:00
drm_framebuffer_unreference ( fb ) ;
2011-10-04 20:13:26 +02:00
out_no_fb :
2012-12-02 01:48:38 +01:00
drm_modeset_unlock_all ( dev ) ;
2011-10-04 20:13:26 +02:00
out_no_copy :
kfree ( clips ) ;
out_clips :
return ret ;
}
2011-10-10 12:23:27 +02: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 .
*/
unsigned int vmw_fops_poll ( struct file * filp , struct poll_table_struct * wait )
{
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 ) ;
}