2009-12-11 12:24:15 +03:00
/*
* Copyright ( C ) 2008 Maarten Maathuis .
* 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 , sublicense , 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 NONINFRINGEMENT .
* IN NO EVENT SHALL THE COPYRIGHT OWNER ( S ) 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 .
*
*/
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2012-07-31 10:16:21 +04:00
2009-12-11 12:24:15 +03:00
# include "nouveau_fbcon.h"
2013-03-21 09:45:11 +04:00
# include "dispnv04/hw.h"
2010-10-21 01:35:40 +04:00
# include "nouveau_crtc.h"
# include "nouveau_dma.h"
2012-07-31 10:16:21 +04:00
# include "nouveau_gem.h"
2011-10-17 06:23:41 +04:00
# include "nouveau_connector.h"
2011-02-09 04:57:45 +03:00
# include "nv50_display.h"
2009-12-11 12:24:15 +03:00
2012-07-20 02:17:34 +04:00
# include "nouveau_fence.h"
2012-07-31 10:16:21 +04:00
# include <engine/disp.h>
2012-07-10 06:20:17 +04:00
2013-01-31 03:23:34 +04:00
# include <core/class.h>
2013-10-03 01:02:29 +04:00
static int
nouveau_display_vblank_handler ( void * data , int head )
{
struct nouveau_drm * drm = data ;
drm_handle_vblank ( drm - > dev , head ) ;
return NVKM_EVENT_KEEP ;
}
int
nouveau_display_vblank_enable ( struct drm_device * dev , int head )
{
struct nouveau_display * disp = nouveau_display ( dev ) ;
if ( disp ) {
nouveau_event_get ( disp - > vblank [ head ] ) ;
return 0 ;
}
return - EIO ;
}
void
nouveau_display_vblank_disable ( struct drm_device * dev , int head )
{
struct nouveau_display * disp = nouveau_display ( dev ) ;
if ( disp )
nouveau_event_put ( disp - > vblank [ head ] ) ;
}
static void
nouveau_display_vblank_fini ( struct drm_device * dev )
{
struct nouveau_display * disp = nouveau_display ( dev ) ;
int i ;
if ( disp - > vblank ) {
for ( i = 0 ; i < dev - > mode_config . num_crtc ; i + + )
nouveau_event_ref ( NULL , & disp - > vblank [ i ] ) ;
kfree ( disp - > vblank ) ;
disp - > vblank = NULL ;
}
drm_vblank_cleanup ( dev ) ;
}
static int
nouveau_display_vblank_init ( struct drm_device * dev )
{
struct nouveau_display * disp = nouveau_display ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_disp * pdisp = nouveau_disp ( drm - > device ) ;
int ret , i ;
disp - > vblank = kzalloc ( dev - > mode_config . num_crtc *
sizeof ( * disp - > vblank ) , GFP_KERNEL ) ;
if ( ! disp - > vblank )
return - ENOMEM ;
for ( i = 0 ; i < dev - > mode_config . num_crtc ; i + + ) {
ret = nouveau_event_new ( pdisp - > vblank , i ,
nouveau_display_vblank_handler ,
drm , & disp - > vblank [ i ] ) ;
if ( ret ) {
nouveau_display_vblank_fini ( dev ) ;
return ret ;
}
}
ret = drm_vblank_init ( dev , dev - > mode_config . num_crtc ) ;
if ( ret ) {
nouveau_display_vblank_fini ( dev ) ;
return ret ;
}
return 0 ;
}
2009-12-11 12:24:15 +03:00
static void
nouveau_user_framebuffer_destroy ( struct drm_framebuffer * drm_fb )
{
struct nouveau_framebuffer * fb = nouveau_framebuffer ( drm_fb ) ;
2010-02-09 08:49:12 +03:00
if ( fb - > nvbo )
2013-10-02 12:15:17 +04:00
drm_gem_object_unreference_unlocked ( & fb - > nvbo - > gem ) ;
2009-12-11 12:24:15 +03:00
drm_framebuffer_cleanup ( drm_fb ) ;
kfree ( fb ) ;
}
static int
nouveau_user_framebuffer_create_handle ( struct drm_framebuffer * drm_fb ,
struct drm_file * file_priv ,
unsigned int * handle )
{
struct nouveau_framebuffer * fb = nouveau_framebuffer ( drm_fb ) ;
2013-10-02 12:15:17 +04:00
return drm_gem_handle_create ( file_priv , & fb - > nvbo - > gem , handle ) ;
2009-12-11 12:24:15 +03:00
}
static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
. destroy = nouveau_user_framebuffer_destroy ,
. create_handle = nouveau_user_framebuffer_create_handle ,
} ;
2010-03-30 09:34:13 +04:00
int
2011-02-09 04:57:45 +03:00
nouveau_framebuffer_init ( struct drm_device * dev ,
struct nouveau_framebuffer * nv_fb ,
2011-11-15 02:51:28 +04:00
struct drm_mode_fb_cmd2 * mode_cmd ,
2011-02-09 04:57:45 +03:00
struct nouveau_bo * nvbo )
2009-12-11 12:24:15 +03:00
{
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2011-02-09 04:57:45 +03:00
struct drm_framebuffer * fb = & nv_fb - > base ;
2009-12-11 12:24:15 +03:00
int ret ;
2011-02-09 04:57:45 +03:00
drm_helper_mode_fill_fb_struct ( fb , mode_cmd ) ;
nv_fb - > nvbo = nvbo ;
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type > = NV_50 ) {
2011-02-09 04:57:45 +03:00
u32 tile_flags = nouveau_bo_tile_layout ( nvbo ) ;
if ( tile_flags = = 0x7a00 | |
tile_flags = = 0xfe00 )
nv_fb - > r_dma = NvEvoFB32 ;
else
if ( tile_flags = = 0x7000 )
nv_fb - > r_dma = NvEvoFB16 ;
else
nv_fb - > r_dma = NvEvoVRAM_LP ;
switch ( fb - > depth ) {
2012-11-16 05:54:31 +04:00
case 8 : nv_fb - > r_format = 0x1e00 ; break ;
case 15 : nv_fb - > r_format = 0xe900 ; break ;
case 16 : nv_fb - > r_format = 0xe800 ; break ;
2011-02-09 04:57:45 +03:00
case 24 :
2012-11-16 05:54:31 +04:00
case 32 : nv_fb - > r_format = 0xcf00 ; break ;
case 30 : nv_fb - > r_format = 0xd100 ; break ;
2011-02-09 04:57:45 +03:00
default :
2012-07-31 10:16:21 +04:00
NV_ERROR ( drm , " unknown depth %d \n " , fb - > depth ) ;
2011-02-09 04:57:45 +03:00
return - EINVAL ;
}
2013-08-07 19:11:27 +04:00
if ( nvbo - > tile_flags & NOUVEAU_GEM_TILE_NONCONTIG ) {
NV_ERROR ( drm , " framebuffer requires contiguous bo \n " ) ;
return - EINVAL ;
}
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > chipset = = 0x50 )
2011-02-09 04:57:45 +03:00
nv_fb - > r_format | = ( tile_flags < < 8 ) ;
2011-07-06 03:59:40 +04:00
if ( ! tile_flags ) {
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type < NV_D0 )
2011-12-20 02:06:49 +04:00
nv_fb - > r_pitch = 0x00100000 | fb - > pitches [ 0 ] ;
2011-07-06 03:59:40 +04:00
else
2011-12-20 02:06:49 +04:00
nv_fb - > r_pitch = 0x01000000 | fb - > pitches [ 0 ] ;
2011-07-06 03:59:40 +04:00
} else {
2011-02-09 04:57:45 +03:00
u32 mode = nvbo - > tile_mode ;
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type > = NV_C0 )
2011-02-09 04:57:45 +03:00
mode > > = 4 ;
2011-12-20 02:06:49 +04:00
nv_fb - > r_pitch = ( ( fb - > pitches [ 0 ] / 4 ) < < 4 ) | mode ;
2011-02-09 04:57:45 +03:00
}
}
2012-12-14 02:38:38 +04:00
ret = drm_framebuffer_init ( dev , fb , & nouveau_framebuffer_funcs ) ;
if ( ret ) {
return ret ;
}
2010-03-30 09:34:13 +04:00
return 0 ;
2009-12-11 12:24:15 +03:00
}
static struct drm_framebuffer *
nouveau_user_framebuffer_create ( struct drm_device * dev ,
struct drm_file * file_priv ,
2011-11-15 02:51:28 +04:00
struct drm_mode_fb_cmd2 * mode_cmd )
2009-12-11 12:24:15 +03:00
{
2010-03-30 09:34:13 +04:00
struct nouveau_framebuffer * nouveau_fb ;
2009-12-11 12:24:15 +03:00
struct drm_gem_object * gem ;
2013-07-08 08:50:54 +04:00
int ret = - ENOMEM ;
2009-12-11 12:24:15 +03:00
2011-11-15 02:51:28 +04:00
gem = drm_gem_object_lookup ( dev , file_priv , mode_cmd - > handles [ 0 ] ) ;
2009-12-11 12:24:15 +03:00
if ( ! gem )
2010-08-08 16:36:38 +04:00
return ERR_PTR ( - ENOENT ) ;
2009-12-11 12:24:15 +03:00
2010-03-30 09:34:13 +04:00
nouveau_fb = kzalloc ( sizeof ( struct nouveau_framebuffer ) , GFP_KERNEL ) ;
if ( ! nouveau_fb )
2013-07-08 08:50:54 +04:00
goto err_unref ;
2010-03-30 09:34:13 +04:00
ret = nouveau_framebuffer_init ( dev , nouveau_fb , mode_cmd , nouveau_gem_object ( gem ) ) ;
2013-07-08 08:50:54 +04:00
if ( ret )
goto err ;
2009-12-11 12:24:15 +03:00
2010-03-30 09:34:13 +04:00
return & nouveau_fb - > base ;
2013-07-08 08:50:54 +04:00
err :
kfree ( nouveau_fb ) ;
err_unref :
drm_gem_object_unreference ( gem ) ;
return ERR_PTR ( ret ) ;
2009-12-11 12:24:15 +03:00
}
2011-10-06 06:46:40 +04:00
static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
2009-12-11 12:24:15 +03:00
. fb_create = nouveau_user_framebuffer_create ,
2010-05-07 10:42:51 +04:00
. output_poll_changed = nouveau_fbcon_output_poll_changed ,
2009-12-11 12:24:15 +03:00
} ;
2011-10-06 07:29:05 +04:00
2012-02-06 13:58:17 +04:00
struct nouveau_drm_prop_enum_list {
2011-10-17 06:23:41 +04:00
u8 gen_mask ;
2011-10-06 07:29:05 +04:00
int type ;
char * name ;
} ;
2012-02-06 13:58:17 +04:00
static struct nouveau_drm_prop_enum_list underscan [ ] = {
2011-11-11 17:49:06 +04:00
{ 6 , UNDERSCAN_AUTO , " auto " } ,
{ 6 , UNDERSCAN_OFF , " off " } ,
{ 6 , UNDERSCAN_ON , " on " } ,
2011-10-17 06:23:41 +04:00
{ }
2011-10-06 07:29:05 +04:00
} ;
2012-02-06 13:58:17 +04:00
static struct nouveau_drm_prop_enum_list dither_mode [ ] = {
2011-10-17 06:23:41 +04:00
{ 7 , DITHERING_MODE_AUTO , " auto " } ,
{ 7 , DITHERING_MODE_OFF , " off " } ,
{ 1 , DITHERING_MODE_ON , " on " } ,
{ 6 , DITHERING_MODE_STATIC2X2 , " static 2x2 " } ,
{ 6 , DITHERING_MODE_DYNAMIC2X2 , " dynamic 2x2 " } ,
{ 4 , DITHERING_MODE_TEMPORAL , " temporal " } ,
{ }
} ;
2012-02-06 13:58:17 +04:00
static struct nouveau_drm_prop_enum_list dither_depth [ ] = {
2011-10-17 06:23:41 +04:00
{ 6 , DITHERING_DEPTH_AUTO , " auto " } ,
{ 6 , DITHERING_DEPTH_6BPC , " 6 bpc " } ,
{ 6 , DITHERING_DEPTH_8BPC , " 8 bpc " } ,
{ }
} ;
# define PROP_ENUM(p,gen,n,list) do { \
2012-02-06 13:58:17 +04:00
struct nouveau_drm_prop_enum_list * l = ( list ) ; \
2011-10-17 06:23:41 +04:00
int c = 0 ; \
while ( l - > gen_mask ) { \
if ( l - > gen_mask & ( 1 < < ( gen ) ) ) \
c + + ; \
l + + ; \
} \
if ( c ) { \
p = drm_property_create ( dev , DRM_MODE_PROP_ENUM , n , c ) ; \
l = ( list ) ; \
c = 0 ; \
while ( p & & l - > gen_mask ) { \
if ( l - > gen_mask & ( 1 < < ( gen ) ) ) { \
drm_property_add_enum ( p , c , l - > type , l - > name ) ; \
c + + ; \
} \
l + + ; \
} \
} \
} while ( 0 )
2011-11-09 09:18:47 +04:00
int
nouveau_display_init ( struct drm_device * dev )
{
2012-07-31 10:16:21 +04:00
struct nouveau_display * disp = nouveau_display ( dev ) ;
2011-11-24 06:59:44 +04:00
struct drm_connector * connector ;
2011-11-09 09:18:47 +04:00
int ret ;
ret = disp - > init ( dev ) ;
2011-11-24 06:59:44 +04:00
if ( ret )
return ret ;
2012-01-31 03:16:59 +04:00
/* enable polling for external displays */
2011-11-24 06:59:44 +04:00
drm_kms_helper_poll_enable ( dev ) ;
/* enable hotplug interrupts */
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
struct nouveau_connector * conn = nouveau_connector ( connector ) ;
2013-10-03 01:02:29 +04:00
if ( conn - > hpd_func ) nouveau_event_get ( conn - > hpd_func ) ;
2011-11-09 09:18:47 +04:00
}
return ret ;
}
void
nouveau_display_fini ( struct drm_device * dev )
{
2012-07-31 10:16:21 +04:00
struct nouveau_display * disp = nouveau_display ( dev ) ;
2011-11-24 06:59:44 +04:00
struct drm_connector * connector ;
/* disable hotplug interrupts */
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
struct nouveau_connector * conn = nouveau_connector ( connector ) ;
2013-10-03 01:02:29 +04:00
if ( conn - > hpd_func ) nouveau_event_put ( conn - > hpd_func ) ;
2011-11-24 06:59:44 +04:00
}
2011-11-09 09:18:47 +04:00
drm_kms_helper_poll_disable ( dev ) ;
disp - > fini ( dev ) ;
}
2011-10-06 06:46:40 +04:00
int
nouveau_display_create ( struct drm_device * dev )
{
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_display * disp ;
2011-10-17 06:23:41 +04:00
int ret , gen ;
2011-10-06 06:46:40 +04:00
2012-07-31 10:16:21 +04:00
disp = drm - > display = kzalloc ( sizeof ( * disp ) , GFP_KERNEL ) ;
if ( ! disp )
return - ENOMEM ;
2011-10-06 06:46:40 +04:00
drm_mode_config_init ( dev ) ;
drm_mode_create_scaling_mode_property ( dev ) ;
2011-10-14 17:55:47 +04:00
drm_mode_create_dvi_i_properties ( dev ) ;
2011-10-17 06:23:41 +04:00
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type < NV_50 )
2011-10-17 06:23:41 +04:00
gen = 0 ;
else
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type < NV_D0 )
2011-10-17 06:23:41 +04:00
gen = 1 ;
else
gen = 2 ;
PROP_ENUM ( disp - > dithering_mode , gen , " dithering mode " , dither_mode ) ;
PROP_ENUM ( disp - > dithering_depth , gen , " dithering depth " , dither_depth ) ;
PROP_ENUM ( disp - > underscan_property , gen , " underscan " , underscan ) ;
2011-10-06 07:29:05 +04:00
disp - > underscan_hborder_property =
2012-02-06 13:58:18 +04:00
drm_property_create_range ( dev , 0 , " underscan hborder " , 0 , 128 ) ;
2011-10-06 07:29:05 +04:00
disp - > underscan_vborder_property =
2012-02-06 13:58:18 +04:00
drm_property_create_range ( dev , 0 , " underscan vborder " , 0 , 128 ) ;
2011-10-06 07:29:05 +04:00
2012-11-21 07:03:42 +04:00
if ( gen > = 1 ) {
2013-01-27 19:43:00 +04:00
/* -90..+90 */
2012-01-22 02:13:26 +04:00
disp - > vibrant_hue_property =
2013-01-27 19:43:00 +04:00
drm_property_create_range ( dev , 0 , " vibrant hue " , 0 , 180 ) ;
2012-01-22 02:13:26 +04:00
2013-01-27 19:43:00 +04:00
/* -100..+100 */
2012-01-22 02:13:26 +04:00
disp - > color_vibrance_property =
2013-01-27 19:43:00 +04:00
drm_property_create_range ( dev , 0 , " color vibrance " , 0 , 200 ) ;
2012-01-22 02:13:26 +04:00
}
2012-05-17 15:27:23 +04:00
dev - > mode_config . funcs = & nouveau_mode_config_funcs ;
2011-10-06 06:46:40 +04:00
dev - > mode_config . fb_base = pci_resource_start ( dev - > pdev , 1 ) ;
dev - > mode_config . min_width = 0 ;
dev - > mode_config . min_height = 0 ;
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type < NV_10 ) {
2011-10-06 06:46:40 +04:00
dev - > mode_config . max_width = 2048 ;
dev - > mode_config . max_height = 2048 ;
} else
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type < NV_50 ) {
2011-10-06 06:46:40 +04:00
dev - > mode_config . max_width = 4096 ;
dev - > mode_config . max_height = 4096 ;
} else {
dev - > mode_config . max_width = 8192 ;
dev - > mode_config . max_height = 8192 ;
}
2012-02-20 18:39:11 +04:00
dev - > mode_config . preferred_depth = 24 ;
dev - > mode_config . prefer_shadow = 1 ;
2013-11-11 07:59:40 +04:00
if ( nv_device ( drm - > device ) - > chipset < 0x11 )
dev - > mode_config . async_page_flip = false ;
else
dev - > mode_config . async_page_flip = true ;
2011-11-09 09:18:47 +04:00
drm_kms_helper_poll_init ( dev ) ;
drm_kms_helper_poll_disable ( dev ) ;
2013-09-10 07:20:34 +04:00
if ( drm - > vbios . dcb . entries ) {
if ( nv_device ( drm - > device ) - > card_type < NV_50 )
ret = nv04_display_create ( dev ) ;
else
ret = nv50_display_create ( dev ) ;
} else {
ret = 0 ;
}
2012-10-31 06:11:15 +04:00
2013-09-10 07:20:34 +04:00
if ( ret )
goto disp_create_err ;
2012-10-31 06:11:15 +04:00
2013-09-10 07:20:34 +04:00
if ( dev - > mode_config . num_crtc ) {
2013-10-03 01:02:29 +04:00
ret = nouveau_display_vblank_init ( dev ) ;
2013-09-10 07:20:34 +04:00
if ( ret )
goto vblank_err ;
2011-11-09 09:18:47 +04:00
}
2013-09-10 07:20:34 +04:00
nouveau_backlight_init ( dev ) ;
2012-04-15 16:36:08 +04:00
return 0 ;
vblank_err :
2012-07-31 10:16:21 +04:00
disp - > dtor ( dev ) ;
2012-04-15 16:36:08 +04:00
disp_create_err :
drm_kms_helper_poll_fini ( dev ) ;
drm_mode_config_cleanup ( dev ) ;
2011-11-09 05:36:33 +04:00
return ret ;
2011-10-06 06:46:40 +04:00
}
void
nouveau_display_destroy ( struct drm_device * dev )
{
2012-07-31 10:16:21 +04:00
struct nouveau_display * disp = nouveau_display ( dev ) ;
2011-10-06 06:46:40 +04:00
2012-07-31 10:16:21 +04:00
nouveau_backlight_exit ( dev ) ;
2013-10-03 01:02:29 +04:00
nouveau_display_vblank_fini ( dev ) ;
2011-11-09 09:18:47 +04:00
2012-10-30 04:59:12 +04:00
drm_kms_helper_poll_fini ( dev ) ;
drm_mode_config_cleanup ( dev ) ;
2012-10-31 06:11:15 +04:00
if ( disp - > dtor )
disp - > dtor ( dev ) ;
2011-11-09 09:18:47 +04:00
2012-07-31 10:16:21 +04:00
nouveau_drm ( dev ) - > display = NULL ;
kfree ( disp ) ;
}
int
nouveau_display_suspend ( struct drm_device * dev )
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct drm_crtc * crtc ;
nouveau_display_fini ( dev ) ;
2013-11-08 08:38:40 +04:00
NV_INFO ( drm , " unpinning framebuffer(s)... \n " ) ;
2012-07-31 10:16:21 +04:00
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_framebuffer * nouveau_fb ;
nouveau_fb = nouveau_framebuffer ( crtc - > fb ) ;
if ( ! nouveau_fb | | ! nouveau_fb - > nvbo )
continue ;
nouveau_bo_unpin ( nouveau_fb - > nvbo ) ;
}
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_crtc * nv_crtc = nouveau_crtc ( crtc ) ;
nouveau_bo_unmap ( nv_crtc - > cursor . nvbo ) ;
nouveau_bo_unpin ( nv_crtc - > cursor . nvbo ) ;
}
return 0 ;
}
void
2012-09-10 08:20:51 +04:00
nouveau_display_repin ( struct drm_device * dev )
2012-07-31 10:16:21 +04:00
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct drm_crtc * crtc ;
int ret ;
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_framebuffer * nouveau_fb ;
nouveau_fb = nouveau_framebuffer ( crtc - > fb ) ;
if ( ! nouveau_fb | | ! nouveau_fb - > nvbo )
continue ;
nouveau_bo_pin ( nouveau_fb - > nvbo , TTM_PL_FLAG_VRAM ) ;
}
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_crtc * nv_crtc = nouveau_crtc ( crtc ) ;
ret = nouveau_bo_pin ( nv_crtc - > cursor . nvbo , TTM_PL_FLAG_VRAM ) ;
if ( ! ret )
ret = nouveau_bo_map ( nv_crtc - > cursor . nvbo ) ;
if ( ret )
NV_ERROR ( drm , " Could not pin/map cursor. \n " ) ;
}
2012-09-10 08:20:51 +04:00
}
2012-07-31 10:16:21 +04:00
2012-09-10 08:20:51 +04:00
void
nouveau_display_resume ( struct drm_device * dev )
{
struct drm_crtc * crtc ;
2012-07-31 10:16:21 +04:00
nouveau_display_init ( dev ) ;
/* Force CLUT to get re-loaded during modeset */
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_crtc * nv_crtc = nouveau_crtc ( crtc ) ;
nv_crtc - > lut . depth = 0 ;
}
drm_helper_resume_force_mode ( dev ) ;
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_crtc * nv_crtc = nouveau_crtc ( crtc ) ;
u32 offset = nv_crtc - > cursor . nvbo - > bo . offset ;
nv_crtc - > cursor . set_offset ( nv_crtc , offset ) ;
nv_crtc - > cursor . set_pos ( nv_crtc , nv_crtc - > cursor_saved_x ,
nv_crtc - > cursor_saved_y ) ;
}
2011-10-06 06:46:40 +04:00
}
2010-10-21 01:35:40 +04:00
static int
nouveau_page_flip_emit ( struct nouveau_channel * chan ,
struct nouveau_bo * old_bo ,
struct nouveau_bo * new_bo ,
struct nouveau_page_flip_state * s ,
struct nouveau_fence * * pfence )
{
2012-07-22 05:55:54 +04:00
struct nouveau_fence_chan * fctx = chan - > fence ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = chan - > drm ;
struct drm_device * dev = drm - > dev ;
2010-10-21 01:35:40 +04:00
unsigned long flags ;
int ret ;
/* Queue it to the pending list */
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
2012-07-22 05:55:54 +04:00
list_add_tail ( & s - > head , & fctx - > flip ) ;
2010-10-21 01:35:40 +04:00
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
/* Synchronize with the old framebuffer */
ret = nouveau_fence_sync ( old_bo - > bo . sync_obj , chan ) ;
if ( ret )
goto fail ;
/* Emit the pageflip */
2013-11-13 04:49:46 +04:00
ret = RING_SPACE ( chan , 2 ) ;
2010-10-21 01:35:40 +04:00
if ( ret )
goto fail ;
2013-11-13 04:49:46 +04:00
if ( nv_device ( drm - > device ) - > card_type < NV_C0 )
2012-04-01 15:09:13 +04:00
BEGIN_NV04 ( chan , NvSubSw , NV_SW_PAGE_FLIP , 1 ) ;
2013-11-13 04:49:46 +04:00
else
BEGIN_NVC0 ( chan , FermiSw , NV_SW_PAGE_FLIP , 1 ) ;
OUT_RING ( chan , 0x00000000 ) ;
2011-02-08 08:16:23 +03:00
FIRE_RING ( chan ) ;
2010-10-21 01:35:40 +04:00
2013-02-14 07:43:21 +04:00
ret = nouveau_fence_new ( chan , false , pfence ) ;
2010-10-21 01:35:40 +04:00
if ( ret )
goto fail ;
return 0 ;
fail :
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
list_del ( & s - > head ) ;
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
return ret ;
}
int
nouveau_crtc_page_flip ( struct drm_crtc * crtc , struct drm_framebuffer * fb ,
2013-11-11 07:59:40 +04:00
struct drm_pending_vblank_event * event , u32 flags )
2010-10-21 01:35:40 +04:00
{
2013-11-11 07:59:40 +04:00
const int swap_interval = ( flags & DRM_MODE_PAGE_FLIP_ASYNC ) ? 0 : 1 ;
2010-10-21 01:35:40 +04:00
struct drm_device * dev = crtc - > dev ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2010-10-21 01:35:40 +04:00
struct nouveau_bo * old_bo = nouveau_framebuffer ( crtc - > fb ) - > nvbo ;
struct nouveau_bo * new_bo = nouveau_framebuffer ( fb ) - > nvbo ;
struct nouveau_page_flip_state * s ;
2013-11-13 04:17:17 +04:00
struct nouveau_channel * chan = drm - > channel ;
2010-10-21 01:35:40 +04:00
struct nouveau_fence * fence ;
int ret ;
2012-07-31 10:16:21 +04:00
if ( ! drm - > channel )
2010-10-21 01:35:40 +04:00
return - ENODEV ;
s = kzalloc ( sizeof ( * s ) , GFP_KERNEL ) ;
if ( ! s )
return - ENOMEM ;
2014-01-14 19:48:58 +04:00
if ( new_bo ! = old_bo ) {
ret = nouveau_bo_pin ( new_bo , TTM_PL_FLAG_VRAM ) ;
if ( ret )
goto fail_free ;
}
mutex_lock ( & chan - > cli - > mutex ) ;
2013-11-13 04:17:17 +04:00
/* synchronise rendering channel with the kernel's channel */
spin_lock ( & new_bo - > bo . bdev - > fence_lock ) ;
fence = nouveau_fence_ref ( new_bo - > bo . sync_obj ) ;
spin_unlock ( & new_bo - > bo . bdev - > fence_lock ) ;
ret = nouveau_fence_sync ( fence , chan ) ;
2013-11-21 08:22:39 +04:00
nouveau_fence_unref ( & fence ) ;
2013-11-13 04:17:17 +04:00
if ( ret )
2013-12-30 02:08:54 +04:00
goto fail_free ;
2013-06-27 15:48:18 +04:00
2013-11-13 04:00:32 +04:00
ret = ttm_bo_reserve ( & old_bo - > bo , true , false , false , NULL ) ;
2013-07-08 08:15:51 +04:00
if ( ret )
goto fail_unpin ;
2013-06-27 15:48:18 +04:00
/* Initialize a page flip struct */
* s = ( struct nouveau_page_flip_state )
{ { } , event , nouveau_crtc ( crtc ) - > index ,
fb - > bits_per_pixel , fb - > pitches [ 0 ] , crtc - > x , crtc - > y ,
new_bo - > bo . offset } ;
2010-10-21 01:35:40 +04:00
/* Emit a page flip */
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type > = NV_50 ) {
2013-11-11 07:59:40 +04:00
ret = nv50_display_flip_next ( crtc , fb , chan , swap_interval ) ;
2013-07-08 08:15:51 +04:00
if ( ret )
2011-02-07 07:27:04 +03:00
goto fail_unreserve ;
2013-08-21 05:30:36 +04:00
} else {
struct nv04_display * dispnv04 = nv04_display ( dev ) ;
2013-11-11 07:59:40 +04:00
int head = nouveau_crtc ( crtc ) - > index ;
if ( swap_interval ) {
ret = RING_SPACE ( chan , 8 ) ;
if ( ret )
goto fail_unreserve ;
BEGIN_NV04 ( chan , NvSubImageBlit , 0x012c , 1 ) ;
OUT_RING ( chan , 0 ) ;
BEGIN_NV04 ( chan , NvSubImageBlit , 0x0134 , 1 ) ;
OUT_RING ( chan , head ) ;
BEGIN_NV04 ( chan , NvSubImageBlit , 0x0100 , 1 ) ;
OUT_RING ( chan , 0 ) ;
BEGIN_NV04 ( chan , NvSubImageBlit , 0x0130 , 1 ) ;
OUT_RING ( chan , 0 ) ;
}
nouveau_bo_ref ( new_bo , & dispnv04 - > image [ head ] ) ;
2011-02-07 07:27:04 +03:00
}
2010-10-21 01:35:40 +04:00
ret = nouveau_page_flip_emit ( chan , old_bo , new_bo , s , & fence ) ;
2012-07-31 10:16:21 +04:00
mutex_unlock ( & chan - > cli - > mutex ) ;
2010-10-21 01:35:40 +04:00
if ( ret )
goto fail_unreserve ;
/* Update the crtc struct and cleanup */
crtc - > fb = fb ;
2013-11-13 04:00:32 +04:00
nouveau_bo_fence ( old_bo , fence ) ;
ttm_bo_unreserve ( & old_bo - > bo ) ;
2013-07-08 08:15:51 +04:00
if ( old_bo ! = new_bo )
2013-06-27 15:48:18 +04:00
nouveau_bo_unpin ( old_bo ) ;
2010-10-21 01:35:40 +04:00
nouveau_fence_unref ( & fence ) ;
return 0 ;
fail_unreserve :
2013-11-13 04:00:32 +04:00
ttm_bo_unreserve ( & old_bo - > bo ) ;
2013-07-08 08:15:51 +04:00
fail_unpin :
mutex_unlock ( & chan - > cli - > mutex ) ;
if ( old_bo ! = new_bo )
2013-06-27 15:48:18 +04:00
nouveau_bo_unpin ( new_bo ) ;
2010-10-21 01:35:40 +04:00
fail_free :
kfree ( s ) ;
return ret ;
}
int
nouveau_finish_page_flip ( struct nouveau_channel * chan ,
struct nouveau_page_flip_state * ps )
{
2012-07-22 05:55:54 +04:00
struct nouveau_fence_chan * fctx = chan - > fence ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = chan - > drm ;
struct drm_device * dev = drm - > dev ;
2010-10-21 01:35:40 +04:00
struct nouveau_page_flip_state * s ;
unsigned long flags ;
spin_lock_irqsave ( & dev - > event_lock , flags ) ;
2012-07-22 05:55:54 +04:00
if ( list_empty ( & fctx - > flip ) ) {
2012-07-31 10:16:21 +04:00
NV_ERROR ( drm , " unexpected pageflip \n " ) ;
2010-10-21 01:35:40 +04:00
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
return - EINVAL ;
}
2012-07-22 05:55:54 +04:00
s = list_first_entry ( & fctx - > flip , struct nouveau_page_flip_state , head ) ;
2012-10-08 23:50:41 +04:00
if ( s - > event )
2013-11-20 09:14:31 +04:00
drm_send_vblank_event ( dev , s - > crtc , s - > event ) ;
2010-10-21 01:35:40 +04:00
list_del ( & s - > head ) ;
2011-02-07 07:27:04 +03:00
if ( ps )
* ps = * s ;
2010-10-21 01:35:40 +04:00
kfree ( s ) ;
spin_unlock_irqrestore ( & dev - > event_lock , flags ) ;
return 0 ;
}
2011-09-30 02:55:50 +04:00
2012-07-22 05:55:54 +04:00
int
nouveau_flip_complete ( void * data )
{
struct nouveau_channel * chan = data ;
2012-07-31 10:16:21 +04:00
struct nouveau_drm * drm = chan - > drm ;
2012-07-22 05:55:54 +04:00
struct nouveau_page_flip_state state ;
if ( ! nouveau_finish_page_flip ( chan , & state ) ) {
2012-07-31 10:16:21 +04:00
if ( nv_device ( drm - > device ) - > card_type < NV_50 ) {
nv_set_crtc_base ( drm - > dev , state . crtc , state . offset +
2012-07-22 05:55:54 +04:00
state . y * state . pitch +
state . x * state . bpp / 8 ) ;
}
}
return 0 ;
}
2011-09-30 02:55:50 +04:00
int
nouveau_display_dumb_create ( struct drm_file * file_priv , struct drm_device * dev ,
struct drm_mode_create_dumb * args )
{
struct nouveau_bo * bo ;
int ret ;
args - > pitch = roundup ( args - > width * ( args - > bpp / 8 ) , 256 ) ;
args - > size = args - > pitch * args - > height ;
args - > size = roundup ( args - > size , PAGE_SIZE ) ;
2012-09-14 07:28:23 +04:00
ret = nouveau_gem_new ( dev , args - > size , 0 , NOUVEAU_GEM_DOMAIN_VRAM , 0 , 0 , & bo ) ;
2011-09-30 02:55:50 +04:00
if ( ret )
return ret ;
2013-10-02 12:15:17 +04:00
ret = drm_gem_handle_create ( file_priv , & bo - > gem , & args - > handle ) ;
drm_gem_object_unreference_unlocked ( & bo - > gem ) ;
2011-09-30 02:55:50 +04:00
return ret ;
}
int
nouveau_display_dumb_map_offset ( struct drm_file * file_priv ,
struct drm_device * dev ,
uint32_t handle , uint64_t * poffset )
{
struct drm_gem_object * gem ;
gem = drm_gem_object_lookup ( dev , file_priv , handle ) ;
if ( gem ) {
2013-10-02 12:15:17 +04:00
struct nouveau_bo * bo = nouveau_gem_object ( gem ) ;
2013-07-24 23:08:53 +04:00
* poffset = drm_vma_node_offset_addr ( & bo - > bo . vma_node ) ;
2011-09-30 02:55:50 +04:00
drm_gem_object_unreference_unlocked ( gem ) ;
return 0 ;
}
return - ENOENT ;
}