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>
2013-06-27 15:48:18 +04:00
# include <drm/ttm/ttm_execbuf_util.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-10 06:20:17 +04:00
# include <subdev/bios/gpio.h>
2012-07-31 10:16:21 +04:00
# include <subdev/gpio.h>
# include <engine/disp.h>
2012-07-10 06:20:17 +04:00
2013-01-31 03:23:34 +04:00
# include <core/class.h>
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_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_display * disp = nouveau_display ( dev ) ;
struct nouveau_gpio * gpio = nouveau_gpio ( drm - > device ) ;
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-02-03 06:56:16 +04:00
if ( gpio & & conn - > hpd . func ! = DCB_GPIO_UNUSED ) {
nouveau_event_get ( gpio - > events , conn - > hpd . line ,
& 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_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_display * disp = nouveau_display ( dev ) ;
struct nouveau_gpio * gpio = nouveau_gpio ( drm - > device ) ;
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-02-03 06:56:16 +04:00
if ( gpio & & conn - > hpd . func ! = DCB_GPIO_UNUSED ) {
nouveau_event_put ( gpio - > events , conn - > hpd . line ,
& 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 ;
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 ) {
ret = drm_vblank_init ( dev , dev - > mode_config . num_crtc ) ;
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 ) ;
2011-11-09 09:18:47 +04:00
drm_vblank_cleanup ( dev ) ;
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 ) ;
2012-09-10 08:20:51 +04:00
NV_SUSPEND ( 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 */
2012-03-21 07:53:49 +04:00
ret = RING_SPACE ( chan , 3 ) ;
2010-10-21 01:35:40 +04:00
if ( ret )
goto fail ;
2012-07-31 10:16:21 +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 ) ;
2012-03-21 07:53:49 +04:00
OUT_RING ( chan , 0x00000000 ) ;
OUT_RING ( chan , 0x00000000 ) ;
} else {
2012-04-01 15:09:13 +04:00
BEGIN_NVC0 ( chan , 0 , NV10_SUBCHAN_REF_CNT , 1 ) ;
2012-04-30 07:55:29 +04:00
OUT_RING ( chan , 0 ) ;
2012-04-01 15:09:13 +04:00
BEGIN_IMC0 ( chan , 0 , NVSW_SUBCHAN_PAGE_FLIP , 0x0000 ) ;
2012-03-21 07:53:49 +04:00
}
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-07-23 05:49:58 +04:00
struct drm_pending_vblank_event * event ,
uint32_t page_flip_flags )
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 ;
2012-04-30 07:30:00 +04:00
struct nouveau_channel * chan = NULL ;
2010-10-21 01:35:40 +04:00
struct nouveau_fence * fence ;
2013-07-08 08:15:51 +04:00
struct ttm_validate_buffer resv [ 2 ] = {
{ . bo = & old_bo - > bo } ,
{ . bo = & new_bo - > bo } ,
} ;
2013-06-27 15:48:18 +04:00
struct ww_acquire_ctx ticket ;
2013-07-08 08:15:51 +04:00
LIST_HEAD ( res ) ;
2010-10-21 01:35:40 +04:00
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 ;
/* Choose the channel the flip will be handled in */
2013-06-27 15:48:18 +04:00
spin_lock ( & old_bo - > bo . bdev - > fence_lock ) ;
2012-04-30 07:30:00 +04:00
fence = new_bo - > bo . sync_obj ;
if ( fence )
2012-07-20 02:17:34 +04:00
chan = fence - > channel ;
2010-10-21 01:35:40 +04:00
if ( ! chan )
2012-07-31 10:16:21 +04:00
chan = drm - > channel ;
2013-06-27 15:48:18 +04:00
spin_unlock ( & old_bo - > bo . bdev - > fence_lock ) ;
if ( new_bo ! = old_bo ) {
ret = nouveau_bo_pin ( new_bo , TTM_PL_FLAG_VRAM ) ;
2013-07-08 08:15:51 +04:00
if ( ret )
goto fail_free ;
2013-06-27 15:48:18 +04:00
2013-07-08 08:15:51 +04:00
list_add ( & resv [ 1 ] . head , & res ) ;
2013-06-27 15:48:18 +04:00
}
2013-07-08 08:15:51 +04:00
list_add ( & resv [ 0 ] . head , & res ) ;
mutex_lock ( & chan - > cli - > mutex ) ;
ret = ttm_eu_reserve_buffers ( & ticket , & res ) ;
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 ) {
2012-11-21 08:40:21 +04:00
ret = nv50_display_flip_next ( crtc , fb , chan , 0 ) ;
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 ) ;
nouveau_bo_ref ( new_bo , & dispnv04 - > image [ nouveau_crtc ( crtc ) - > index ] ) ;
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-07-08 08:15:51 +04:00
ttm_eu_fence_buffer_objects ( & ticket , & res , fence ) ;
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-07-08 08:15:51 +04:00
ttm_eu_backoff_reservation ( & ticket , & res ) ;
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 )
drm_send_vblank_event ( dev , - 1 , 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 ;
}