2012-04-17 14:12:29 +01:00
/*
* Copyright 2012 Red Hat
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details .
*
* Authors : Matthew Garrett
* Dave Airlie
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2018-03-30 15:11:15 +01:00
# include <drm/drm_gem_framebuffer_helper.h>
2012-04-17 14:12:29 +01:00
# include "cirrus_drv.h"
static const struct drm_framebuffer_funcs cirrus_fb_funcs = {
2018-03-30 15:11:15 +01:00
. create_handle = drm_gem_fb_create_handle ,
. destroy = drm_gem_fb_destroy ,
2012-04-17 14:12:29 +01:00
} ;
int cirrus_framebuffer_init ( struct drm_device * dev ,
2018-03-30 15:11:16 +01:00
struct drm_framebuffer * gfb ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd ,
2012-04-17 14:12:29 +01:00
struct drm_gem_object * obj )
{
int ret ;
2018-03-30 15:11:16 +01:00
drm_helper_mode_fill_fb_struct ( dev , gfb , mode_cmd ) ;
gfb - > obj [ 0 ] = obj ;
ret = drm_framebuffer_init ( dev , gfb , & cirrus_fb_funcs ) ;
2012-04-17 14:12:29 +01:00
if ( ret ) {
DRM_ERROR ( " drm_framebuffer_init failed: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static struct drm_framebuffer *
cirrus_user_framebuffer_create ( struct drm_device * dev ,
struct drm_file * filp ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd )
2012-04-17 14:12:29 +01:00
{
2014-10-29 11:04:24 -07:00
struct cirrus_device * cdev = dev - > dev_private ;
2012-04-17 14:12:29 +01:00
struct drm_gem_object * obj ;
2018-03-30 15:11:16 +01:00
struct drm_framebuffer * fb ;
2016-10-18 01:41:15 +03:00
u32 bpp ;
2012-04-17 14:12:29 +01:00
int ret ;
2016-10-18 01:41:15 +03:00
bpp = drm_format_plane_cpp ( mode_cmd - > pixel_format , 0 ) * 8 ;
2014-10-29 11:04:24 -07:00
if ( ! cirrus_check_framebuffer ( cdev , mode_cmd - > width , mode_cmd - > height ,
bpp , mode_cmd - > pitches [ 0 ] ) )
2012-04-17 14:12:29 +01:00
return ERR_PTR ( - EINVAL ) ;
2016-05-09 11:04:54 +01:00
obj = drm_gem_object_lookup ( filp , mode_cmd - > handles [ 0 ] ) ;
2012-04-17 14:12:29 +01:00
if ( obj = = NULL )
return ERR_PTR ( - ENOENT ) ;
2018-03-30 15:11:16 +01:00
fb = kzalloc ( sizeof ( * fb ) , GFP_KERNEL ) ;
if ( ! fb ) {
2017-08-11 15:32:52 +03:00
drm_gem_object_put_unlocked ( obj ) ;
2012-04-17 14:12:29 +01:00
return ERR_PTR ( - ENOMEM ) ;
}
2018-03-30 15:11:16 +01:00
ret = cirrus_framebuffer_init ( dev , fb , mode_cmd , obj ) ;
2012-04-17 14:12:29 +01:00
if ( ret ) {
2017-08-11 15:32:52 +03:00
drm_gem_object_put_unlocked ( obj ) ;
2018-03-30 15:11:16 +01:00
kfree ( fb ) ;
2012-04-17 14:12:29 +01:00
return ERR_PTR ( ret ) ;
}
2018-03-30 15:11:16 +01:00
return fb ;
2012-04-17 14:12:29 +01:00
}
static const struct drm_mode_config_funcs cirrus_mode_funcs = {
. fb_create = cirrus_user_framebuffer_create ,
} ;
/* Unmap the framebuffer from the core and release the memory */
static void cirrus_vram_fini ( struct cirrus_device * cdev )
{
iounmap ( cdev - > rmmio ) ;
cdev - > rmmio = NULL ;
if ( cdev - > mc . vram_base )
release_mem_region ( cdev - > mc . vram_base , cdev - > mc . vram_size ) ;
}
/* Map the framebuffer from the card and configure the core */
static int cirrus_vram_init ( struct cirrus_device * cdev )
{
/* BAR 0 is VRAM */
cdev - > mc . vram_base = pci_resource_start ( cdev - > dev - > pdev , 0 ) ;
2014-10-29 11:04:24 -07:00
cdev - > mc . vram_size = pci_resource_len ( cdev - > dev - > pdev , 0 ) ;
2012-04-17 14:12:29 +01:00
if ( ! request_mem_region ( cdev - > mc . vram_base , cdev - > mc . vram_size ,
" cirrusdrmfb_vram " ) ) {
DRM_ERROR ( " can't reserve VRAM \n " ) ;
return - ENXIO ;
}
return 0 ;
}
/*
* Our emulated hardware has two sets of memory . One is video RAM and can
* simply be used as a linear framebuffer - the other provides mmio access
* to the display registers . The latter can also be accessed via IO port
* access , but we map the range and use mmio to program them instead
*/
int cirrus_device_init ( struct cirrus_device * cdev ,
struct drm_device * ddev ,
struct pci_dev * pdev , uint32_t flags )
{
int ret ;
cdev - > dev = ddev ;
cdev - > flags = flags ;
/* Hardcode the number of CRTCs to 1 */
cdev - > num_crtc = 1 ;
/* BAR 0 is the framebuffer, BAR 1 contains registers */
cdev - > rmmio_base = pci_resource_start ( cdev - > dev - > pdev , 1 ) ;
cdev - > rmmio_size = pci_resource_len ( cdev - > dev - > pdev , 1 ) ;
if ( ! request_mem_region ( cdev - > rmmio_base , cdev - > rmmio_size ,
" cirrusdrmfb_mmio " ) ) {
DRM_ERROR ( " can't reserve mmio registers \n " ) ;
return - ENOMEM ;
}
cdev - > rmmio = ioremap ( cdev - > rmmio_base , cdev - > rmmio_size ) ;
if ( cdev - > rmmio = = NULL )
return - ENOMEM ;
ret = cirrus_vram_init ( cdev ) ;
if ( ret ) {
release_mem_region ( cdev - > rmmio_base , cdev - > rmmio_size ) ;
return ret ;
}
return 0 ;
}
void cirrus_device_fini ( struct cirrus_device * cdev )
{
release_mem_region ( cdev - > rmmio_base , cdev - > rmmio_size ) ;
cirrus_vram_fini ( cdev ) ;
}
/*
* Functions here will be called by the core once it ' s bound the driver to
* a PCI device
*/
int cirrus_driver_load ( struct drm_device * dev , unsigned long flags )
{
struct cirrus_device * cdev ;
int r ;
cdev = kzalloc ( sizeof ( struct cirrus_device ) , GFP_KERNEL ) ;
if ( cdev = = NULL )
return - ENOMEM ;
dev - > dev_private = ( void * ) cdev ;
r = cirrus_device_init ( cdev , dev , dev - > pdev , flags ) ;
if ( r ) {
dev_err ( & dev - > pdev - > dev , " Fatal error during GPU init: %d \n " , r ) ;
goto out ;
}
r = cirrus_mm_init ( cdev ) ;
2014-11-17 17:19:41 -08:00
if ( r ) {
2012-04-17 14:12:29 +01:00
dev_err ( & dev - > pdev - > dev , " fatal err on mm init \n " ) ;
2014-11-17 17:19:41 -08:00
goto out ;
}
2012-04-17 14:12:29 +01:00
2016-08-09 02:16:18 +02:00
/*
* cirrus_modeset_init ( ) is initializing / registering the emulated fbdev
* and DRM internals can access / test some of the fields in
* mode_config - > funcs as part of the fbdev registration process .
* Make sure dev - > mode_config . funcs is properly set to avoid
* dereferencing a NULL pointer .
* FIXME : mode_config . funcs assignment should probably be done in
* cirrus_modeset_init ( ) ( that ' s a common pattern seen in other DRM
* drivers ) .
*/
dev - > mode_config . funcs = & cirrus_mode_funcs ;
2012-04-17 14:12:29 +01:00
r = cirrus_modeset_init ( cdev ) ;
2014-11-17 17:19:41 -08:00
if ( r ) {
2012-04-17 14:12:29 +01:00
dev_err ( & dev - > pdev - > dev , " Fatal error during modeset init: %d \n " , r ) ;
2014-11-17 17:19:41 -08:00
goto out ;
}
2012-04-17 14:12:29 +01:00
2014-11-17 17:19:41 -08:00
return 0 ;
2012-04-17 14:12:29 +01:00
out :
2014-11-17 17:19:41 -08:00
cirrus_driver_unload ( dev ) ;
2012-04-17 14:12:29 +01:00
return r ;
}
2017-01-06 15:57:31 -02:00
void cirrus_driver_unload ( struct drm_device * dev )
2012-04-17 14:12:29 +01:00
{
struct cirrus_device * cdev = dev - > dev_private ;
if ( cdev = = NULL )
2017-01-06 15:57:31 -02:00
return ;
2012-04-17 14:12:29 +01:00
cirrus_modeset_fini ( cdev ) ;
cirrus_mm_fini ( cdev ) ;
cirrus_device_fini ( cdev ) ;
kfree ( cdev ) ;
dev - > dev_private = NULL ;
}
int cirrus_gem_create ( struct drm_device * dev ,
u32 size , bool iskernel ,
struct drm_gem_object * * obj )
{
struct cirrus_bo * cirrusbo ;
int ret ;
* obj = NULL ;
size = roundup ( size , PAGE_SIZE ) ;
if ( size = = 0 )
return - EINVAL ;
ret = cirrus_bo_create ( dev , size , 0 , 0 , & cirrusbo ) ;
if ( ret ) {
if ( ret ! = - ERESTARTSYS )
DRM_ERROR ( " failed to allocate GEM object \n " ) ;
return ret ;
}
* obj = & cirrusbo - > gem ;
return 0 ;
}
int cirrus_dumb_create ( struct drm_file * file ,
struct drm_device * dev ,
struct drm_mode_create_dumb * args )
{
int ret ;
struct drm_gem_object * gobj ;
u32 handle ;
args - > pitch = args - > width * ( ( args - > bpp + 7 ) / 8 ) ;
args - > size = args - > pitch * args - > height ;
ret = cirrus_gem_create ( dev , args - > size , false ,
& gobj ) ;
if ( ret )
return ret ;
ret = drm_gem_handle_create ( file , gobj , & handle ) ;
2017-08-11 15:32:52 +03:00
drm_gem_object_put_unlocked ( gobj ) ;
2012-04-17 14:12:29 +01:00
if ( ret )
return ret ;
args - > handle = handle ;
return 0 ;
}
2014-01-06 20:29:12 +05:30
static void cirrus_bo_unref ( struct cirrus_bo * * bo )
2012-04-17 14:12:29 +01:00
{
struct ttm_buffer_object * tbo ;
if ( ( * bo ) = = NULL )
return ;
tbo = & ( ( * bo ) - > bo ) ;
2018-07-31 08:31:28 +02:00
ttm_bo_put ( tbo ) ;
2014-04-05 10:09:36 +02:00
* bo = NULL ;
2012-04-17 14:12:29 +01:00
}
void cirrus_gem_free_object ( struct drm_gem_object * obj )
{
struct cirrus_bo * cirrus_bo = gem_to_cirrus_bo ( obj ) ;
cirrus_bo_unref ( & cirrus_bo ) ;
}
static inline u64 cirrus_bo_mmap_offset ( struct cirrus_bo * bo )
{
2013-07-24 21:08:53 +02:00
return drm_vma_node_offset_addr ( & bo - > bo . vma_node ) ;
2012-04-17 14:12:29 +01:00
}
int
cirrus_dumb_mmap_offset ( struct drm_file * file ,
struct drm_device * dev ,
uint32_t handle ,
uint64_t * offset )
{
struct drm_gem_object * obj ;
struct cirrus_bo * bo ;
2016-05-09 11:04:54 +01:00
obj = drm_gem_object_lookup ( file , handle ) ;
2015-07-09 23:32:40 +02:00
if ( obj = = NULL )
return - ENOENT ;
2012-04-17 14:12:29 +01:00
bo = gem_to_cirrus_bo ( obj ) ;
* offset = cirrus_bo_mmap_offset ( bo ) ;
2017-08-11 15:32:52 +03:00
drm_gem_object_put_unlocked ( obj ) ;
2012-04-17 14:12:29 +01:00
2015-07-09 23:32:40 +02:00
return 0 ;
2012-04-17 14:12:29 +01:00
}
2014-10-29 11:04:24 -07:00
bool cirrus_check_framebuffer ( struct cirrus_device * cdev , int width , int height ,
int bpp , int pitch )
{
const int max_pitch = 0x1FF < < 3 ; /* (4096 - 1) & ~111b bytes */
const int max_size = cdev - > mc . vram_size ;
2015-02-03 17:51:23 +01:00
if ( bpp > cirrus_bpp )
return false ;
2014-10-29 11:04:24 -07:00
if ( bpp > 32 )
return false ;
if ( pitch > max_pitch )
return false ;
if ( pitch * height > max_size )
return false ;
return true ;
}